当前位置: 首页 > news >正文

音视频入门基础:MPEG2-TS专题(7)——FFmpeg源码中,读取出一个transport packet数据的实现

一、引言

从《音视频入门基础:MPEG2-TS专题(3)——TS Header简介》可以知道,TS格式有三种:分别为transport packet长度固定为188、192和204字节。而FFmpeg源码中是通过read_packet函数从一段MPEG2-TS传输流/TS文件中读取出一个transport packet的。

二、read_packet函数

(一)read_packet函数的定义

read_packet函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/mpegts.c中:

/* return AVERROR_something if error or EOF. Return 0 if OK. */
static int read_packet(AVFormatContext *s, uint8_t *buf, int raw_packet_size,const uint8_t **data)
{AVIOContext *pb = s->pb;int len;for (;;) {len = ffio_read_indirect(pb, buf, TS_PACKET_SIZE, data);if (len != TS_PACKET_SIZE)return len < 0 ? len : AVERROR_EOF;/* check packet sync byte */if ((*data)[0] != 0x47) {/* find a new packet start */if (mpegts_resync(s, raw_packet_size, *data) < 0)return AVERROR(EAGAIN);elsecontinue;} else {break;}}return 0;
}

该函数的作用是:从一段MPEG2-TS传输流/TS文件或内存中读取出接下来的一个transport packet的前188个字节。transport packet长度为192和204字节的TS格式实际上是在188字节的Packet后部加上额外的字段,所以read_packet函数只会读取出transport packet的前188字节。对于transport packet长度固定为188字节的TS格式,使用read_packet函数可以读取出一个transport packet的全部数据。

形参s:既是输入型参数也是输出型参数。指向一个AVFormatContext类型变量。

形参buf:输出型参数。仅当“AVIOContext输入缓冲区中还未被读取的数据量不小于计划要读取的字节数(TS_PACKET_SIZE,188个字节)”,并且“该MPEG2-TS传输流/TS文件被打开不是为了写入的”时有意义。保存读上来的数据的缓冲区。

形参data:输出型参数。“*data”指向保存读上来的数据的缓冲区。执行read_packet函数后,“*data”指向缓冲区会存贮被读取到的这个transport packet的前188字节。

返回值:返回0表示成功,返回一个负数表示出错。

(二)read_packet函数的内部实现分析

TS_PACKET_SIZE为宏定义,等价于188,表示一个普通transport packet的长度:

#define TS_PACKET_SIZE 188

read_packet函数中,首先通过ffio_read_indirect函数从内存或TS文件或socket中读取188个字节数据,如果实际读取到的数据小于188字节,表示文件读完了或出错了,read_packet函数直接返回。关于ffio_read_indirect函数的用法可以参考:《FFmpeg源码:ffio_read_indirect函数分析》:

        len = ffio_read_indirect(pb, buf, TS_PACKET_SIZE, data);if (len != TS_PACKET_SIZE)return len < 0 ? len : AVERROR_EOF;

如果上述读取到的数据的第一个字节不是同步字节(0x47),执行mpegts_resync函数进行重新同步操作,让AVIOContext文件位置指针(s->pb->buf_ptr)指向MPEG2-TS传输流/TS文件中的值为“0x47”的同步字节。再通过continue关键字在for循环中重新执行ffio_read_indirect函数,重新读取188个字节数据,保证读取到的数据的第一个字节为同步字节,从而保证通过read_packet函数读取的是一个transport packet的前188个字节数据:

        /* check packet sync byte */if ((*data)[0] != 0x47) {/* find a new packet start */if (mpegts_resync(s, raw_packet_size, *data) < 0)return AVERROR(EAGAIN);elsecontinue;} else {break;}

三、mpegts_resync函数

(一)mpegts_resync函数的定义

mpegts_resync函数定义在源文件libavformat/mpegts.c中:

static int mpegts_resync(AVFormatContext *s, int seekback, const uint8_t *current_packet)
{MpegTSContext *ts = s->priv_data;AVIOContext *pb = s->pb;int c, i;uint64_t pos = avio_tell(pb);int64_t back = FFMIN(seekback, pos);//Special case for files like 01c56b0dc1.tsif (current_packet[0] == 0x80 && current_packet[12] == 0x47 && pos >= TS_PACKET_SIZE) {avio_seek(pb, 12 - TS_PACKET_SIZE, SEEK_CUR);return 0;}avio_seek(pb, -back, SEEK_CUR);for (i = 0; i < ts->resync_size; i++) {c = avio_r8(pb);if (avio_feof(pb))return AVERROR_EOF;if (c == 0x47) {int new_packet_size, ret;avio_seek(pb, -1, SEEK_CUR);pos = avio_tell(pb);ret = ffio_ensure_seekback(pb, PROBE_PACKET_MAX_BUF);if (ret < 0)return ret;new_packet_size = get_packet_size(s);if (new_packet_size > 0 && new_packet_size != ts->raw_packet_size) {av_log(ts->stream, AV_LOG_WARNING, "changing packet size to %d\n", new_packet_size);ts->raw_packet_size = new_packet_size;}avio_seek(pb, pos, SEEK_SET);return 0;}}av_log(s, AV_LOG_ERROR,"max resync size reached, could not find sync byte\n");/* no sync found */return AVERROR_INVALIDDATA;
}

该函数的作用是:进行重新同步操作,让AVIOContext文件位置指针(s->pb->buf_ptr)指向MPEG2-TS传输流/TS文件中的值为“0x47”的同步字节。

形参s:既是输入型参数也是输出型参数。指向一个AVFormatContext类型变量。

形参seekback:输入型参数。需要回退的大小,值一般为该MPEG2-TS传输流/TS文件中的一个transport packet的长度,以字节为单位。

形参current_packet:输入型参数,保存一个transport packet的数据。

返回值:返回0表示成功,返回一个负数表示出错。

(二)mpegts_resync函数的内部实现分析

mpegts_resync函数中,首先通过avio_tell函数,得到文件位置指针当前位置(s->buf_ptr)相对于TS文件的文件首(s->buffer)的偏移字节数。关于avio_tell函数用法可以参考:《FFmpeg源码:avio_tell函数分析》:

    uint64_t pos = avio_tell(pb);

得到需要回退的最小值:

    int64_t back = FFMIN(seekback, pos);

判断该媒体文件/流是不是属于TS文件的特殊例子(非标的TS文件),如果是,把AVIOContext的文件位置指针回退到离当前位置(12 - TS_PACKET_SIZE)字节处。关于avio_seek函数的有用法可以参考:《FFmpeg源码:avio_seek函数分析》:

    //Special case for files like 01c56b0dc1.tsif (current_packet[0] == 0x80 && current_packet[12] == 0x47 && pos >= TS_PACKET_SIZE) {avio_seek(pb, 12 - TS_PACKET_SIZE, SEEK_CUR);return 0;}

不断通过avio_r8函数读取一个字节数据(关于avio_r8函数的有用法可以参考:FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析),如果读取到TS文件的末尾了(avio_feof(pb)为真),返回AVERROR_EOF(关于avio_feof函数的有用法可以参考:FFmpeg源码:avio_feof函数分析)。如果还没读取到末尾,并且读取到的数据是“0x47”,表示是同步字节,执行if (c == 0x47)为真时大括号里的操作:

    for (i = 0; i < ts->resync_size; i++) {c = avio_r8(pb);if (avio_feof(pb))return AVERROR_EOF;if (c == 0x47) {//...return 0;}}

c == 0x47为真时,首先通过avio_seek函数让AVIOContext的文件位置指针回退一个字节,这样pb->buf_ptr就会指向值为“0x47”的同步字节:

            int new_packet_size, ret;avio_seek(pb, -1, SEEK_CUR);

重新得到此时文件位置指针当前位置(s->buf_ptr)相对于TS文件的文件首(s->buffer)的偏移字节数:

            pos = avio_tell(pb);

确保请求的seekback缓冲区大小可用:

            ret = ffio_ensure_seekback(pb, PROBE_PACKET_MAX_BUF);if (ret < 0)return ret;

得到这段MPEG2-TS传输流/TS文件中每个transport packet的长度,赋值给变量new_packet_size。关于get_packet_size函数的用法可以参考:《音视频入门基础:MPEG2-TS专题(6)——FFmpeg源码中,获取MPEG2-TS传输流每个transport packet长度的实现》:

            new_packet_size = get_packet_size(s);

如果上述获取到的transport packet的长度跟原来内存中保存的transport packet长度不一致,打印日志:"changing packet size to XXX",调整内存中保存的transport packet长度(ts->raw_packet_size)为新的长度:

            if (new_packet_size > 0 && new_packet_size != ts->raw_packet_size) {av_log(ts->stream, AV_LOG_WARNING, "changing packet size to %d\n", new_packet_size);ts->raw_packet_size = new_packet_size;}

由于执行上述get_packet_size函数后,AVIOContext的文件位置指针(pb->buf_ptr)会改变,所以重新执行一次avio_seek函数,确保pb->buf_ptr指向值为“0x47”的同步字节:

            avio_seek(pb, pos, SEEK_SET);return 0;

相关文章:

音视频入门基础:MPEG2-TS专题(7)——FFmpeg源码中,读取出一个transport packet数据的实现

一、引言 从《音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;3&#xff09;——TS Header简介》可以知道&#xff0c;TS格式有三种&#xff1a;分别为transport packet长度固定为188、192和204字节。而FFmpeg源码中是通过read_packet函数从一段MPEG2-TS传输流/TS文件中读…...

Flutter中sqflite的使用案例

目录 引言 安装sqflite 创建表 查询数据 添加数据 删除数据 更新数据 完整使用案例 引言 随着移动应用的发展&#xff0c;本地数据存储成为了一个不可或缺的功能。在Flutter中&#xff0c;sqflite 是一个非常流行且强大的SQLite插件&#xff0c;它允许开发者在移动设备…...

【2024 Optimal Control 16-745】【Lecture 2】integrators.ipynb功能分析

代码功能分析 导入库和项目设置 import Pkg; Pkg.activate(__DIR__); Pkg.instantiate()功能&#xff1a;激活当前文件夹为 Julia 项目环境&#xff0c;并安装当前项目中缺失的依赖包。 import Pkg&#xff1a; 导入 Julia 的包管理模块 Pkg&#xff0c;用于管理项目依赖。 …...

【linux】ubuntu下常用快捷键【笔记】

环境 硬件&#xff1a;通用PC 系统&#xff1a;Ubuntu 20.04 软件 &#xff1a; 打开终端窗口&#xff1a;Ctrl Alt T 关闭当前窗口&#xff1a;Alt F4 改变窗口大小&#xff1a;Alt F8 移动窗口&#xff1a; Alt F7 配合 “←”、“→”、“↑”、“↓”来移动窗口 …...

【Linux】常用命令练习

一、常用命令 1、在/hadoop目录下创建src和WebRoot两个文件夹 分别创建&#xff1a;mkdir -p /hadoop/src mkdir -p /hadoop/WebRoot 同时创建&#xff1a;mkdir -p /hadoop/{src,WebRoot}2、进入到/hadoop目录&#xff0c;在该目录下创建.classpath和README文件 分别创建&am…...

力扣-Hot100-数组【算法学习day.37】

前言 ###我做这类文档一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&am…...

表格不同类型的数据如何向量化?

在进行机器学习项目时&#xff0c;首先需要获取数据&#xff0c;这些数据可以来自数据库、API、网络抓取&#xff0c;或从CSV、Excel等文件中读取。数据可能包含数值、文本和类别等多种特征&#xff0c;但原始数据通常无法直接用于训练模型。 数据预处理包括清洗、填补缺失值和…...

成都栩熙酷,电商服务新选择

在当今数字经济蓬勃发展的时代&#xff0c;电商平台已成为推动商业创新、促进消费升级的重要力量。抖音小店&#xff0c;作为短视频与电商深度融合的产物&#xff0c;凭借其独特的社交属性和内容营销优势&#xff0c;迅速吸引了大量用户和商家的关注。在这场变革中&#xff0c;…...

【java基础】微服务篇

参考黑马八股视频。 目录 Spring Cloud 5大组件 注册中心 负载均衡 限流 CAP和BASE 分布式事务解决方案 分布式服务的接口幂等性 分布式任务调度 Spring Cloud 5大组件 注册中心 Eureka的作用 健康监控 负载均衡 限流 漏桶固定速率&#xff0c;令牌桶不限速 CAP和BA…...

【LLM训练系列02】如何找到一个大模型Lora的target_modules

方法1&#xff1a;观察attention中的线性层 import numpy as np import pandas as pd from peft import PeftModel import torch import torch.nn.functional as F from torch import Tensor from transformers import AutoTokenizer, AutoModel, BitsAndBytesConfig from typ…...

uni-app快速入门(八)--常用内置组件(上)

uni-app提供了一套基础组件&#xff0c;类似HTML里的标签元素&#xff0c;不推荐在uni-app中使用使用div等HTML标签。在uni-app中&#xff0c;对应<div>的标签是view&#xff0c;对应<span>的是text&#xff0c;对应<a>的是navigator&#xff0c;常用uni-app…...

基于Amazon Bedrock:一站式多模态数据处理新体验

目录 引言 关于Amazon Bedrock 基础模型体验 1、进入环境 2、发现模型及快速体验 3、打开 Amazon Bedrock 控制台 4、通过 Playgrounds 体验模型 &#xff08;1&#xff09;文本生成 &#xff08;2&#xff09;图片生成 关于资源清理 结束语 引言 在云计算和人工智能…...

FAX动作文件优化脚本(MAX清理多余关键帧插件)

大较好,为大家介绍一个节省FBX容量的插件!只保留有用的动画轴向,其他不参与动画运动的清除! 一.插件目的:: 1.我们使用的U3D引擎产生的游戏资源包容量太大,故全方位优化动画资源; 2.在max曲线编辑器内,点取轴向太过麻烦,费事,直观清除帧大大提高效率。 如: 二:…...

Chapter 2 - 16. Understanding Congestion in Fibre Channel Fabrics

Transforming an I/O Operation to FC frames A read or write I/O operation (Figure 2-28) between an initiator and a target undergoes a series of transformations before being transmitted on a Fibre Channel link. 启动程序和目标程序之间的读取或写入 I/O 操作(图…...

mysql数据库(六)pymysql、视图、触发器、存储过程、函数、流程控制、数据库连接池

pymysql、视图、触发器、存储过程、函数、流程控制、数据库连接池 文章目录 pymysql、视图、触发器、存储过程、函数、流程控制、数据库连接池一、pymysql二、视图三、触发器四、存储过程五、函数六、流程控制七、数据库连接池 一、pymysql 可以使用pip install pymysql安装py…...

RFdiffusion EuclideanDiffuser类解读

EuclideanDiffuser 是 RFdiffusion 中的一个关键类,专门设计用于对**三维空间中的点(如蛋白质的原子坐标)**进行扩散处理。它通过逐步向这些点添加噪音来实现扩散过程,从而为扩散模型提供输入数据,并通过逆扩散还原这些数据。 get_beta_schedule函数源代码 def get_beta…...

Flutter实现气泡提示框学习

前置知识点学习 GlobalKey GlobalKey 是 Flutter 中一个非常重要的概念&#xff0c;它用于唯一标识 widget 树中的特定 widget&#xff0c;并提供对该 widget 的访问。这在需要跨越 widget 树边界进行交互或在 widget 树重建时保持状态时尤其有用。 GlobalKey 的作用 唯一标…...

vue3 路由守卫

在Vue 3中&#xff0c;路由守卫是一种控制和管理路由跳转的机制。它允许你在执行导航前后进行一些逻辑处理&#xff0c;比如权限验证、数据预取等&#xff0c;从而增强应用的安全性和效率。路由守卫分为几种不同的类型&#xff0c;每种类型的守卫都有其特定的应用场景。 其实路…...

【MATLAB源码-第218期】基于matlab的北方苍鹰优化算法(NGO)无人机三维路径规划,输出做短路径图和适应度曲线.

操作环境&#xff1a; MATLAB 2022a 1、算法描述 北方苍鹰优化算法&#xff08;Northern Goshawk Optimization&#xff0c;简称NGO&#xff09;是一种新兴的智能优化算法&#xff0c;灵感来源于北方苍鹰的捕猎行为。北方苍鹰是一种敏捷且高效的猛禽&#xff0c;广泛分布于北…...

如何控制自己玩手机的时间?两台苹果手机帮助自律

对一些人来说&#xff0c;被智能手机“绑架”是一件心甘情愿的事&#xff0c;和它相处的一天中&#xff0c;不必面对现实的压力&#xff0c;它就像个“舒适区”。这是因为在使用手机的过程中&#xff0c;应用程序&#xff08;尤其是游戏和社交媒体应用&#xff09;会不断刺激大…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...