音视频入门基础:FLV专题(8)——FFmpeg源码中,解码Tag header的实现

一、引言

在《音视频入门基础:FLV专题(7)------Tag header简介》中对Tag header进行了简介,本文讲述FFmpeg源码中是怎样解码FLV文件的Tag header,拿到里面的信息。

二、FFmpeg源码中,解码Tag header的实现

FFmpeg源码中使用flv_read_packet函数来读取每个Tag的信息。该函数的前半部分实现了解码Tag header的功能。该函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/flvdec.c中:

cpp 复制代码
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;
    int enhanced_flv = 0;
    uint32_t video_codec_id = 0;

retry:
    /* pkt size is repeated at end. skip it */
    pos  = avio_tell(s->pb);
    type = (avio_r8(s->pb) & 0x1F);
    orig_size =
    size = avio_rb24(s->pb);
    flv->sum_flv_tag_size += size + 11LL;
    dts  = avio_rb24(s->pb);
    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;
    avio_skip(s->pb, 3); /* stream id, always 0 */
    flags = 0;

//...

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

    next = size + avio_tell(s->pb);

    if (type == FLV_TAG_TYPE_AUDIO) {
        //...
    } else if (type == FLV_TAG_TYPE_VIDEO) {
        //...
    }else if (type == FLV_TAG_TYPE_META) {
        //...
    }else{
        //...
    }
    //...
    return ret;
}

flv_read_packet函数中,首先获取Tag header的TagType属性,赋值给局部变量type。关于avio_r8函数的用法可以参考:《FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析》。可以看到Tag header中的Filter属性并没有被保存到FFmpeg的内存中,所以FFmpeg源码内部是不会判断FLV文件是否被加密的:

cpp 复制代码
    type = (avio_r8(s->pb) & 0x1F);

在FFmpeg源码中TagType属性对应的枚举成员有三个:FLV_TAG_TYPE_AUDIO、FLV_TAG_TYPE_VIDEO、FLV_TAG_TYPE_META。如果局部变量type的值为FLV_TAG_TYPE_AUDIO表示该Tag为音频Tag;如果值为FLV_TAG_TYPE_VIDEO表示是视频Tag;如果值为FLV_TAG_TYPE_META表示是脚本Tag:

cpp 复制代码
enum FlvTagType {
    FLV_TAG_TYPE_AUDIO = 0x08,
    FLV_TAG_TYPE_VIDEO = 0x09,
    FLV_TAG_TYPE_META  = 0x12,
};

获取Tag header的DataSize属性,即该Tag以字节为单位的Tag data的长度,赋值给局部变量orig_size和size:

cpp 复制代码
    orig_size =
    size = avio_rb24(s->pb);

从《音视频入门基础:FLV专题(7)------Tag header简介》可以知道,DataSize属性的值等于整个Tag的长度 - 11。所以让DataSize属性的值加上11就是整个Tag的长度。所以flv->sum_flv_tag_size为已被读取的各个Tag加起来的总长度,单位为字节:

cpp 复制代码
    flv->sum_flv_tag_size += size + 11LL;

获取Tag header的Timestamp和TimestampExtended属性,合成1个4字节的解码时间戳,赋值给局部变量dts:

cpp 复制代码
    dts  = avio_rb24(s->pb);
    dts |= (unsigned)avio_r8(s->pb) << 24;

如果已经读取到文件末尾,返回AVERROR_EOF。关于avio_feof函数用法可以参考:《FFmpeg源码:avio_feof函数分析》:

cpp 复制代码
    if (avio_feof(s->pb))
        return AVERROR_EOF;

如果还没有读取到文件末尾,继续往下执行,跳过Tag header的StreamID属性。关于avio_skip函数用法可以参考:《FFmpeg源码:avio_skip函数分析》:

cpp 复制代码
    avio_skip(s->pb, 3); /* stream id, always 0 */

avio_tell(s->pb)是当前读取到的位置相对于文件首的偏移,关于avio_tell函数用法可以参考:《FFmpeg源码:avio_tell函数分析》。这时候已经读取完了该Tag的Tag header了,而从前面我们可以知道,局部变量size存贮该Tag的Tag data的长度。所以size + avio_tell(s->pb)为该Tag对应的PreviousTagSize相对于文件首的偏移(单位为字节):

cpp 复制代码
    next = size + avio_tell(s->pb);

根据该Tag为音频Tag、视频Tag还是脚本Tag,分别执行不同的解码操作:

cpp 复制代码
    if (type == FLV_TAG_TYPE_AUDIO) {
        //...
    } else if (type == FLV_TAG_TYPE_VIDEO) {
        //...
    }else if (type == FLV_TAG_TYPE_META) {
        //...
    }else{
        //...
    }
    //...

三、总结

1.FFmpeg源码中通过flv_read_packet函数的前半部分来解码FLV文件每个Tag的Tag header,根据Tag header的TagType属性来判断该Tag的类型,然后分别执行不同的解码Tag的操作。

2.Tag header中的Filter属性并没有被保存到FFmpeg的内存中,FFmpeg源码内部是不会判断FLV文件是否被加密的。要想处理加密过的FLV文件,得改FFmpeg源码或者自己实现。

相关推荐
xcLeigh5 小时前
HTML5超酷响应式视频背景动画特效(六种风格,附源码)
前端·音视频·html5
韩曙亮6 小时前
【FFmpeg】FFmpeg 内存结构 ③ ( AVPacket 函数简介 | av_packet_ref 函数 | av_packet_clone 函数 )
ffmpeg·音视频·avpacket·av_packet_clone·av_packet_ref·ffmpeg内存结构
9527华安10 小时前
FPGA实现PCIE3.0视频采集转10G万兆UDP网络输出,基于XDMA+GTH架构,提供工程源码和技术支持
网络·fpga开发·udp·音视频·xdma·pcie3.0·万兆网
电子科技圈11 小时前
XMOS携手合作伙伴晓龙国际联合推出集成了ASRC等功能的多通道音频板
科技·嵌入式硬件·mcu·物联网·音视频·iot
oushaojun211 小时前
ubuntu中使用ffmpeg和nginx推流rtmp视频
nginx·ubuntu·ffmpeg·rtmp
码码哈哈0.011 小时前
免费的视频混剪综合处理工具介绍与下载
音视频
莫固执,朋友11 小时前
网络抓包工具tcpdump 在海思平台上的编译使用
网络·ffmpeg·音视频·tcpdump
深海呐12 小时前
Android 从本地选择视频,用APP播放或进行其他处理
android·音视频·从本地选择视频,用app播放·从本地选择视频,并拿到信息·跳转到本地视频列表
lxkj_202412 小时前
修改ffmpeg实现https-flv内容加密
网络协议·https·ffmpeg
cuijiecheng201812 小时前
音视频入门基础:MPEG2-TS专题(6)——FFmpeg源码中,获取MPEG2-TS传输流每个transport packet长度的实现
ffmpeg·音视频