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

音视频入门基础:H.264专题(15)——FFmpeg源码中通过SPS属性获取视频帧率的实现

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

音视频入门基础:H.264专题系列文章:

音视频入门基础:H.264专题(1)——H.264官方文档下载

音视频入门基础:H.264专题(2)——使用FFmpeg命令生成H.264裸流文件

音视频入门基础:H.264专题(3)——EBSP, RBSP和SODB

音视频入门基础:H.264专题(4)——NALU Header:forbidden_zero_bit、nal_ref_idc、nal_unit_type简介

音视频入门基础:H.264专题(5)——FFmpeg源码中 解析NALU Header的函数分析

音视频入门基础:H.264专题(6)——FFmpeg源码:从H.264码流中提取NALU Header、EBSP、RBSP和SODB

音视频入门基础:H.264专题(7)——FFmpeg源码中 指数哥伦布编码的解码实现

音视频入门基础:H.264专题(8)——H.264官方文档的描述符

音视频入门基础:H.264专题(9)——SPS简介

音视频入门基础:H.264专题(10)——FFmpeg源码中,存放SPS属性的结构体和解码SPS的函数分析

音视频入门基础:H.264专题(11)——计算视频分辨率的公式

音视频入门基础:H.264专题(12)——FFmpeg源码中通过SPS属性计算视频分辨率的实现

音视频入门基础:H.264专题(13)——FFmpeg源码中通过SPS属性获取视频色彩格式的实现

音视频入门基础:H.264专题(14)——计算视频帧率的公式

音视频入门基础:H.264专题(15)——FFmpeg源码中通过SPS属性获取视频帧率的实现

音视频入门基础:H.264专题(16)——FFmpeg源码中,判断某文件是否为H.264裸流文件的实现

音视频入门基础:H.264专题(17)——FFmpeg源码获取H.264裸流文件信息(视频压缩编码格式、色彩格式、视频分辨率、帧率)的总流程

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

一、引言

在上一节《音视频入门基础:H.264专题(14)——计算视频帧率的公式》中,讲述了通过SPS中的属性计算H.264编码的视频的帧率的公式。本文讲解FFmpeg源码中计算视频帧率的实现。

二、FFmpeg源码中计算视频帧率的实现

从文章《音视频入门基础:H.264专题(10)——FFmpeg源码中,存放SPS属性的结构体和解码SPS的函数分析》中,我们可以知道,FFmpeg源码中通过ff_h264_decode_seq_parameter_set函数解码SPS,从而拿到SPS中的属性。

计算视频帧率所需的属性在SPS的VUI parameters(视频可用参数)中。ff_h264_decode_seq_parameter_set函数通过调用decode_vui_parameters函数解码VUI parameters:

int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,H264ParamSets *ps, int ignore_truncation)
{
//...sps->vui_parameters_present_flag = get_bits1(gb);if (sps->vui_parameters_present_flag) {int ret = decode_vui_parameters(gb, avctx, sps);if (ret < 0)goto fail;}//...
}

decode_vui_parameters函数中通过下面的这部分代码拿到计算视频帧率所需的属性(timing_info_present_flag、num_units_in_tick、time_scale):

static inline int decode_vui_parameters(GetBitContext *gb, void *logctx,SPS *sps)
{
//...sps->timing_info_present_flag = get_bits1(gb);if (sps->timing_info_present_flag) {unsigned num_units_in_tick = get_bits_long(gb, 32);unsigned time_scale        = get_bits_long(gb, 32);if (!num_units_in_tick || !time_scale) {av_log(logctx, AV_LOG_ERROR,"time_scale/num_units_in_tick invalid or unsupported (%u/%u)\n",time_scale, num_units_in_tick);sps->timing_info_present_flag = 0;} else {sps->num_units_in_tick = num_units_in_tick;sps->time_scale = time_scale;}sps->fixed_frame_rate_flag = get_bits1(gb);}//...
}

然后在FFmpeg源码的源文件libavcodec/h264_parser.c的parse_nal_units函数中,通过如下代码,得到视频帧率:

static inline int parse_nal_units(AVCodecParserContext *s,AVCodecContext *avctx,const uint8_t * const buf, int buf_size)
{//...for (;;) {switch (nal.type) {case H264_NAL_SPS:ff_h264_decode_seq_parameter_set(&nal.gb, avctx, &p->ps, 0);break;//...case H264_NAL_IDR_SLICE://...if (sps->timing_info_present_flag) {int64_t den = sps->time_scale;if (p->sei.unregistered.x264_build < 44U)den *= 2;av_reduce(&avctx->framerate.den, &avctx->framerate.num,sps->num_units_in_tick * avctx->ticks_per_frame, den, 1 << 30);}//... }//...}
}

可以看到在FFmpeg源码的parse_nal_units函数中,最终是通过语句

av_reduce(&avctx->framerate.den, &avctx->framerate.num,sps->num_units_in_tick * avctx->ticks_per_frame, den, 1 << 30);

计算出视频帧率的。

上述函数av_reduce的实参avctx->ticks_per_frame是结构体AVCodecContext的成员变量,它会被设置为每帧的时基的时钟数。默认值为1,如果编解码器是H.264或MPEG-2,会被设置为2:

typedef struct AVCodecContext {/*** For some codecs, the time base is closer to the field rate than the frame rate.* Most notably, H.264 and MPEG-2 specify time_base as half of frame duration* if no telecine is used ...** Set to time_base ticks per frame. Default 1, e.g., H.264/MPEG-2 set it to 2.*/int ticks_per_frame;
}

用户需要获取H.264编码的视频的媒体信息时,会调用avformat_find_stream_info函数,而该函数内部会调用h264_decode_init函数,让avctx->ticks_per_frame被初始化为2(也就是说对于H.264,avctx->ticks_per_frame的值就是2):

static av_cold int h264_decode_init(AVCodecContext *avctx)
{
//...if (avctx->ticks_per_frame == 1) {if(h->avctx->time_base.den < INT_MAX/2) {h->avctx->time_base.den *= 2;} elseh->avctx->time_base.num /= 2;}avctx->ticks_per_frame = 2;
//...
}

所以在parse_nal_units函数中,语句:

av_reduce(&avctx->framerate.den, &avctx->framerate.num,sps->num_units_in_tick * avctx->ticks_per_frame, den, 1 << 30);

等价于:

av_reduce(&avctx->framerate.den, &avctx->framerate.num,sps->num_units_in_tick * 2, den, 1 << 30);

而den的值为sps->time_scale。所以上述语句等价于:

av_reduce(&avctx->framerate.den, &avctx->framerate.num,sps->num_units_in_tick * 2, sps->time_scale, 1 << 30);

av_reduce函数是用来计算视频帧率的,其源码定义在FFmpeg源码libavutil/rational.c中:

int av_reduce(int *dst_num, int *dst_den,int64_t num, int64_t den, int64_t max)
{AVRational a0 = { 0, 1 }, a1 = { 1, 0 };int sign = (num < 0) ^ (den < 0);int64_t gcd = av_gcd(FFABS(num), FFABS(den));if (gcd) {num = FFABS(num) / gcd;den = FFABS(den) / gcd;}if (num <= max && den <= max) {a1 = (AVRational) { num, den };den = 0;}while (den) {uint64_t x        = num / den;int64_t next_den  = num - den * x;int64_t a2n       = x * a1.num + a0.num;int64_t a2d       = x * a1.den + a0.den;if (a2n > max || a2d > max) {if (a1.num) x =          (max - a0.num) / a1.num;if (a1.den) x = FFMIN(x, (max - a0.den) / a1.den);if (den * (2 * x * a1.den + a0.den) > num * a1.den)a1 = (AVRational) { x * a1.num + a0.num, x * a1.den + a0.den };break;}a0  = a1;a1  = (AVRational) { a2n, a2d };num = den;den = next_den;}av_assert2(av_gcd(a1.num, a1.den) <= 1U);av_assert2(a1.num <= max && a1.den <= max);*dst_num = sign ? -a1.num : a1.num;*dst_den = a1.den;return den == 0;
}

所以语句:

av_reduce(&avctx->framerate.den, &avctx->framerate.num,sps->num_units_in_tick * 2, sps->time_scale, 1 << 30);

相当于执行了公式:视频帧率 = time_scale / (2 * num_units_in_tick)。然后把得到的视频帧率的分子和分母分别存放到avctx->framerate.den和avctx->framerate.num中返回。

相关文章:

音视频入门基础:H.264专题(15)——FFmpeg源码中通过SPS属性获取视频帧率的实现

音视频入门基础&#xff1a;H.264专题系列文章&#xff1a; 音视频入门基础&#xff1a;H.264专题&#xff08;1&#xff09;——H.264官方文档下载 音视频入门基础&#xff1a;H.264专题&#xff08;2&#xff09;——使用FFmpeg命令生成H.264裸流文件 音视频入门基础&…...

【C++高阶】哈希之美:探索位图与布隆过滤器的应用之旅

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;模拟实现unordered 的奥秘 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀哈希应用 &#x1f4…...

文件包涵条件竞争(ctfshow82)

Web82 利用 session.upload_progress 包含文件漏洞 <!DOCTYPE html> <html> <body> <form action"https://09558c1b-9569-4abd-bf78-86c4a6cb6608.challenge.ctf.show//" method"POST" enctype"multipart/form-data"> …...

通信原理-思科实验三:无线局域网实验

实验三 无线局域网实验 一&#xff1a;无线局域网基础服务集 实验步骤&#xff1a; 进入物理工作区&#xff0c;导航选择 城市家园; 选择设备 AP0&#xff0c;并分别选择Laptop0、Laptop1放在APO范围外区域 修改笔记本的网卡&#xff0c;从以太网卡切换到无线网卡WPC300N 切…...

*算法训练(leetcode)第三十一天 | 1049. 最后一块石头的重量 II、494. 目标和、474. 一和零

刷题记录 *1049. 最后一块石头的重量 II*494. 目标和474. 一和零 *1049. 最后一块石头的重量 II leetcode题目地址 本题与分割等和子集类似&#xff0c;要达到碰撞最后的石头重量最小&#xff0c;则尽可能把石头等分为两堆。 时间复杂度&#xff1a; O ( m ∗ n ) O(m * n)…...

mac中如何使用obs推流以及使用vlc播放

使用obs推流 1.打开obs&#xff0c;在“来源”框中->点加号->选择媒体源->选择本地ts文件 2.obs中->点击右下角设置->点直播->服务选自定义->服务器填写你的srt服务url&#xff0c;比如&#xff1a;srt://192.168.13.211:14000?modecaller 注意&#xff…...

shopee虾皮 java后端 一面面经 整体感觉不难

面试总结&#xff1a;总体不难&#xff0c;算法题脑抽了只过了一半&#xff0c;面试官点出了问题说时间到了&#xff0c;反问一点点&#xff0c;感觉五五开&#xff0c;许愿一个二面 1.Java中的锁机制&#xff0c;什么是可重入锁 Java中的机制主要包括 synchronized关键字 Loc…...

HydraRPC: RPC in the CXL Era——论文阅读

ATC 2024 Paper CXL论文阅读笔记整理 问题 远程过程调用&#xff08;RPC&#xff09;是分布式系统中的一项基本技术&#xff0c;它允许函数在远程服务器上通过本地调用执行来促进网络通信&#xff0c;隐藏底层通信过程的复杂性简化了客户端/服务器交互[15]。RPC已成为数据中心…...

pve笔记

配置显卡直通参考 https://blog.csdn.net/m0_59148723/article/details/130923893 https://foxi.buduanwang.vip/virtualization/pve/561.html/ https://www.cnblogs.com/MAENESA/p/18005241 https://www.wangsansan.com/archives/181/ pve配置显卡直通到虚拟机后&#xff0c;…...

typecho仿某度响应式主题Xaink

新闻类型博客主题&#xff0c;简洁好看&#xff0c;适合资讯类、快讯类、新闻类博客建站&#xff0c;响应式设计&#xff0c;支持明亮和黑暗模式 直接下载 zip 源码->解压后移动到 Typecho 主题目录->改名为xaink->启用。 演示图&#xff1a; 下载链接&#xff1a; t…...

springcloud RocketMQ 客户端是怎么走到消费业务逻辑的 - debug step by step

springcloud RocketMQ &#xff0c;一个mq消息发送后&#xff0c;客户端是怎么一步步拿到消息去消费的&#xff1f;我们要从代码层面探究这个问题。 找的流程图&#xff0c;有待考究。 以下我们开始debug&#xff1a; 拉取数据的线程&#xff1a; PullMessageService.java 本…...

GPT-4o mini小型模型具备卓越的文本智能和多模态推理能力

GPT-4o mini 是首个应用OpenAI 指令层次结构方法的模型&#xff0c;这有助于增强模型抵抗越狱、提示注入和系统提示提取的能力。这使得模型的响应更加可靠&#xff0c;并有助于在大规模应用中更安全地使用。 GPT-4o mini 在学术基准测试中&#xff0c;无论是在文本智能还是多模…...

Milvus 向量数据库进阶系列丨部署形态选型

本系列文章介绍 在和社区小伙伴们交流的过程中&#xff0c;我们发现大家最关心的问题从来不是某个具体的功能如何使用&#xff0c;而是面对一个具体的实战场景时&#xff0c;如何选择合适的向量数据库解决方案或最优的功能组合。在 “Milvus 向量数据库进阶” 这个系列文章中&…...

【React】详解受控表单绑定

文章目录 一、受控组件的基本概念1. 什么是受控组件&#xff1f;2. 受控组件的优势3. 基本示例导入和初始化定义函数组件处理输入变化处理表单提交渲染表单导出组件 二、受控组件的进阶用法1. 多个输入框的处理使用多个状态变量使用一个对象管理状态 2. 处理选择框&#xff08;…...

使用puma部署ruby on rails的记录

之前写过一篇《记录一下我的Ruby On Rails的systemd服务脚本》的记录&#xff0c;现在补上一个比较政治正确的Ruby On Rails的生产环境部署记录。使用Puma部署项目。 创建文件 /usr/lib/systemd/system/puma.service [Unit] DescriptionPuma HTTP Server DocumentationRuby O…...

如何在Linux上使用Ansible自动化部署

Ansible是一个开源的自动化工具&#xff0c;可以帮助开发人员和系统管理员对大规模的服务器进行自动化部署和管理。它使用SSH协议来在远程服务器上执行任务&#xff0c;并通过模块化的方式提供了丰富的功能&#xff0c;可以轻松地管理服务器配置、软件部署和应用程序运行。 在…...

scrapy爬取城市天气数据

scrapy爬取城市天气数据 一、创建scrapy项目二、修改settings,设置UA,开启管道三、编写爬虫文件四、编写items.py五、在weather.py中导入WeatherSpiderItem类六、管道中存入数据,保存至csv文件七、完整代码一、创建scrapy项目 先来看一下爬取的字段情况: 本次爬取城市天…...

一天搞定React(5)——ReactRouter(下)【已完结】

Hello&#xff01;大家好&#xff0c;今天带来的是React前端JS库的学习&#xff0c;课程来自黑马的往期课程&#xff0c;具体连接地址我也没有找到&#xff0c;大家可以广搜巡查一下&#xff0c;但是总体来说&#xff0c;这套课程教学质量非常高&#xff0c;每个知识点都有一个…...

微信小程序之计算器

在日常生活中&#xff0c;计算器是人们广泛使用的工具&#xff0c;可以帮助我们快速且方便地计算金额、成本、利润等。下面将会讲解如何开发一个“计算器”微信小程序。 一、开发思路 1、界面和功能 “计算器”微信小程序的页面效果如图所示 在计算器中可以进行整数和小数的…...

【logstash】logstash使用多个子配置文件

这里有个误区在pipelines.yml中写conf.d/*&#xff0c;实测会有问题&#xff0c;不同的filter处理逻辑会复用。 现在有两个从kafka采集日志的配置文件&#xff1a;from_kafka1.conf&#xff0c;from_kafka2.conf 修改pipelines.yml配置文件 config/pipelines.yml- pipeline.i…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

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…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

Kafka主题运维全指南:从基础配置到故障处理

#作者&#xff1a;张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1&#xff1a;主题删除失败。常见错误2&#xff1a;__consumer_offsets占用太多的磁盘。 主题日常管理 …...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...