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

=================================================================

音视频入门基础:MPEG2-PS专题系列文章:

音视频入门基础:MPEG2-PS专题(1)——MPEG2-PS官方文档下载

音视频入门基础:MPEG2-PS专题(2)——使用FFmpeg命令生成ps文件

音视频入门基础:MPEG2-PS专题(3)——MPEG2-PS格式简介

音视频入门基础:MPEG2-PS专题(4)——FFmpeg源码中,判断某文件是否为PS文件的实现

音视频入门基础:MPEG2-PS专题(5)——FFmpeg源码中,解析PS流中的PES流的实现

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

音视频入门基础:MPEG2-PS专题(7)——通过FFprobe显示PS流每个packet的信息

=================================================================

一、引言

通过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属性获取视频帧率的实现》)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/505361.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营day27

代码随想录算法训练营 —day27 文章目录 代码随想录算法训练营前言一、贪心算法理论基础二、455.分发饼干三、376. 摆动序列53. 最大子数组和总结 前言 今天是算法营的第27天,希望自己能够坚持下来! 今日任务: ● 贪心算法理论基础 ● 455.…

idea全局替换显示不全(ctrl+shift+R)

修改一下idea的配置就行 idea的默认显示条数为100,可以修改成10000

新版2024AndroidStudio项目目录结构拆分

如题 下载了最新版的android studio 发现目录结构和以前不一样 自动帮你合并了 如何层层抽丝剥茧呢 按照一下步骤即可解决问题!

vue el-table 数据变化后,高度渲染问题

场景:el-table设置了height属性,但是切换查询条件后再次点击查询重新获取data时,el-table渲染的高度会有问题,滚动区域变矮了。 解决办法:使用doLayout方法‌,在表格数据渲染后调用doLayout方法可以重新布局…

一.MySQL程序简介

整体介绍 1.服务端mysqld(可执行文件) mysqld --verbose --help 2.客户端mysql(可执行文件) 3.其它工具包程序

Jenkins-持续集成、交付、构建、部署、测试

Jenkins-持续集成、交付、构建、部署、测试 一: Jenkins 介绍1> Jenkins 概念2> Jenkins 目的3> Jenkins 特性4> Jenkins 作用 二:Jenkins 版本三:DevOps流程简述1> 持续集成(Continuous Integration,CI&#xff0…

用 Python 绘制可爱的招财猫

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​​​​​ ​​​​​​​​​ ​​​​ 招财猫,也被称为“幸运猫”,是一种象征财富和好运的吉祥物,经常…

socket网络编程-TC/IP方式

网络编程 1.概念:两台设备之间通过网络数据传输。 2.网络通信:将数据通过网络从一台设备传输另外一台设备。 3.java.net包下提供了一系列的类和接口,提供程序员使用,完成网络通信。 TCP和UDP TCP协议: 1.使用TCP协…

G1原理—2.G1是如何提升分配对象效率

大纲 1.G1的对象分配原理是怎样的 2.深入分析TLAB机制原理 3.借助TLAB分配对象的实现原理是什么 4.什么是快速分配 什么是慢速分配 5.大对象分配的过程 与TLAB的关系 6.救命的稻草—JVM的最终分配尝试 G1如何分配对象TLAB机制分区协调机制 G1设计了一套TLAB机制快速分…

Wsl报 不存在具有提供的名称的分布

在使用wsl --set-version Ubuntu 2命令升级WSL2时, 不存在具有提供的名称的分布,如下图。 图片 解决的办法也很简单,只需要把该命令的 Ubuntu 改为具体使用的发行版全称即可。此处,使用“wsl -l -v”命令显示安装的Ubuntu 版本。 …

视频编辑最新SOTA!港中文Adobe等发布统一视频生成传播框架——GenProp

文章链接:https://arxiv.org/pdf/2412.19761 项目链接:https://genprop.github.io 亮点直击 定义了一个新的生成视频传播问题,目标是利用 I2V 模型的生成能力,将视频第一帧的各种变化传播到整个视频中。 精心设计了模型 GenProp&…

计算机网络例题

IP地址分类: A类:网络号范围: 1~126 (0000 0001 ~ 0111 1110) B类:网络号范围:128.1 ~ 191.255 (可用范围) C类:网络号段范围:192.0.1 ~ 223.2…

【大数据基础】大数据概述

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈大数据技术原理与应用 ⌋ ⌋ ⌋专栏系统介绍大数据的相关知识,分为大数据基础篇、大数据存储与管理篇、大数据处理与分析篇、大数据应用篇。内容包含大数据概述、大数据处理架构Hadoop、分布式文件系统HDFS、分布式数…

鸿蒙UI(ArkUI-方舟UI框架)

参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/arkts-layout-development-overview-V13 ArkUI简介 ArkUI(方舟UI框架)为应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能&#xff…

【Java项目】基于SpringBoot的【校园交友系统】

【Java项目】基于SpringBoot的【校园交友系统】 技术简介:系统软件架构选择B/S模式、SpringBoot框架、java技术和MySQL数据库等,总体功能模块运用自顶向下的分层思想。 系统简介:系统主要包括管理员和用户。 (a) 管理员的功能主要有首页、个人…

C#中的常用集合

目录 一、动态数组ArrayList 二、List 三、栈(Stack) 四、队列(Queue) 五、字典(Dictionary),int> 一、动态数组ArrayList ArrayList 是 C# 中提供的一种动态数组类,位于命名空间 Syste…

[笔记] 使用 Jenkins 实现 CI/CD :从 GitLab 拉取 Java 项目并部署至 Windows Server

随着软件开发节奏的加快,持续集成(CI)和持续部署(CD)已经成为确保软件质量和加速产品发布的不可或缺的部分。Jenkins作为一款广泛使用的开源自动化服务器,为开发者提供了一个强大的平台来实施这些实践。然而…

WEB前端-2

目录 HTML-常见的网页标签-分类2 语义化标签 列表标签 表单标签 form标签 input标签 select标签 textarea标签 html转义符 h5提供的新标签 【例3:豆瓣电影】 【源码】 【例4:登录注册】 【源码】 【例5:QQ注册】 【源码】 H…

Nacos概述与集群实战

什么是Nacos,它有什么作用 Nacos 是一个开源的服务发现和配置管理平台,由阿里巴巴开发,用于构建云原生应用程序。Nacos 的名字源自于 “Naming and Configuration Service”(命名和配置服务)的缩写。它支持服务注册与发现、动态配置服务、动态服务管理等特性,广泛应用于微…

EtherCAT转Modbus网关与TwinCAT3的连接及配置详述

在工业自动化控制系统中,常常需要整合不同的通信协议设备。本案例旨在展示如何利用捷米特JM-ECT-RTU协议转换网关模块,实现 EtherCAT 网络与 Modbus 设备之间的无缝连接,并在 TwinCAT3 环境中进行有效配置,以构建一个稳定可靠的自…