SRS 代码分析【FLV文件解析】

来源:互联网 发布:js div展开收缩 编辑:程序博客网 时间:2024/05/16 07:52

FLV文件的结构

flv


SRS对FLV文件的解析实现

1.FLV头部解析

header部分记录了flv的类型、版本等信息,是flv的开头,一般都差不多,占9bytes。具体格式如下:文件类型3 bytes“FLV”版本1 byte一般为0x01流信息1 byte倒数第一位是1表示有视频,倒数第三位是1表示有音频,倒数第二、四位必须为0header长度4 bytes整个header的长度,一般为9;大于9表示下面还有扩展信息

int SrsFlvDecoder::read_header(char header[9]){    int ret = ERROR_SUCCESS;        srs_assert(header);        // TODO: FIXME: Should use readfully.    if ((ret = reader->read(header, 9, NULL)) != ERROR_SUCCESS) {        return ret;    }        char* h = header;    if (h[0] != 'F' || h[1] != 'L' || h[2] != 'V') {        ret = ERROR_KERNEL_FLV_HEADER;        srs_warn("flv header must start with FLV. ret=%d", ret);        return ret;    }        return ret;}

2.FLV tag_header解析


   Tag类型1 bytes8:音频
9:视频
18:脚本
其他:保留数据区长度3 bytes在数据区的长度时间戳3 bytes整数,单位是毫秒。对于脚本型的tag总是0时间戳扩展1 bytes将时间戳扩展为4bytes,代表高8位。很少用到StreamsID3 bytes总是0tag_header一共11个字节,tag_header读取结束后会获取tag的类型,和tag data的size

int SrsFlvDecoder::read_tag_header(char* ptype, int32_t* pdata_size, uint32_t* ptime){    int ret = ERROR_SUCCESS;        srs_assert(ptype);    srs_assert(pdata_size);    srs_assert(ptime);        char th[11]; // tag header        // read tag header    // TODO: FIXME: Should use readfully.    if ((ret = reader->read(th, 11, NULL)) != ERROR_SUCCESS) {        if (ret != ERROR_SYSTEM_FILE_EOF) {            srs_error("read flv tag header failed. ret=%d", ret);        }        return ret;    }        // Reserved UB [2]    // Filter UB [1]    // TagType UB [5]    *ptype = (th[0] & 0x1F);        // DataSize UI24    char* pp = (char*)pdata_size;    pp[3] = 0;    pp[2] = th[1];    pp[1] = th[2];    pp[0] = th[3];        // Timestamp UI24    pp = (char*)ptime;    pp[2] = th[4];    pp[1] = th[5];    pp[0] = th[6];        // TimestampExtended UI8    pp[3] = th[7];        return ret;}

3.解析Tag data 

Tag data的大小在read_tag_header中已经读取出来

int SrsFlvDecoder::read_tag_data(char* data, int32_t size){    int ret = ERROR_SUCCESS;        srs_assert(data);        // TODO: FIXME: Should use readfully.    if ((ret = reader->read(data, size, NULL)) != ERROR_SUCCESS) {        if (ret != ERROR_SYSTEM_FILE_EOF) {            srs_error("read flv tag header failed. ret=%d", ret);        }        return ret;    }        return ret;    }

4.读取Previous tag size

在Tag与Tag之间有4个字节的Previous tag size 记录前一个tag的大小

int SrsFlvDecoder::read_previous_tag_size(char previous_tag_size[4]){    int ret = ERROR_SUCCESS;        srs_assert(previous_tag_size);        // ignore 4bytes tag size.    // TODO: FIXME: Should use readfully.    if ((ret = reader->read(previous_tag_size, 4, NULL)) != ERROR_SUCCESS) {        if (ret != ERROR_SYSTEM_FILE_EOF) {            srs_error("read flv previous tag size failed. ret=%d", ret);        }        return ret;    }        return ret;}

SRS写入数据到FLV文件的实现

1.写入FLV header,写入头部后会接着写入4个字节的Previous tag size 值为0

int SrsFlvTransmuxer::write_header(){    int ret = ERROR_SUCCESS;        // 9bytes header and 4bytes first previous-tag-size    char flv_header[] = {        'F', 'L', 'V', // Signatures "FLV"        (char)0x01, // File version (for example, 0x01 for FLV version 1)        (char)0x05, // 4, audio; 1, video; 5 audio+video.        (char)0x00, (char)0x00, (char)0x00, (char)0x09 // DataOffset UI32 The length of this header in bytes    };        // flv specification should set the audio and video flag,    // actually in practise, application generally ignore this flag,    // so we generally set the audio/video to 0.        // write 9bytes header.    if ((ret = write_header(flv_header)) != ERROR_SUCCESS) {        return ret;    }        return ret;}

int SrsFlvTransmuxer::write_header(char flv_header[9]){    int ret = ERROR_SUCCESS;        // write data.    if ((ret = writer->write(flv_header, 9, NULL)) != ERROR_SUCCESS) {        srs_error("write flv header failed. ret=%d", ret);        return ret;    }        // previous tag size.    char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)0x00 };    if ((ret = writer->write(pts, 4, NULL)) != ERROR_SUCCESS) {        return ret;    }        return ret;}

2.写入MetaData tag到FLV中

脚本Tag一般只有一个,是flv的第一个Tag,用于存放flv的信息,比如duration、audiodatarate、creator、width等。

首先介绍下脚本的数据类型。所有数据都是以数据类型+(数据长度)+数据的格式出现的,数据类型占1byte,数据长度看数据类型是否存在,后面才是数据。
其中数据类型的种类有:

  • 0 = Number type
  • 1 = Boolean type
  • 2 = String type
  • 3 = Object type
  • 4 = MovieClip type
  • 5 = Null type
  • 6 = Undefined type
  • 7 = Reference type
  • 8 = ECMA array type
  • 10 = Strict array type
  • 11 = Date type
  • 12 = Long string type

如果类型为String,后面的2bytes为字符串的长度(Long String是4bytes),再后面才是字符串数据;如果是Number类型,后面的8bytes为Double类型的数据;Boolean类型,后面1byte为Bool类型。

知道了这些后再来看看flv中的脚本,一般开头是0x02,表示String类型,后面的2bytes为字符串长度,一般是0x000a(“onMetaData”的长度),再后面就是字符串“onMetaData”。


首先调用write_metadata_to_cache将MetaData的Tag Header写入cache中,然后调用write_tag将cache中tag header 与MetaData一同写入

int SrsFlvTransmuxer::write_metadata(char type, char* data, int size){    int ret = ERROR_SUCCESS;        srs_assert(data);        if ((ret = write_metadata_to_cache(type, data, size, tag_header)) != ERROR_SUCCESS) {        return ret;    }        if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {        if (!srs_is_client_gracefully_close(ret)) {            srs_error("write flv data tag failed. ret=%d", ret);        }        return ret;    }        return ret;}
metadata tag header写入cache

int SrsFlvTransmuxer::write_metadata_to_cache(char type, char* data, int size, char* cache){    int ret = ERROR_SUCCESS;        srs_assert(data);        // 11 bytes tag header    /*char tag_header[] = {     (char)type, // TagType UB [5], 18 = script data     (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.     (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.     (char)0x00, // TimestampExtended UI8     (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.     };*/        // write data size.    if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) {        return ret;    }    tag_stream->write_1bytes(type);    tag_stream->write_3bytes(size);    tag_stream->write_3bytes(0x00);    tag_stream->write_1bytes(0x00);    tag_stream->write_3bytes(0x00);        return ret;}

3. 写入Audio Tag数据

音频信息第一个byte格式如下。
   音频格式4 bits0 = Linear PCM, platform endian
1 = ADPCM
2 = MP3
3 = Linear PCM, little endian
4 = Nellymoser 16-kHz mono
5 = Nellymoser 8-kHz mono
6 = Nellymoser
7 = G.711 A-law logarithmic PCM
8 = G.711 mu-law logarithmic PCM
9 = reserved
10 = AAC
11 = Speex
14 = MP3 8-Khz
15 = Device-specific sound采样率2 bits0 = 5.5-kHz
1 = 11-kHz
2 = 22-kHz
3 = 44-kHz
对于AAC总是3采样的长度1 bit0 = snd8Bit
1 = snd16Bit
压缩过的音频都是16bit音频类型1 bit0 = sndMono
1 = sndStereo
对于AAC总是1

首先调用write_audio_to_cache将Audio的Tag Header写入到cache中,然后调用write_tag将写入cache的header与AudioData一同写入

int SrsFlvTransmuxer::write_audio(int64_t timestamp, char* data, int size){    int ret = ERROR_SUCCESS;        srs_assert(data);        if ((ret = write_audio_to_cache(timestamp, data, size, tag_header)) != ERROR_SUCCESS) {        return ret;    }        if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {        if (!srs_is_client_gracefully_close(ret)) {            srs_error("write flv audio tag failed. ret=%d", ret);        }        return ret;    }        return ret;}

Audio tag header 写入cache

int SrsFlvTransmuxer::write_audio_to_cache(int64_t timestamp, char* data, int size, char* cache){    int ret = ERROR_SUCCESS;        srs_assert(data);        timestamp &= 0x7fffffff;        // 11bytes tag header    /*char tag_header[] = {     (char)SrsFrameTypeAudio, // TagType UB [5], 8 = audio     (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.     (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.     (char)0x00, // TimestampExtended UI8     (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.     };*/        // write data size.    if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) {        return ret;    }    tag_stream->write_1bytes(SrsFrameTypeAudio);    tag_stream->write_3bytes(size);    tag_stream->write_3bytes((int32_t)timestamp);    // default to little-endian    tag_stream->write_1bytes((timestamp >> 24) & 0xFF);    tag_stream->write_3bytes(0x00);        return ret;}


4.写入Video Tag数据

视频信息第一个byte,格式说明如下:名称长度介绍帧类型4 bits1: keyframe (for AVC, a seekable frame)
2: inter frame (for AVC, a non-seekable frame)
3: disposable inter frame (H.263 only)
4: generated keyframe (reserved for server use only)
5: video info/command frame编码ID4 bits1: JPEG (currently unused)
2: Sorenson H.263
3: Screen video
4: On2 VP6
5: On2 VP6 with alpha channel
6: Screen video version 2

首先调用write_video_to_cache将header写入到cache,然后调用write_tag将tag Header与VideoData的数据写入

int SrsFlvTransmuxer::write_video(int64_t timestamp, char* data, int size){    int ret = ERROR_SUCCESS;        srs_assert(data);        if ((ret = write_video_to_cache(timestamp, data, size, tag_header)) != ERROR_SUCCESS) {        return ret;    }        if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {        srs_error("write flv video tag failed. ret=%d", ret);        return ret;    }        return ret;}

将Video tag header 写入cache中
int SrsFlvTransmuxer::write_video_to_cache(int64_t timestamp, char* data, int size, char* cache){    int ret = ERROR_SUCCESS;        srs_assert(data);        timestamp &= 0x7fffffff;        // 11bytes tag header    /*char tag_header[] = {     (char)SrsFrameTypeVideo, // TagType UB [5], 9 = video     (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.     (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.     (char)0x00, // TimestampExtended UI8     (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.     };*/        // write data size.    if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) {        return ret;    }    tag_stream->write_1bytes(SrsFrameTypeVideo);    tag_stream->write_3bytes(size);    tag_stream->write_3bytes((int32_t)timestamp);    // default to little-endian    tag_stream->write_1bytes((timestamp >> 24) & 0xFF);    tag_stream->write_3bytes(0x00);        return ret;}

5. 将tag header,tag data以及Previous tag size 一同写入 flv文件中

int SrsFlvTransmuxer::write_tag(char* header, int header_size, char* tag, int tag_size){    int ret = ERROR_SUCCESS;        // PreviousTagSizeN UI32 Size of last tag, including its header, in bytes.    char pre_size[SRS_FLV_PREVIOUS_TAG_SIZE];    if ((ret = write_pts_to_cache(tag_size + header_size, pre_size)) != ERROR_SUCCESS) {        return ret;    }        iovec iovs[3];    iovs[0].iov_base = header;    iovs[0].iov_len = header_size;    iovs[1].iov_base = tag;    iovs[1].iov_len = tag_size;    iovs[2].iov_base = pre_size;    iovs[2].iov_len = SRS_FLV_PREVIOUS_TAG_SIZE;        if ((ret = writer->writev(iovs, 3, NULL)) != ERROR_SUCCESS) {        if (!srs_is_client_gracefully_close(ret)) {            srs_error("write flv tag failed. ret=%d", ret);        }        return ret;    }        return ret;}









原创粉丝点击