FLV 格式详解

一、FLV格式介绍:

      是Adobe发布的一种可以用于直播也可以用于点播的封装格式,以FLVTAG的形式存在,每一个TAG都是独立存在的,我们先用FlvAnalyzer.exe打开一个flv视频看看。
 

 

 

 

   从上图可以看到FLV包括文件头(File Header)和文件体(File Body)两部分,其中文件体由一系列的Tag组成。
1. 文件头

Header 部分记录了FLV的类型、版本等信息,是FLV的开头。一般差不多占9bytes。具体格式如下:

a. 文件标识(3B):总是为”FLV”, 0x46 0x4c 0x56

b. 版本(1B):目前为0x01

c. 流信息(1B):文件的标志位说明。前5位保留,必须为0;第6位为音频Tag:1表示有音频;第七位保留,为0; 第8位为视频Tag:1表示有视频

d. Header长度(4B):整个Header的长度,一般为9(版本为0x01时);大于9表示下面还有扩展信息。即0x00000009。

2. 文件体 

文件体由一系列的Tag组成。其中,每个Tag前面还包含了Previous Tag Size字段,表示前面一个Tag的大小,其中第一个Tag的Previous Tag Size为0。Tag的类型可以是视频、音频和Script,每个Tag只能包含以上三种类型的数据中的一种。

3. Tag

每个Tag由也是由两部分组成的:Tag Header和Tag Data。Tag Header里存放的是当前Tag的类型、数据区(Tag Data)长度等信息,具体如下:

Tag类型(1):0x08:音频;     0x09:视频;      0x12:脚本;     其他:保留

数据区长度(3):数据区的长度

时间戳(3):整数,单位是毫秒。对于脚本型的tag总是0 (CTS)

时间戳扩展(1):将时间戳扩展为4bytes,代表高8位。很少用到

StreamsID(3):总是0

数据区(由数据区长度决定):数据实体

下面是三个Tag类型说明:

Audio Tag Data结构(音频类型) :音频Tag Data区域开始的第一个字节包含了音频数据的参数信息,从第二个字节开始为音频流数据。

video Tag Data结构(视频类型):视频Tag Data开始的第一个字节包含视频数据的参数信息,从第二个字节开始为视频流数据。
Script Tag Data结构(脚本类型、帧类型):该类型Tag又被称为MetaData Tag,存放一些关于FLV视频和音频的元信息,比如:duration、width、height等。通常该类型Tag会作为FLV文件的第一个tag,并且只有一个,跟在File Header后。

二、FLV格式使用

    文章开头我们有提到 flv可以用于直播也可以用于点播的封装格式。之前的H264编码有讲到I帧(关键帧),P帧,B帧。关键帧可以独立解码出一张图片并且解码P,B也是需要I的。所以在点播,直播/点播的时候用户是从任意时间看的,而不是从文件头开始。这样当用户定位到某个时间的时候就需要寻找最近的I帧,从这里开始解码播放。所以我们在实际应用中,特别是现在直播的应用中,我们往往需要向FLV格式中写入关键帧索引,并将这些索引文件写在Metadata 中。

1. 使用FFmpeg生成带关键索引信息的FLV

ffmpeg -i U1.mp4 -c copy -f flv -flvflags add_keyframe_index U1.flv 

2.使用ffprobe -v trace -i U1.flv 查看信息:

[NULL @ 0x556298fe7dc0] Opening 'U1.flv' for reading

[file @ 0x556298fe8940] Setting default whitelist 'file,crypto,data'

Probing flv score:100 size:2048

[flv @ 0x556298fe7dc0] Format flv probed with size=2048 and score=100

[flv @ 0x556298fe7dc0] Before avformat_find_stream_info() pos: 13 bytes read:32768 seeks:0 nb_streams:0

[flv @ 0x556298fe7dc0] type:18, size:1904, last:-1, dts:0 pos:21

[flv @ 0x556298fe7dc0] keyframe stream hasn't been created

[flv @ 0x556298fe7dc0] type:9, size:45, last:-1, dts:0 pos:1940

[flv @ 0x556298fe7dc0] keyframe filepositions = 2016 times = 0

[flv @ 0x556298fe7dc0] keyframe filepositions = 411562 times = 6000

[flv @ 0x556298fe7dc0] keyframe filepositions = 711285 times = 12000

[flv @ 0x556298fe7dc0] keyframe filepositions = 921593 times = 18000

[flv @ 0x556298fe7dc0] keyframe filepositions = 1126035 times = 24000
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。


  Duration: 00:07:01.96, start: 0.000000, bitrate: 507 kb/s

    Stream #0:0, 41, 1/1000: Video: h264 (Main), 1 reference frame, yuv420p(progressive, left), 576x432 [SAR 1:1 DAR 4:3], 0/1, 468 kb/s, 25 fps, 25 tbr, 1k tbn, 50 tbc

    Stream #0:1, 35, 1/1000: Audio: aac (HE-AAC), 44100 Hz, stereo, fltp, 32 kb/s

[h264 @ 0x556299016cc0] nal_unit_type: 7(SPS), nal_ref_idc: 3

[h264 @ 0x556299016cc0] nal_unit_type: 8(PPS), nal_ref_idc: 3

[AVIOContext @ 0x556298ff0d40] Statistics: 163840 bytes read, 0 seeks

crl6@crl6-VirtualBox:~/work/AudioVideo$ 

三、ffmpeg源码分析

static int flv_read_header(AVFormatContext *s)
{
    int flags;
    FLVContext *flv = s->priv_data;
    int offset;
    int pre_tag_size = 0;
    /* Actual FLV data at 0xe40000 in KUX file */
    if(!strcmp(s->iformat->name, "kux"))
        avio_skip(s->pb, 0xe40000);

	//跳过FLV和版本号
    avio_skip(s->pb, 4);
    //读取一个字节,得到Flags
    flags = avio_r8(s->pb);
	//检查音频流或视频流
    flv->missing_streams = flags & (FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO);

    s->ctx_flags |= AVFMTCTX_NOHEADER;
	//读取4个字节,也就是HeaderSize
    offset = avio_rb32(s->pb);
    //偏移4个字节
    avio_seek(s->pb, offset, SEEK_SET);

    /* Annex E. The FLV File Format
     * E.3 TheFLVFileBody
     *     Field               Type    Comment
     *     PreviousTagSize0    UI32    Always 0
     * */
    //读取4个字节,也就是我们的body的第一个 previous tag size
    pre_tag_size = avio_rb32(s->pb);
    if (pre_tag_size) {
        av_log(s, AV_LOG_WARNING, "Read FLV header error, input file is not a standard flv format, first PreviousTagSize0 always is 0\n");
    }

    s->start_time = 0;
    flv->sum_flv_tag_size = 0;
    flv->last_keyframe_stream_index = -1;

    return 0;
}



static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    FLVContext *flv = s->priv_data;
    int ret, i, size, flags;
    enum FlvTagType type;
    int stream_type=-1;
    int64_t next, pos, meta_pos;
    int64_t dts, pts = AV_NOPTS_VALUE;
    int av_uninit(channels);
    int av_uninit(sample_rate);
    AVStream *st    = NULL;
    int last = -1;
    int orig_size;

retry:
    /* pkt size is repeated at end. skip it */
    //得到目前所在的位置
        pos  = avio_tell(s->pb);
        //读取一个字节,也就是我们Tag Header中的Type
        //每个NALU的第一个byte & 0x1f就可以得出它的类型
        type = (avio_r8(s->pb) & 0x1F);
        //读取3个字节,得到Tag Data的大小
        orig_size =
        size = avio_rb24(s->pb);
        //得到总的Tag的大小
        flv->sum_flv_tag_size += size + 11;
        //读取3个字节,得到Timestamp
        dts  = avio_rb24(s->pb);
        //再读取一个字节,得到Timestamp_ex,然后拼接为dts
        dts |= (unsigned)avio_r8(s->pb) << 24;
        av_log(s, AV_LOG_TRACE, "type:%d, size:%d, last:%d, dts:%"PRId64" pos:%"PRId64"\n", type, size, last, dts, avio_tell(s->pb));
        if (avio_feof(s->pb))
            return AVERROR_EOF;
        //跳过三个字节,也就是StreamID    
        avio_skip(s->pb, 3); /* stream id, always 0 */
        flags = 0;
		
        if (flv->validate_next < flv->validate_count) {
            int64_t validate_pos = flv->validate_index[flv->validate_next].pos;
            if (pos == validate_pos) {
                if (FFABS(dts - flv->validate_index[flv->validate_next].dts) <=
                    VALIDATE_INDEX_TS_THRESH) {
                    flv->validate_next++;
                } else {
                    clear_index_entries(s, validate_pos);
                    flv->validate_count = 0;
                }
            } else if (pos > validate_pos) {
                clear_index_entries(s, validate_pos);
                flv->validate_count = 0;
            }
        }
		//如果tag data的size为0,就跳到leave
        if (size == 0) {
            ret = FFERROR_REDO;
            goto leave;
        }

        next = size + avio_tell(s->pb);
		//如果是audio
        if (type == FLV_TAG_TYPE_AUDIO) {
            stream_type = FLV_STREAM_TYPE_AUDIO;
            //读取一个字节,得到音频信息
            flags    = avio_r8(s->pb);
            size--;
        } else if (type == FLV_TAG_TYPE_VIDEO) {
            stream_type = FLV_STREAM_TYPE_VIDEO;
            //得到视频信息
            flags    = avio_r8(s->pb);
            size--;
            //如果是video info/command frame
            if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD)
                goto skip;
        } else if (type == FLV_TAG_TYPE_META) { //如果是元数据
            stream_type=FLV_STREAM_TYPE_DATA;
            if (size > 13 + 1 + 4) { // Header-type metadata stuff
                int type;
                meta_pos = avio_tell(s->pb);
                //读取metabody  1
                type = flv_read_metabody(s, next);
                if (type == 0 && dts == 0 || type < 0 || type == TYPE_UNKNOWN) {
                    if (type < 0 && flv->validate_count &&
                        flv->validate_index[0].pos     > next &&
                        flv->validate_index[0].pos - 4 < next
                    ) {
                        av_log(s, AV_LOG_WARNING, "Adjusting next position due to index mismatch\n");
                        next = flv->validate_index[0].pos - 4;
                    }
                    goto skip;
                } else if (type == TYPE_ONTEXTDATA) {
                    avpriv_request_sample(s, "OnTextData packet");
                    //2
                    return flv_data_packet(s, pkt, dts, next);
                } else if (type == TYPE_ONCAPTION) {
                    return flv_data_packet(s, pkt, dts, next);
                }
                avio_seek(s->pb, meta_pos, SEEK_SET);
            }
        } else {
            av_log(s, AV_LOG_DEBUG,
                   "Skipping flv packet: type %d, size %d, flags %d.\n",
                   type, size, flags);
skip:
            if (avio_seek(s->pb, next, SEEK_SET) != next) {
                 // This can happen if flv_read_metabody above read past
                 // next, on a non-seekable input, and the preceding data has
                 // been flushed out from the IO buffer.
                 av_log(s, AV_LOG_ERROR, "Unable to seek to the next packet\n");
                 return AVERROR_INVALIDDATA;
            }
            ret = FFERROR_REDO;
            goto leave;
        }

        /* skip empty data packets */
        if (!size) {
            ret = FFERROR_REDO;
            goto leave;
        }

        /* now find stream */
        //找到相应的codec
        for (i = 0; i < s->nb_streams; i++) {
            st = s->streams[i];
            if (stream_type == FLV_STREAM_TYPE_AUDIO) {
                if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
                    (s->audio_codec_id || flv_same_audio_codec(st->codecpar, flags)))
                    break;
            } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
                if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
                    (s->video_codec_id || flv_same_video_codec(st->codecpar, flags)))
                    break;
            } else if (stream_type == FLV_STREAM_TYPE_DATA) {
                if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
                    break;
            }
        }
        if (i == s->nb_streams) {
            static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_SUBTITLE};
            //如果没有就创建
            st = create_stream(s, stream_types[stream_type]);
            if (!st)
                return AVERROR(ENOMEM);

        }
        av_log(s, AV_LOG_TRACE, "%d %X %d \n", stream_type, flags, st->discard);

        if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
            ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
              stream_type == FLV_STREAM_TYPE_AUDIO))
            av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME);

        if (  (st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || (stream_type == FLV_STREAM_TYPE_AUDIO)))
            ||(st->discard >= AVDISCARD_BIDIR  &&  ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && (stream_type == FLV_STREAM_TYPE_VIDEO)))
            || st->discard >= AVDISCARD_ALL
        ) {
            avio_seek(s->pb, next, SEEK_SET);
            ret = FFERROR_REDO;
            goto leave;
        }

    // if not streamed and no duration from metadata then seek to end to find
    // the duration from the timestamps
    if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
        (!s->duration || s->duration == AV_NOPTS_VALUE) &&
        !flv->searched_for_end) {
        int size;
        const int64_t pos   = avio_tell(s->pb);
        // Read the last 4 bytes of the file, this should be the size of the
        // previous FLV tag. Use the timestamp of its payload as duration.
        int64_t fsize       = avio_size(s->pb);
retry_duration:
        avio_seek(s->pb, fsize - 4, SEEK_SET);
        size = avio_rb32(s->pb);
        if (size > 0 && size < fsize) {
            // Seek to the start of the last FLV tag at position (fsize - 4 - size)
            // but skip the byte indicating the type.
            avio_seek(s->pb, fsize - 3 - size, SEEK_SET);
            if (size == avio_rb24(s->pb) + 11) {
                uint32_t ts = avio_rb24(s->pb);
                ts         |= avio_r8(s->pb) << 24;
                if (ts)
                    s->duration = ts * (int64_t)AV_TIME_BASE / 1000;
                else if (fsize >= 8 && fsize - 8 >= size) {
                    fsize -= size+4;
                    goto retry_duration;
                }
            }
        }

        avio_seek(s->pb, pos, SEEK_SET);
        flv->searched_for_end = 1;
    }
	//如果是音频流
    if (stream_type == FLV_STREAM_TYPE_AUDIO) {
        int bits_per_coded_sample;
        channels = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1;
        sample_rate = 44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >>
                                FLV_AUDIO_SAMPLERATE_OFFSET) >> 3;
        bits_per_coded_sample = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8;
        if (!st->codecpar->channels || !st->codecpar->sample_rate ||
            !st->codecpar->bits_per_coded_sample) {
            st->codecpar->channels              = channels;
            st->codecpar->channel_layout        = channels == 1
                                               ? AV_CH_LAYOUT_MONO
                                               : AV_CH_LAYOUT_STEREO;
            st->codecpar->sample_rate           = sample_rate;
            st->codecpar->bits_per_coded_sample = bits_per_coded_sample;
        }
        if (!st->codecpar->codec_id) {
        //设置音频编解码
            flv_set_audio_codec(s, st, st->codecpar,
                                flags & FLV_AUDIO_CODECID_MASK);
            flv->last_sample_rate =
            sample_rate           = st->codecpar->sample_rate;
            flv->last_channels    =
            channels              = st->codecpar->channels;
        } else {
            AVCodecParameters *par = avcodec_parameters_alloc();
            if (!par) {
                ret = AVERROR(ENOMEM);
                goto leave;
            }
            par->sample_rate = sample_rate;
            par->bits_per_coded_sample = bits_per_coded_sample;
            flv_set_audio_codec(s, st, par, flags & FLV_AUDIO_CODECID_MASK);
            sample_rate = par->sample_rate;
            avcodec_parameters_free(&par);
        }
    } else if (stream_type == FLV_STREAM_TYPE_VIDEO) { //如果是视频流
    //设置视频编解码
        int ret = flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK, 1);
        if (ret < 0)
            return ret;
        size -= ret;
    } else if (stream_type == FLV_STREAM_TYPE_DATA) { //如果是Script流
        st->codecpar->codec_id = AV_CODEC_ID_TEXT;
    }
	//如果是这几个格式
    if (st->codecpar->codec_id == AV_CODEC_ID_AAC ||
        st->codecpar->codec_id == AV_CODEC_ID_H264 ||
        st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {
        //读取1个字节,得到包类型,可以参考h264的结构来看
        int type = avio_r8(s->pb);
        size--;

        if (size < 0) {
            ret = AVERROR_INVALIDDATA;
            goto leave;
        }

        if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {
            // sign extension
            //如果3个字节,得到cts
            int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;
            //通过cts得到pts
            pts = dts + cts;
            if (cts < 0) { // dts might be wrong
                if (!flv->wrong_dts)
                    av_log(s, AV_LOG_WARNING,
                        "Negative cts, previous timestamps might be wrong.\n");
                flv->wrong_dts = 1;
            } else if (FFABS(dts - pts) > 1000*60*15) {
                av_log(s, AV_LOG_WARNING,
                       "invalid timestamps %"PRId64" %"PRId64"\n", dts, pts);
                dts = pts = AV_NOPTS_VALUE;
            }
        }
        if (type == 0 && (!st->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC ||
            st->codecpar->codec_id == AV_CODEC_ID_H264)) {
            AVDictionaryEntry *t;

            if (st->codecpar->extradata) {
                if ((ret = flv_queue_extradata(flv, s->pb, stream_type, size)) < 0)
                    return ret;
                ret = FFERROR_REDO;
                goto leave;
            }
            if ((ret = flv_get_extradata(s, st, size)) < 0)
                return ret;

            /* Workaround for buggy Omnia A/XE encoder */
            t = av_dict_get(s->metadata, "Encoder", NULL, 0);
            if (st->codecpar->codec_id == AV_CODEC_ID_AAC && t && !strcmp(t->value, "Omnia A/XE"))
                st->codecpar->extradata_size = 2;

            if (st->codecpar->codec_id == AV_CODEC_ID_AAC && 0) {
                MPEG4AudioConfig cfg;

                if (avpriv_mpeg4audio_get_config(&cfg, st->codecpar->extradata,
                                                 st->codecpar->extradata_size * 8, 1) >= 0) {
                st->codecpar->channels       = cfg.channels;
                st->codecpar->channel_layout = 0;
                if (cfg.ext_sample_rate)
                    st->codecpar->sample_rate = cfg.ext_sample_rate;
                else
                    st->codecpar->sample_rate = cfg.sample_rate;
                av_log(s, AV_LOG_TRACE, "mp4a config channels %d sample rate %d\n",
                        st->codecpar->channels, st->codecpar->sample_rate);
                }
            }

            ret = FFERROR_REDO;
            goto leave;
        }
    }

    /* skip empty data packets */
    if (!size) {
        ret = FFERROR_REDO;
        goto leave;
    }
	// 获得AVPacket并初始化
    ret = av_get_packet(s->pb, pkt, size);
    if (ret < 0)
        return ret;
    pkt->dts          = dts;
    pkt->pts          = pts == AV_NOPTS_VALUE ? dts : pts;
    pkt->stream_index = st->index;
    pkt->pos          = pos;
    if (flv->new_extradata[stream_type]) {
        uint8_t *side = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
                                                flv->new_extradata_size[stream_type]);
        if (side) {
            memcpy(side, flv->new_extradata[stream_type],
                   flv->new_extradata_size[stream_type]);
            av_freep(&flv->new_extradata[stream_type]);
            flv->new_extradata_size[stream_type] = 0;
        }
    }
    if (stream_type == FLV_STREAM_TYPE_AUDIO &&
                    (sample_rate != flv->last_sample_rate ||
                     channels    != flv->last_channels)) {
        flv->last_sample_rate = sample_rate;
        flv->last_channels    = channels;
        ff_add_param_change(pkt, channels, 0, sample_rate, 0, 0);
    }

    if (    stream_type == FLV_STREAM_TYPE_AUDIO ||
            ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY) ||
            stream_type == FLV_STREAM_TYPE_DATA)
        pkt->flags |= AV_PKT_FLAG_KEY;

leave:
    last = avio_rb32(s->pb);
    if (last != orig_size + 11 && last != orig_size + 10 &&
        !avio_feof(s->pb) &&
        (last != orig_size || !last) && last != flv->sum_flv_tag_size &&
        !flv->broken_sizes) {
        av_log(s, AV_LOG_ERROR, "Packet mismatch %d %d %d\n", last, orig_size + 11, flv->sum_flv_tag_size);
        avio_seek(s->pb, pos + 1, SEEK_SET);
        ret = resync(s);
        av_packet_unref(pkt);
        if (ret >= 0) {
            goto retry;
        }
    }
    return ret;
}
static int flv_read_metabody(AVFormatContext *s, int64_t next_pos)
{
    FLVContext *flv = s->priv_data;
    AMFDataType type;
    AVStream *stream, *astream, *vstream;
    AVStream av_unused *dstream;
    AVIOContext *ioc;
    int i;
    // only needs to hold the string "onMetaData".
    // Anything longer is something we don't want.
    char buffer[32];

    astream = NULL;
    vstream = NULL;
    dstream = NULL;
    ioc     = s->pb;

    // first object needs to be "onMetaData" string
    //读取8个字节,也就是AMF包
    type = avio_r8(ioc);
    if (type != AMF_DATA_TYPE_STRING ||
        amf_get_string(ioc, buffer, sizeof(buffer)) < 0)
        return TYPE_UNKNOWN;

    if (!strcmp(buffer, "onTextData"))
        return TYPE_ONTEXTDATA;

    if (!strcmp(buffer, "onCaption"))
        return TYPE_ONCAPTION;

    if (!strcmp(buffer, "onCaptionInfo"))
        return TYPE_ONCAPTIONINFO;

    if (strcmp(buffer, "onMetaData") && strcmp(buffer, "onCuePoint")) {
        av_log(s, AV_LOG_DEBUG, "Unknown type %s\n", buffer);
        return TYPE_UNKNOWN;
    }

    // find the streams now so that amf_parse_object doesn't need to do
    // the lookup every time it is called.
    for (i = 0; i < s->nb_streams; i++) {
        stream = s->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            vstream = stream;
            flv->last_keyframe_stream_index = i;
        } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            astream = stream;
            if (flv->last_keyframe_stream_index == -1)
                flv->last_keyframe_stream_index = i;
        }
        else if (stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
            dstream = stream;
    }

    // parse the second object (we want a mixed array)
    if (amf_parse_object(s, astream, vstream, buffer, next_pos, 0) < 0)
        return -1;

    return 0;
}
static int flv_data_packet(AVFormatContext *s, AVPacket *pkt,
                           int64_t dts, int64_t next)
{
    AVIOContext *pb = s->pb;
    AVStream *st    = NULL;
    char buf[20];
    int ret = AVERROR_INVALIDDATA;
    int i, length = -1;
    int array = 0;
	//读取1个字节,得到AMF包的类型
    switch (avio_r8(pb)) {
    case AMF_DATA_TYPE_ARRAY:
        array = 1;
    case AMF_DATA_TYPE_MIXEDARRAY:
        avio_seek(pb, 4, SEEK_CUR);
    case AMF_DATA_TYPE_OBJECT:
        break;
    default:
        goto skip;
    }
	//读取AMF包中的数组
    while (array || (ret = amf_get_string(pb, buf, sizeof(buf))) > 0) {
    //得到key
        AMFDataType type = avio_r8(pb);
        if (type == AMF_DATA_TYPE_STRING && (array || !strcmp(buf, "text"))) {
        	//得到数组长度
            length = avio_rb16(pb);
            //获得数组内容
            ret    = av_get_packet(pb, pkt, length);
            if (ret < 0)
                goto skip;
            else
                break;
        } else {
            if ((ret = amf_skip_tag(pb, type)) < 0)
                goto skip;
        }
    }

    if (length < 0) {
        ret = AVERROR_INVALIDDATA;
        goto skip;
    }

    for (i = 0; i < s->nb_streams; i++) {
        st = s->streams[i];
        if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
            break;
    }

    if (i == s->nb_streams) {
        st = create_stream(s, AVMEDIA_TYPE_SUBTITLE);
        if (!st)
            return AVERROR(ENOMEM);
        st->codecpar->codec_id = AV_CODEC_ID_TEXT;
    }

    pkt->dts  = dts;
    pkt->pts  = dts;
    pkt->size = ret;

    pkt->stream_index = st->index;
    pkt->flags       |= AV_PKT_FLAG_KEY;

skip:
    avio_seek(s->pb, next + 4, SEEK_SET);

    return ret;
}

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值