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

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...