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

FFmpeg源码:ff_h2645_extract_rbsp函数分析

一、ff_h2645_extract_rbsp函数的声明

ff_h2645_extract_rbsp函数的声明放在FFmpeg源码(本文演示用的FFmpeg源码版本为5.0.3,该ffmpeg在CentOS 7.5上通过10.2.1版本的gcc编译)的头文件libavcodec/h2645_parse.h中。

/*** Extract the raw (unescaped) bitstream.*/
int ff_h2645_extract_rbsp(const uint8_t *src, int length, H2645RBSP *rbsp,H2645NAL *nal, int small_padding);

该函数在H.264/H.265的解码时被调用。作用是将去掉第一个startcode的H.264/H.265码流(以下全部以H.264码流为例) 中的第一个NALU 提取出来,分别去掉和保留防竞争字节,存贮到形参nal 指向的缓冲区中。关于 NALU和防竞争字节的概念可以参考:音视频入门基础:H.264专题(3)——EBSP, RBSP和SODB

形参src:输入型参数。指向缓冲区的指针,该缓冲区存放 去掉第一个startcode(起始码)后的H.264码流。

形参length:输入型参数。指针src指向的缓冲区的长度,单位为字节。

形参rbsp为H2645RBSP类型,为输出型参数。

结构体H2645RBSP 定义如下:

typedef struct H2645RBSP {uint8_t *rbsp_buffer;AVBufferRef *rbsp_buffer_ref;int rbsp_buffer_alloc_size;int rbsp_buffer_size;
} H2645RBSP;

执行ff_h2645_extract_rbsp函数后,

rbsp->rbsp_buffer 变为:去掉startcode和防竞争字节后的H.264码流,可能包含多个NALU。

rbsp->rbsp_buffer_size 变为:rbsp->rbsp_buffer的大小,单位为字节。

形参nal为H2645NAL类型,为输出型参数。

结构体H2645NAL定义如下:

typedef struct H2645NAL {const uint8_t *data;int size;/*** Size, in bits, of just the data, excluding the stop bit and any trailing* padding. I.e. what HEVC calls SODB.*/int size_bits;int raw_size;const uint8_t *raw_data;GetBitContext gb;/*** NAL unit type*/int type;/*** H.264 only, nal_ref_idc*/int ref_idc;/*** HEVC only, nuh_temporal_id_plus_1 - 1*/int temporal_id;/** HEVC only, identifier of layer to which nal unit belongs*/int nuh_layer_id;int skipped_bytes;int skipped_bytes_pos_size;int *skipped_bytes_pos;
} H2645NAL;

执行ff_h2645_extract_rbsp函数后,

nal->data变为:指向缓冲区的指针。该缓冲区存放 “指针src指向的缓冲区中的第一个NALU”,该NALU去掉了startcode和防竞争字节,但保留了NALU Header。(可以理解为NALU Header + RBSP)

nal->size变为:nal->data指向的缓冲区的大小,单位为字节。

nal->raw_data变为:指向缓冲区的指针。该缓冲区存放 “指针src指向的缓冲区中的第一个NALU”,该NALU去掉了startcode,但保留了防竞争字节和NALU Header。(可以理解为NALU Header + EBSP)

nal->raw_size变为:nal->raw_data指向的缓冲区的大小,单位为字节。

形参small_padding:输入型参数。值一般等于1,可以忽略。

返回值:整形。值等同于nal->raw_size,为nal->raw_data指向的缓冲区的大小,单位为字节

二、ff_h2645_extract_rbsp函数的定义

ff_h2645_extract_rbsp函数定义在libavcodec/h2645_parse.c中:

int ff_h2645_extract_rbsp(const uint8_t *src, int length,H2645RBSP *rbsp, H2645NAL *nal, int small_padding)
{int i, si, di;uint8_t *dst;nal->skipped_bytes = 0;
#define STARTCODE_TEST                                                  \if (i + 2 < length && src[i + 1] == 0 && src[i + 2] <= 3) {     \if (src[i + 2] != 3 && src[i + 2] != 0) {                   \/* startcode, so we must be past the end */             \length = i;                                             \}                                                           \break;                                                      \}
#if HAVE_FAST_UNALIGNED
#define FIND_FIRST_ZERO                                                 \if (i > 0 && !src[i])                                           \i--;                                                        \while (src[i])                                                  \i++
#if HAVE_FAST_64BITfor (i = 0; i + 1 < length; i += 9) {if (!((~AV_RN64(src + i) &(AV_RN64(src + i) - 0x0100010001000101ULL)) &0x8000800080008080ULL))continue;FIND_FIRST_ZERO;STARTCODE_TEST;i -= 7;}
#elsefor (i = 0; i + 1 < length; i += 5) {if (!((~AV_RN32(src + i) &(AV_RN32(src + i) - 0x01000101U)) &0x80008080U))continue;FIND_FIRST_ZERO;STARTCODE_TEST;i -= 3;}
#endif /* HAVE_FAST_64BIT */
#elsefor (i = 0; i + 1 < length; i += 2) {if (src[i])continue;if (i > 0 && src[i - 1] == 0)i--;STARTCODE_TEST;}
#endif /* HAVE_FAST_UNALIGNED */if (i >= length - 1 && small_padding) { // no escaped 0nal->data     =nal->raw_data = src;nal->size     =nal->raw_size = length;return length;} else if (i > length)i = length;dst = &rbsp->rbsp_buffer[rbsp->rbsp_buffer_size];memcpy(dst, src, i);si = di = i;while (si + 2 < length) {// remove escapes (very rare 1:2^22)if (src[si + 2] > 3) {dst[di++] = src[si++];dst[di++] = src[si++];} else if (src[si] == 0 && src[si + 1] == 0 && src[si + 2] != 0) {if (src[si + 2] == 3) { // escapedst[di++] = 0;dst[di++] = 0;si       += 3;if (nal->skipped_bytes_pos) {nal->skipped_bytes++;if (nal->skipped_bytes_pos_size < nal->skipped_bytes) {nal->skipped_bytes_pos_size *= 2;av_assert0(nal->skipped_bytes_pos_size >= nal->skipped_bytes);av_reallocp_array(&nal->skipped_bytes_pos,nal->skipped_bytes_pos_size,sizeof(*nal->skipped_bytes_pos));if (!nal->skipped_bytes_pos) {nal->skipped_bytes_pos_size = 0;return AVERROR(ENOMEM);}}if (nal->skipped_bytes_pos)nal->skipped_bytes_pos[nal->skipped_bytes-1] = di - 1;}continue;} else // next start codegoto nsc;}dst[di++] = src[si++];}while (si < length)dst[di++] = src[si++];nsc:memset(dst + di, 0, AV_INPUT_BUFFER_PADDING_SIZE);nal->data = dst;nal->size = di;nal->raw_data = src;nal->raw_size = si;rbsp->rbsp_buffer_size += si;return si;
}

三、ff_h2645_extract_rbsp函数的内部实现原理分析

ff_h2645_extract_rbsp函数中存在如下代码:

int ff_h2645_extract_rbsp(const uint8_t *src, int length,H2645RBSP *rbsp, H2645NAL *nal, int small_padding)
{//...#define STARTCODE_TEST                                                  \if (i + 2 < length && src[i + 1] == 0 && src[i + 2] <= 3) {     \if (src[i + 2] != 3 && src[i + 2] != 0) {                   \/* startcode, so we must be past the end */             \length = i;                                             \}                                                           \break;                                                      \}//...for (i = 0; i + 1 < length; i += 2) {if (src[i])continue;if (i > 0 && src[i - 1] == 0)i--;STARTCODE_TEST;}//...
}

其中STARTCODE_TEST是宏定义。将宏展开,上述代码相当于:

int ff_h2645_extract_rbsp(const uint8_t *src, int length,H2645RBSP *rbsp, H2645NAL *nal, int small_padding)
{//...for (i = 0; i + 1 < length; i += 2) {if (src[i])continue;if (i > 0 && src[i - 1] == 0)i--;if (i + 2 < length && src[i + 1] == 0 && src[i + 2] <= 3) {     if (src[i + 2] != 3 && src[i + 2] != 0) {                   /* startcode, so we must be past the end */             length = i;                                             }                                                           break;                                                      }}//...
}

上述代码中,首先会通过语句:

    for (i = 0; i + 1 < length; i += 2) {if (src[i])continue;if (i > 0 && src[i - 1] == 0)i--;//...}

来判断H.264码流中是否存在ASCII 码为 0 (值为'\0')的的字符,如果存在则表明接下来的数据中可能会出现startcode(起始码)或防竞争字节。然后执行下面代码

if (i + 2 < length && src[i + 1] == 0 && src[i + 2] <= 3) {     if (src[i + 2] != 3 && src[i + 2] != 0) {                   /* startcode, so we must be past the end */             length = i;                                             }                                                           break;                                                      
}

来判断是否是起始码,如果是起始码或防竞争字节就通过break;跳出循环。

继续执行语句。满足下面条件,说明是防竞争字节:

else if (src[si] == 0 && src[si + 1] == 0 && src[si + 2] != 0) {if (src[si + 2] == 3) { // escape//...
}

如果是防竞争字节,通过下面语句去掉防竞争字节:

dst[di++] = 0;dst[di++] = 0;si       += 3;
//...

如果不满足条件if (src[si + 2] == 3),说明遇到下一个起始码,表示这个NALU结束了。执行else语句,跳转到“nsc”:

if (src[si + 2] == 3) { // escape
//...
}
else // next start codegoto nsc;
//...

跳转到“nsc”后,给输出型参数赋值,并返回。

nsc:memset(dst + di, 0, AV_INPUT_BUFFER_PADDING_SIZE);nal->data = dst;nal->size = di;nal->raw_data = src;nal->raw_size = si;rbsp->rbsp_buffer_size += si;return si;

四、通过修改ff_h2645_extract_rbsp函数降低FFmpeg转码时的cpu使用率

由于ff_h2645_extract_rbsp函数在H.264/H.265的解码时被调用。所以理论上修改该函数(使用算法优化,用空间换时间等策略)可以降低FFmpeg转码时的cpu使用率。具体可以参考:Imagine Computing创新技术大赛赛道2参赛攻略 - 007gzs

相关文章:

FFmpeg源码:ff_h2645_extract_rbsp函数分析

一、ff_h2645_extract_rbsp函数的声明 ff_h2645_extract_rbsp函数的声明放在FFmpeg源码&#xff08;本文演示用的FFmpeg源码版本为5.0.3&#xff0c;该ffmpeg在CentOS 7.5上通过10.2.1版本的gcc编译&#xff09;的头文件libavcodec/h2645_parse.h中。 /*** Extract the raw (u…...

关于 AD21导入电子元器件放置“3D体”STEP模型失去3D纹理贴图 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/139969415 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…...

【JAVA】利用Redisson和Spring实现高效物联温度控制链路,确保温度调节的准确性和效率,定时链路执行使用案例,一环扣一环

主要功能和场景 柔性调温策略&#xff1a;这个类主要用于管理一个温度调节流程&#xff0c;通过不同的策略&#xff08;如策略1和策略2&#xff09;来调节温度&#xff0c;确保设备或环境中的温度达到预设的目标。 紧急停止机制&#xff1a;在流程执行过程中&#xff0c;如果需…...

yolov8部署资料

1.labelImg安装&#xff1a; labelImg的安装过程可以参照以下步骤进行&#xff0c;这里以Windows操作系统为例&#xff1a; 1. 检查Python环境 首先&#xff0c;需要确认你的电脑上是否已经安装了Python。你可以通过Win R打开windows“运行”对话框&#xff0c;输入cmd&#x…...

迅为RK3588开发板支持LVDS信号,标准 HDMI信号,IMIPI信号

性能强--iTOP-3588开发板采用瑞芯微RK3588处理器&#xff0c;是全新一代ALoT高端应用芯片&#xff0c;采用8nm LP制程&#xff0c;搭载八核64位CPU&#xff0c;四核Cortex-A76和四核Cortex-A55架构&#xff0c;主频高达2.4GHZ&#xff0c;8GB内存&#xff0c;32GB EMMC。 四核心…...

页面开发感想

页面开发 1、 前端预览 2、一些思路 2.1、首页自定义element-plus的走马灯 :deep(.el-carousel__arrow){border-radius: 0%;height: 10vh; }需要使用:deep(标签)才能修改样式 或者 ::v-deep 标签 2.2、整体设计思路 <template><div class"card" style&…...

TikTok达人合作ROI分析:品牌如何评估带货效果

在当今的数字营销时代&#xff0c;TikTok已经成为品牌推广和消费者互动的重要平台。通过与TikTok达人的合作&#xff0c;品牌可以有效地提升其市场影响力和销售额。其中&#xff0c;评估这些合作的投入产出比&#xff08;ROI&#xff09;对于品牌来说是至关重要的。本文Nox聚星…...

硬件实用技巧:电容精度和常用容值表

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140009042 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…...

Java面试题:内存管理、类加载机制、对象生命周期及性能优化

1. 说一下 JVM 的主要组成部分及其作用? JVM包含两个子系统和两个组件:Class loader(类装载)、Execution engine(执行引擎)、Runtime data area(运行时数据区)、Native Interface(本地接口)。 Class loader(类装载):根据给定的全限定名类名(如:java.lang.Object)装载class文…...

什么是 Payment Request API?

Payment Request API 是一个 Web API&#xff0c;允许网页和 Web 应用程序向用户展示一个标准化的支付界面&#xff0c;以便用户快速、方便地进行付款。这种 API 的设计目的是为了简化用户支付过程&#xff0c;提高支付转化率&#xff0c;并提供一种更加统一的支付体验。 支持…...

【杂记-浅谈EBGP外部边界网关协议、IBGP内部边界网关协议】

一、EBGP概述 EBGP&#xff0c;External Border Gateway Protocol&#xff0c;即外部边界网关协议&#xff0c;EBGP主要用于在不同自治系统&#xff08;AS&#xff09;之间交换路由信息&#xff0c;每个AS都有一个独特的AS号码&#xff0c;用于区分不同的自治系统。EBGP通过AS…...

基于Java的宠物领养管理系统【附源码】

摘 要 近些年来&#xff0c;随着科技的飞速发展&#xff0c;互联网的普及逐渐延伸到各行各业中&#xff0c;给人们生活带来了十分的便利&#xff0c;宠物管理系统利用计算机网络实现信息化管理&#xff0c;使整个宠物领养的发展和服务水平有显著提升。 本文拟采用IDEA开发工具…...

Grafana 对接 Zabbix 数据源API错误

介绍 主要报错为 Invalid params. Invalid parameter "/": unexpected parameter "user". 主要原因为Zabbix 6.4.0以上的版本更新了API&#xff0c;导致Grafana的数据源插件不兼容。 解决方案 更新到最新的Grafana 和 grafana-zabbix 插件即可。&#x…...

Spring Boot与Apache Kafka的深度集成

Spring Boot与Apache Kafka的深度集成 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将探讨如何在Spring Boot应用中实现与Apache Kafka的深度集成&am…...

07 Pytoch Module

1.继承nn.Module 2.class A (B) 进入到 super_init() 3.进入construct&#xff08;&#xff09; 初始化参数 同时判断是否为train 4.跳出来&#xff1a;进入了 forward 中 5.子模块的构建 nn.Module总结 一个module可以包含多个子module 一个module相当于一个运算,必须实现…...

Isaac Sim 9 物理(1)

使用Python USD API 来实现 Physics 。 以下内容中&#xff0c;大部分 Python 代码可以在 Physics Python 演示脚本文件中找到&#xff0c;本文仅作为个人学习笔记。 一.设置 USD Stage 和物理场景 Setting up a USD Stage and a Physics Scene USD Stage不知道怎么翻译&#…...

vue vue.config.js webpack 加密混淆代码

一、下载加密插件 webpack-obfuscator npm install --save-dev webpack-obfuscatorVue CLI 本身依赖于 Webpack 进行构建和打包。不需要单独安装 Webpack 二、配置vue.config.js const { defineConfig } require(vue/cli-service) const WebpackObfuscator require(webpac…...

Talk|北京大学PKU-DAIR余昭辰:从多模态理解到生成 - 从LLM到Diffusion Model

本期为TechBeat人工智能社区第603期线上Talk。 北京时间6月26日(周三)20:00&#xff0c;北京大学PKU-DAIR实习生—余昭辰的Talk已经准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “从多模态理解到生成 - 从LLM到Diffusion Model”&#xff0c;在本次Talk…...

数据中台高频面试题及参考答案(持续更新)

做大数据开发的,个人感觉招人最多的是是数据中台和数据仓库领域的。所以对数据中台、数据仓库相关的面试题要熟悉。 什么是数据中台?它与传统数据仓库的区别是什么? 数据中台是一种企业级的数据管理和分析平台,旨在通过集成、处理和分析来自企业内外部的大量多样化的数据…...

腾讯云CVM,CentOS8系统下部署Java-Web项目步骤详解

在CVM中部署项目首先要配置好JDK,Tomcat,Mysql(这里以Tomcat和Mysql为例)。部署JDK和Tomcat的步骤可以参考 CentOS7系统下部署tomcat,浏览器访问localhost:8080/_不积跬步&#xff0c;无以至千里&#xff1b;不积小流&#xff0c;无以成江河。-CSDN博客 我这里从Mysql的安装和设…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

深入理解Optional:处理空指针异常

1. 使用Optional处理可能为空的集合 在Java开发中&#xff0c;集合判空是一个常见但容易出错的场景。传统方式虽然可行&#xff0c;但存在一些潜在问题&#xff1a; // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...