FFMPEG-RTMP之抽取FLV

ffmpeg-rtmp模块中维护了flv_data、flv_size、flv_off,共同对flv数据进行处理,

flv_data: 数据首地址

flv_size: 数据缓冲区数据量

flv_off: 读取的数据

 

它是一个动态缓冲区,可以看做某个网络flv文件的滑动窗口,它在rtmp open时写下文件头,并由rtmp每一次chunk的到来而扩展,而由每一次对flv_data的读取而向后滑动。

 

FLV头生成

rtmp中FLV头的建立发生在rtmp_open函数中

static int rtmp_open(URLContext *s, const char *uri, int flags)
{
    ....
       if (rt->is_input) {
        // generate FLV header for demuxer
        rt->flv_size = 13;
        if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
            goto fail;
        rt->flv_off  = 0;
        memcpy(rt->flv_data, "FLV\1\0\0\0\0\011\0\0\0\0", rt->flv_size);

        // Read packets until we reach the first A/V packet or read metadata.
        // If there was a metadata package in front of the A/V packets, we can
        // build the FLV header from this. If we do not receive any metadata,
        // the FLV decoder will allocate the needed streams when their first
        // audio or video packet arrives.
        while (!rt->has_audio && !rt->has_video && !rt->received_metadata) {
            if ((ret = get_packet(s, 0)) < 0)
               goto fail;
        }

        // Either after we have read the metadata or (if there is none) the
        // first packet of an A/V stream, we have a better knowledge about the
        // streams, so set the FLV header accordingly.
        if (rt->has_audio) {
            rt->flv_data[4] |= FLV_HEADER_FLAG_HASAUDIO;
        }
        if (rt->has_video) {
            rt->flv_data[4] |= FLV_HEADER_FLAG_HASVIDEO;
        }

        // If we received the first packet of an A/V stream and no metadata but
        // the server returned a valid duration, create a fake metadata packet
        // to inform the FLV decoder about the duration.
        if (!rt->received_metadata && rt->duration > 0) {
            if ((ret = inject_fake_duration_metadata(rt)) < 0)
                goto fail;
        }
    } else {
        rt->flv_size = 0;
        rt->flv_data = NULL;
        rt->flv_off  = 0;
        rt->skip_bytes = 13;
    }
    }

按照FLV的格式,flv写头的时候固定了13个字节,包含9个字节的头以及FLV FILE Body的PreviousTagSize0。

 The FLV header

 

Type

Comment

Signature

UI8

Signature byte always 'F' (0x46)

Signature

UI8

Signature byte always 'L' (0x4C)

Signature

UI8

Signature byte always 'V' (0x56)

Version

UI8

File version (for example, 0x01 for FLV version 1)

TypeFlagsReserved

UB [5]

Shall be 0

TypeFlagsAudio

UB [1]

1 = Audio tags are present

TypeFlagsReserved

UB [1]

Shall be 0

TypeFlagsVideo

UB [1]

1 = Video tags are present

DataOffset

UI32

The length of this header in bytes

The FLV File Body

Field

Type

Comment

PreviousTagSize0

UI32

Always 0

Tag1

FLVTAG

First tag

PreviousTagSize1

UI32

Size of previous tag, including its header, in bytes. For FLV version1,

this value is 11 plus the DataSize of the previous tag.

Tag2

FLVTAG

Second tag

...

...

...

PreviousTagSizeN-1

UI32

Size of second-to-last tag, including its header, in bytes.

TagN

FLVTAG

Last tag

PreviousTagSizeN

UI32

Size of last tag, including its header, in bytes

 

扩展FLV数据

扩展FLV发生在append_flv_data函数中,可以看到,append_flv_data会刷新flv_data缓存区,

static int append_flv_data(RTMPContext *rt, RTMPPacket *pkt, int skip)
{
    int old_flv_size, ret;
    PutByteContext pbc;
    const uint8_t *data = pkt->data + skip;
    const int size      = pkt->size - skip;
    uint32_t ts         = pkt->timestamp;

    if (pkt->type == RTMP_PT_AUDIO) {
        rt->has_audio = 1;
    } else if (pkt->type == RTMP_PT_VIDEO) {
        rt->has_video = 1;
    }

    old_flv_size = update_offset(rt, size + 15);

    if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0) {
        rt->flv_size = rt->flv_off = 0;
        return ret;
    }
    if(rt->flv_size >= 3)
    //printf("%c%c%c\n",rt->flv_data[0],rt->flv_data[1],rt->flv_data[2]);
    bytestream2_init_writer(&pbc, rt->flv_data, rt->flv_size);
    bytestream2_skip_p(&pbc, old_flv_size);
    bytestream2_put_byte(&pbc, pkt->type);
    bytestream2_put_be24(&pbc, size);
    bytestream2_put_be24(&pbc, ts);
    bytestream2_put_byte(&pbc, ts >> 24);
    bytestream2_put_be24(&pbc, 0);
    bytestream2_put_buffer(&pbc, data, size);
    bytestream2_put_be32(&pbc, size + RTMP_HEADER);

    return 0;
}

观察FLV-tag结构,发现ffmpeg在装载flv数据的时候,将tag 结构中StreamID以前的成员都生成了一遍,而把rtmp来的负载部分装到后面,说明从rtmp负载来的数据并不是一个完整的tag结构,还需要手动打上timestamp、tagType等信息。

Field

Type

Comment

Reserved

UB [2]

Reserved for FMS, should be 0

Filter

UB [1]

Indicates if packets are filtered.

0 = No pre-processing required.

1 = Pre-processing (such as decryption) of the packet is

required before it can be rendered.

Shall be 0 in unencrypted files, and 1 for encrypted tags.

See Annex F. FLV Encryption for the use of filters.

TagType

UB [5]

Type of contents in this tag. The following types are

defined:

8 = audio

9 = video

18 = script data

DataSize

UI24

Length of the message. Number of bytes after StreamID to

end of tag (Equal to length of the tag – 11)

Timestamp

UI24

Time in milliseconds at which the data in this tag applies.

This value is relative to the first tag in the FLV file, which

always has a timestamp of 0.

TimestampExtended

UI8

Extension of the Timestamp field to form a SI32 value. This

field represents the upper 8 bits, while the previous

Timestamp field represents the lower 24 bits of the time in

milliseconds.

StreamID

UI24

Always 0.

AudioTagHeader

IF TagType == 8

AudioTagHeader

 

VideoTagHeader

IF TagType == 9

VideoTagHeader

 

EncryptionHeader

IF Filter == 1

EncryptionTagHeader

 

FilterParams

IF Filter == 1

FilterParams

 

Data

IF TagType == 8

AUDIODATA

IF TagType == 9

VIDEODATA

IF TagType == 18

SCRIPTDATA

Data specific for each media type.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值