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

news/2024/12/31 2:49:13/

一、引言

在《音视频入门基础: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中:

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文件是否被加密的:

    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:

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:

    orig_size =size = avio_rb24(s->pb);

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

    flv->sum_flv_tag_size += size + 11LL;

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

    dts  = avio_rb24(s->pb);dts |= (unsigned)avio_r8(s->pb) << 24;

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

    if (avio_feof(s->pb))return AVERROR_EOF;

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

    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相对于文件首的偏移(单位为字节):

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

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

    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源码或者自己实现。


http://www.ppmy.cn/news/1533899.html

相关文章

Transformer架构分析

1 encoder 每个称之为一个layer&#xff0c;重复N次 每个里面有两个sublayer&#xff1b;multi-head self-attention MLP后面使用layer normalization LayerNorm(x Sublayer(x)) 残差连接需要两个维度一致&#xff0c;本文采用513。 2 decoder 3 注意力机制 输出维度和val…

空间计算/XR的现状:Meta Orion的优势与挑战

2024年初,苹果公司发布了Apple Vision Pro,这标志着空间计算/XR技术进入了一个新的阶段——双子座(Gemini)阶段。与此同时,Meta推出的Orion AR眼镜进一步确认了这一趋势。XR领域的关键意见领袖Rony Abovitz对Orion进行了深入分析,指出了其优势、亮点以及尚待改进的部分。…

【LeetCode】每日一题 2024_9_30 座位预约管理系统(堆)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 题目&#xff1a;座位预约管理系统 代码与解题思路 type SeatManager struct {sort.IntSlice // 默认最小堆 }// 非常常规的一道堆的题目&#xff0c;建一个最小堆 func Constructor(n int) SeatManager …

Spring Mvc 基础源码分析

一、onRefresh 初始化 在 Spring MVC 中&#xff0c;onRefresh 是 FrameworkServlet 类中的一个关键方法&#xff0c;负责在 Spring Web 应用的容器刷新时&#xff0c;初始化与 Web 相关的组件&#xff0c;包括加载和配置 DispatcherServlet。这一过程是 Spring MVC 启动和运行…

【已解决】【Hadoop】找到java环境路径

在 Hadoop 环境中&#xff0c;Java 环境路径通常指的是 Java 的安装目录&#xff0c;因为 Hadoop 是用 Java 编写的&#xff0c;并且需要 Java 运行时环境&#xff08;JRE&#xff09;或 Java 开发工具&#xff08;JDK&#xff09;来运行。以下是几种方法来找到 Java 环境路径&…

go channel的使用

channel是goroutine之间通信的管道&#xff0c;可以将值从一个goroutine发送到channel&#xff0c;另一个goroutine从channel接收到这些值。 Do not communicate by sharing memory; instead, share memory by communicating. 创建channel //无缓存channel ch : make(chan int…

通信工程学习:什么是NFS网络文件系统

NFS&#xff1a;网络文件系统 NFS&#xff08;Network File System&#xff09;&#xff0c;即网络文件系统&#xff0c;是一种用于在计算机网络上共享文件的协议。它允许一个计算机系统通过网络将其文件和存储设备共享给其他计算机系统&#xff0c;使得这些系统可以像访问本地…

C++随心记

C随心记 C中的 CONST C中的const是表示不可修改 int main() {/* 对于变量而言 */// 不可修改的常量const int A 10;// 不可修改的指针指向const int* pointer_0 nullptr;int const* poniter_1 nullptr;// 不可修改指针指向的内容int* const poniter_2 nullptr; }const也…