当前位置: 首页 > 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;会不断刺激大…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

stm32wle5 lpuart DMA数据不接收

配置波特率9600时&#xff0c;需要使用外部低速晶振...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL

ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...