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

音视频入门基础:MPEG2-PS专题(6)——FFmpeg源码中,获取PS流的视频信息的实现

一、引言

通过FFmpeg命令可以获取到PS文件/PS流的视频压缩编码格式、色彩格式(像素格式)、分辨率、帧率信息:

./ffmpeg -i XXX.ps

本文以H.264为例讲述FFmpeg到底是从哪个地方获取到这些视频信息的。  

二、视频压缩编码格式

(一)FFmpeg获取PS流的视频压缩编码格式的原理

FFmpeg获取PS文件/PS流的视频压缩编码格式,是从PES packet的有效载荷,即ES流数据中获取的。从《音视频入门基础:MPEG2-TS专题(18)——PES流简介》可以知道,PES packet的PES packet header里面存在一个stream_id属性,指定ES流的类型和编号。但是仅根据这个stream_id属性是无法判断视频压缩编码格式的:

所以要获取视频压缩编码格式,得从PES packet的有效载荷中获取。用Elecard Stream Analyzer工具可以看到,如果PS流的视频压缩编码格式为H.264,那PES packet的有效载荷中携带的就是以0x000001或0x00000001作为起始码的AnnexB格式的H.264码流(关于AnnexB可以参考:《音视频入门基础:H.264专题(3)——EBSP, RBSP和SODB》):

所以这时候就可以通过解析PES packet的有效载荷,即ES流来获取视频压缩编码格式。下面讲解相关代码。

(二)FFmpeg获取PS流的视频压缩编码格式的实现

由《音视频入门基础:MPEG2-PS专题(5)——FFmpeg源码中,解析PS流中的PES流的实现》可以知道,FFmpeg源码中通过mpegps_read_pes_header函数解析PS流中的一个PES packet,将其PES packet header里面的信息解析出来。而在调用完mpegps_read_pes_header函数后,指针s->pb.buf_ptr会指向该PES packet的有效载荷,如果PS流的视频压缩编码格式为H.264,那就是指向以0x000001或0x00000001作为起始码的AnnexB格式的H.264码流:

然后在mpegps_read_packet函数中,会通过av_get_packet函数将s->pb.buf_ptr指向的H.264码流数据保存到pkt->data指向的缓冲区中(关于av_get_packet函数的用法可以参考:《FFmpeg源码:append_packet_chunked、av_get_packet、av_append_packet函数分析》):

static int mpegps_read_packet(AVFormatContext *s,AVPacket *pkt)
{
//...len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts);
//...ret = av_get_packet(s->pb, pkt, len);
//...
}

之后在probe_codec函数中,会通过语句:memcpy(pd->buf + pd->buf_size, pkt->data, pkt->size) 将上述H.264码流数据从pkt->data拷贝到pd->buf中:

static int probe_codec(AVFormatContext *s, AVStream *st, const AVPacket *pkt)
{
//...if (sti->request_probe > 0) {//...AVProbeData *const pd = &sti->probe_data;int end;av_log(s, AV_LOG_DEBUG, "probing stream %d pp:%d\n", st->index, sti->probe_packets);--sti->probe_packets;if (pkt) {uint8_t *new_buf = av_realloc(pd->buf, pd->buf_size+pkt->size+AVPROBE_PADDING_SIZE);if (!new_buf) {av_log(s, AV_LOG_WARNING,"Failed to reallocate probe buffer for stream %d\n",st->index);goto no_packet;}pd->buf = new_buf;memcpy(pd->buf + pd->buf_size, pkt->data, pkt->size);pd->buf_size += pkt->size;memset(pd->buf + pd->buf_size, 0, AVPROBE_PADDING_SIZE);}//...}
//...
}

然后probe_codec函数中会调用set_codec_from_probe_data函数,set_codec_from_probe_data函数的定义如下:

static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st,AVProbeData *pd)
{static const struct {const char *name;enum AVCodecID id;enum AVMediaType type;} fmt_id_type[] = {{ "aac",        AV_CODEC_ID_AAC,          AVMEDIA_TYPE_AUDIO    },{ "ac3",        AV_CODEC_ID_AC3,          AVMEDIA_TYPE_AUDIO    },{ "aptx",       AV_CODEC_ID_APTX,         AVMEDIA_TYPE_AUDIO    },{ "dts",        AV_CODEC_ID_DTS,          AVMEDIA_TYPE_AUDIO    },{ "dvbsub",     AV_CODEC_ID_DVB_SUBTITLE, AVMEDIA_TYPE_SUBTITLE },{ "dvbtxt",     AV_CODEC_ID_DVB_TELETEXT, AVMEDIA_TYPE_SUBTITLE },{ "eac3",       AV_CODEC_ID_EAC3,         AVMEDIA_TYPE_AUDIO    },{ "h264",       AV_CODEC_ID_H264,         AVMEDIA_TYPE_VIDEO    },{ "hevc",       AV_CODEC_ID_HEVC,         AVMEDIA_TYPE_VIDEO    },{ "loas",       AV_CODEC_ID_AAC_LATM,     AVMEDIA_TYPE_AUDIO    },{ "m4v",        AV_CODEC_ID_MPEG4,        AVMEDIA_TYPE_VIDEO    },{ "mjpeg_2000", AV_CODEC_ID_JPEG2000,     AVMEDIA_TYPE_VIDEO    },{ "mp3",        AV_CODEC_ID_MP3,          AVMEDIA_TYPE_AUDIO    },{ "mpegvideo",  AV_CODEC_ID_MPEG2VIDEO,   AVMEDIA_TYPE_VIDEO    },{ "truehd",     AV_CODEC_ID_TRUEHD,       AVMEDIA_TYPE_AUDIO    },{ "evc",        AV_CODEC_ID_EVC,          AVMEDIA_TYPE_VIDEO    },{ "vvc",        AV_CODEC_ID_VVC,          AVMEDIA_TYPE_VIDEO    },{ 0 }};int score;const AVInputFormat *fmt = av_probe_input_format3(pd, 1, &score);FFStream *const sti = ffstream(st);if (fmt) {av_log(s, AV_LOG_DEBUG,"Probe with size=%d, packets=%d detected %s with score=%d\n",pd->buf_size, s->max_probe_packets - sti->probe_packets,fmt->name, score);for (int i = 0; fmt_id_type[i].name; i++) {if (!strcmp(fmt->name, fmt_id_type[i].name)) {if (fmt_id_type[i].type != AVMEDIA_TYPE_AUDIO &&st->codecpar->sample_rate)continue;if (sti->request_probe > score &&st->codecpar->codec_id != fmt_id_type[i].id)continue;st->codecpar->codec_id   = fmt_id_type[i].id;st->codecpar->codec_type = fmt_id_type[i].type;sti->need_context_update = 1;return score;}}}return 0;
}

可以看到set_codec_from_probe_data函数中调用了av_probe_input_format3函数来推测pd->buf中的码流的格式。关于av_probe_input_format3函数的用法可以参考:《FFmpeg源码:av_probe_input_format3函数和AVInputFormat结构体分析(FFmpeg源码5.0.3版本)》。对于H.264裸流,av_probe_input_format3函数中就是调用了h264_probe函数来检测这段码流是否为AnnexB格式的H.264裸流,具体可以参考:《音视频入门基础:H.264专题(16)——FFmpeg源码中,判断某文件是否为H.264裸流文件的实现》。

判断出这段码流为H.264裸流后,set_codec_from_probe_data函数中会执行语句:st->codecpar->codec_id   = fmt_id_type[i].id,让AVCodecParameters的codec_id得到视频压缩编码格式:

static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st,AVProbeData *pd)
{
//...if (fmt) {
//...for (int i = 0; fmt_id_type[i].name; i++) {if (!strcmp(fmt->name, fmt_id_type[i].name)) {if (fmt_id_type[i].type != AVMEDIA_TYPE_AUDIO &&st->codecpar->sample_rate)continue;if (sti->request_probe > score &&st->codecpar->codec_id != fmt_id_type[i].id)continue;st->codecpar->codec_id   = fmt_id_type[i].id;st->codecpar->codec_type = fmt_id_type[i].type;sti->need_context_update = 1;return score;}}}return 0;
}

然后在set_codec_from_probe_data函数外部,通过avcodec_parameters_to_context函数将AVCodecParameters的codec_id赋值给AVCodecContext的codec_id:

int avcodec_parameters_to_context(AVCodecContext *codec,const AVCodecParameters *par)
{
//...codec->codec_id   = par->codec_id;
//...
}

然后在dump_stream_format函数中,通过avcodec_string函数中的语句:codec_name = avcodec_get_name(enc->codec_id) 拿到AVCodecContext的codec_id对应的视频压缩编码格式名称。最后再在dump_stream_format函数中将视频压缩编码格式打印出来:

void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...codec_name = avcodec_get_name(enc->codec_id);
//...
}

所以FFmpeg获取PS文件/PS流的视频压缩编码格式,是从PES packet的有效载荷,即ES流数据中获取的:

三、视频压缩编码格式的profile

如果PS文件/PS流中的视频压缩编码格式为H.264,FFmpeg获取其视频压缩编码格式的profile,是通过SPS的profile_idc属性获取到的,具体可以参考:《音视频入门基础:H.264专题(17)——FFmpeg源码中,获取H.264视频的profile的实现》:

四、视频的色彩格式

如果PS文件/PS流中的视频压缩编码格式为H.264,FFmpeg获取其视频的色彩格式,是通过SPS中的属性chroma_format_idc获取到的,具体可以参考:《音视频入门基础:H.264专题(13)——FFmpeg源码中通过SPS属性获取视频色彩格式的实现》:

五、视频分辨率

如果PS文件/PS流中的视频压缩编码格式为H.264,FFmpeg获取其视频分辨率,是通过SPS中的属性获取的,具体可以参考:《音视频入门基础:H.264专题(12)——FFmpeg源码中通过SPS属性计算视频分辨率的实现》:

六、视频码率

由于PS文件/PS流的文件格式(包括TS Header、PES packet header)不包含视频码率信息,所以无法通过FFmpeg直接获取到其视频码率。与之对应,由于FLV文件的Script Tag中包含视频码率信息,所以FFmpeg可以直接打印FLV文件的视频码率,具体可以参考:《音视频入门基础:FLV专题(24)——FFmpeg源码中,获取FLV文件视频信息的实现》。

七、视频帧率

如果TS文件/TS流中的视频压缩编码格式为H.264,对其视频进行编解码时,FFmpeg源码内部使用的是通过SPS中的属性计算得到的视频帧率(具体可以参考:《音视频入门基础:H.264专题(15)——FFmpeg源码中通过SPS属性获取视频帧率的实现》)。

相关文章:

音视频入门基础:MPEG2-PS专题(6)——FFmpeg源码中,获取PS流的视频信息的实现

一、引言 通过FFmpeg命令可以获取到PS文件/PS流的视频压缩编码格式、色彩格式(像素格式)、分辨率、帧率信息: ./ffmpeg -i XXX.ps 本文以H.264为例讲述FFmpeg到底是从哪个地方获取到这些视频信息的。 二、视频压缩编码格式 (…...

深入解析HDFS:定义、架构、原理、应用场景及常用命令

引言 Hadoop分布式文件系统(HDFS,Hadoop Distributed File System)是Hadoop框架的核心组件之一,它提供了高可靠性、高可用性和高吞吐量的大规模数据存储和管理能力。本文将从HDFS的定义、架构、工作原理、应用场景以及常用命令等…...

Rust:运行调用 Lua 脚本

以下是一个Rust运行Lua脚本的简单例子: 首先,确保你的Rust项目中已经添加了rust-lua库的依赖。可以在Cargo.toml文件中添加以下内容: [dependencies] rust-lua "0.36" # 注意:版本号可能会更新,请根据实…...

PHP语言的数据库编程

PHP语言的数据库编程 引言 随着互联网的发展,各类网站和应用程序如雨后春笋般涌现,数据库作为它们数据存储和管理的核心,扮演着至关重要的角色。PHP作为一种流行的服务器端脚本语言,被广泛应用于Web开发。PHP不仅具有简单易学的…...

Formality:参数化设计的命名规则

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 在Formality中使用set_top命令设置一个容器的顶层设计(elaborate)时,一个参数化的设计(或者说模块)可能因为其参数覆盖而出现不同…...

xss-labs关卡记录8-14

第八关 还是常规方法&#xff0c;先上传我们常用的试试&#xff0c;onfocus <script> <a hrefjavascript:alert()> 查看源码发现&#xff0c;value这里应该是对我们的<>进行了 处理&#xff0c;然后在href这里&#xff0c;对常用的关键词进行了替换处理&…...

SPSS实现中介效应与调节效应

1. 中介效应 SPSS 实现 本例研究的自变量&#xff08;X&#xff09; “工作不被认同”&#xff1b;中介变量&#xff08;M&#xff09;为“焦虑”&#xff0c;因变量&#xff08;Y&#xff09;为“工作绩效”。探讨焦虑是否在工作不被认同与工作绩效间的作用。 &#xff08;2&…...

计算机的错误计算(二百零三)

摘要 利用两个大模型化简计算 其中一个大模型是数学解题器&#xff0c;它通过化简得出了正确结果&#xff1b;另外一个大模型给出了 Python代码。 例1. 化简计算摘要中算式。 下面是一个数学解题器大模型给的回答。 以上是数学解题器大模型给的回答。 下面是与另外一个大模型…...

【计算机网络】什么是AC和AP?

在现代的无线网络中&#xff0c;AC&#xff08;Access Controller&#xff0c;接入控制器&#xff09;和AP&#xff08;Access Point&#xff0c;无线接入点&#xff09;是两个至关重要的设备&#xff0c;它们在网络的管理、连接和优化中扮演着重要角色。理解它们的功能和区别&…...

python3中函数的参数

一. 简介 前面学习了Python3中函数的语法规则&#xff0c;文章如下&#xff1a; python3中函数的语法规则-CSDN博客 本文继续学习python中函数的参数。调用函数时可使用的正式参数类型&#xff1a; 必需参数&#xff0c;关键字参数&#xff0c;默认参数&#xff0c;不定长参…...

数据仓库建设方案和经验总结

在做数据集成的过程中&#xff0c;往往第二步的需求就是建设数仓由于数据分散在不同的存储环境或数据库中&#xff0c;对于新业务需求的开发需要人工先从不同的数据库中同步、集中、合并等处理&#xff0c;造成资源和人力的浪费。同时&#xff0c;目前的系统架构&#xff0c;无…...

Re77 读论文:LoRA: Low-Rank Adaptation of Large Language Models

诸神缄默不语-个人CSDN博文目录 诸神缄默不语的论文阅读笔记和分类 论文全名&#xff1a;LoRA: Low-Rank Adaptation of Large Language Models ArXiv网址&#xff1a;https://arxiv.org/abs/2106.09685 官方GitHub网站&#xff08;包含在RoBERTa、DeBERTa、GPT-2上用Lora微调…...

曲波系数 curvelet transform

Curvelet 变换后的系数涵义 曲波变换&#xff08;Curvelet Transform&#xff09;是一种多尺度、多方向的变换工具&#xff0c;能够有效地表示信号中的几何特征&#xff08;如边缘、曲线等&#xff09;。曲波变换后的系数具有明确的物理意义&#xff0c;反映了信号在不同尺度、…...

OS的随机数生成过程中的内核熵池

内核熵池&#xff08;Kernel Entropy Pool&#xff09;是操作系统内核中用于收集和管理熵&#xff08;随机性来源&#xff09;的机制 &#xff0c;在操作系统的随机数生成过程中发挥关键作用。 以下从其原理、作用、工作方式方面详细介绍&#xff1a; 原理&#xff1a;熵在信…...

数据结构:双向循环链表

双向循环链表&#xff08;Doubly Circular Linked List&#xff09; 双向循环链表是双向链表的一种变体&#xff0c;其特点是链表的头节点和尾节点相连&#xff0c;形成一个闭环。这种结构允许在链表中进行无缝的双向遍历&#xff0c;并且由于循环特性&#xff0c;可以从任何节…...

IP网和传输网区别(以访问百度为例!)

1. IP网和传输网的关系 IP网&#xff1a;是基于IP协议的网络&#xff0c;负责数据的逻辑传输&#xff0c;包括数据包的路由、寻址和转发。IP网是“虚拟”的&#xff0c;它依赖于底层的传输网来实际传递数据。 传输网&#xff1a;是物理网络基础设施&#xff0c;负责数据的物理…...

STM32裸机开发转FreeRTOS教程

目录 1. 简介2. RTOS设置&#xff08;1&#xff09;分配内存&#xff08;2&#xff09;查看任务剩余空间&#xff08;3&#xff09;使用osDelay 3. 队列的使用&#xff08;1&#xff09;创建队列&#xff08;1&#xff09;直接传值和指针传值&#xff08;2&#xff09;发送/接收…...

FreeSWITCH dialplan/default.xml 之释疑

准备花时间好好研究下&#xff0c;一直都是一知半解 sip_looped_call 通俗地说&#xff0c;就是自己呼叫自己 查文档&#xff0c;是这样讲的&#xff1a;如果调用已通过 ACL 以外的方式进行身份验证&#xff0c;并且当前请求 IP/port 与配置文件 IP/port 匹配&#xff0c;那…...

lambda用法及其原理

目录 lambda形式lambda用法1.sort降序2.swap3.捕捉列表 习题解题 lambda形式 [capture-list](parameters)->return type{function boby}[capture-list]&#xff1a;[捕捉列表]用于捕捉函数外的参数&#xff0c;可以为空&#xff0c;但不能省略&#xff1b;(parameters) &am…...

Go Ebiten随机迷宫生成示例

引言 迷宫生成是计算机科学中一个经典的问题&#xff0c;常用于算法教学和游戏开发。本文将介绍如何使用 Go 语言和 Ebiten 游戏引擎实现一个基于深度优先搜索&#xff08;DFS&#xff09;的随机迷宫生成算法&#xff0c;并通过可视化的方式展示迷宫的生成过程。 技术栈 Go …...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!

目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟

2025年4月29日&#xff0c;在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上&#xff0c;可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞&#xff0c;强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...

【java面试】微服务篇

【java面试】微服务篇 一、总体框架二、Springcloud&#xff08;一&#xff09;Springcloud五大组件&#xff08;二&#xff09;服务注册和发现1、Eureka2、Nacos &#xff08;三&#xff09;负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...