音视频入门基础: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源码或者自己实现。

相关推荐
cuijiecheng20183 小时前
音视频入门基础:MPEG2-TS专题(21)——FFmpeg源码中,获取TS流的视频信息的实现
ffmpeg·音视频
γ..3 小时前
基于MATLAB的图像增强
开发语言·深度学习·神经网络·学习·机器学习·matlab·音视频
cuijiecheng20183 小时前
音视频入门基础:AAC专题(13)——FFmpeg源码中,获取ADTS格式的AAC裸流音频信息的实现
ffmpeg·音视频·aac
悟纤7 小时前
Suno Api V4模型无水印开发「高清音频WAV下载」 —— 「Suno Api系列」第6篇
音视频·suno·suno v4·suno ai
gomogomono13 小时前
HDR视频技术之八:色域映射
音视频·hdr·yuv
流氓也是种气质 _Cookie15 小时前
uniapp blob格式转换为video .mp4文件使用ffmpeg工具
ffmpeg·uni-app
野蛮的大西瓜17 小时前
BigBlueButton视频会议 vs 华为云会议的详细对比
人工智能·自动化·音视频·实时音视频·信息与通信·视频编解码
网络安全queen18 小时前
网络安全-企业环境渗透2-wordpress任意文件读&&FFmpeg任意文件读
安全·web安全·ffmpeg
野蛮的大西瓜18 小时前
文心一言对接FreeSWITCH实现大模型呼叫中心
人工智能·机器人·自动化·音视频·实时音视频·文心一言·信息与通信
野蛮的大西瓜19 小时前
BigBlueButton视频会议 vs 钉钉视频会议系统的详细对比
人工智能·自然语言处理·自动化·音视频·实时音视频·信息与通信·视频编解码