FFmpeg5.0源码阅读——VideoToobox硬件解码
摘要:本文描述了FFmpeg中videotoobox解码器如何进行解码工作,如何将一个编码的码流解码为最终的裸流。
关键字:videotoobox,decoder,ffmpeg
VideoToolbox 是一个低级框架,提供对硬件编码器和解码器的直接访问。 它提供视频压缩和解压缩服务,以及存储在 CoreVideo 像素缓冲区中的光栅图像格式之间的转换服务。 这些服务以会话对象(压缩、解压缩和像素传输)的形式提供,并作为 Core Foundation (CF) 类型输出。 VideoToolbox支持H.263, H.264, HEVC, MPEG-1, MPEG-2, MPEG-4 Part 2, ProRes解码,H.264, HEVC, ProRes编码,最新的版本似乎也支持了VP9解码。
1 主流程
1.1 涉及的Context
FFmpeg中每个解码器都有自己的Context描述,该描述按照约定的格式描述对应的解码器参数和解码器的处理函数指针。FFmpeg中的VideoToolbox解码器主要实现代码在libavcodec/videotoobox.{h,c}中,其中针对每一种支持的解码格式定义了一个独立的Context,比如ff_h263_videotoolbox_hwaccel,ff_h263_videotoolbox_hwaccel,ff_h264_videotoolbox_hwaccel,...等,只是实现上有差异,我们主要关注其中一个即可,这里主要关注ff_h264_videotoolbox_hwaccel。
const AVHWAccel ff_h264_videotoolbox_hwaccel = {.name = "h264_videotoolbox",.type = AVMEDIA_TYPE_VIDEO,.id = AV_CODEC_ID_H264,.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX,.alloc_frame = ff_videotoolbox_alloc_frame,.start_frame = ff_videotoolbox_h264_start_frame,.decode_slice = ff_videotoolbox_h264_decode_slice,.decode_params = videotoolbox_h264_decode_params,.end_frame = videotoolbox_h264_end_frame,.frame_params = ff_videotoolbox_frame_params,.init = ff_videotoolbox_common_init,.uninit = ff_videotoolbox_uninit,.priv_data_size = sizeof(VTContext),
};
该结构中定义了:
- 解码器的名称;
- 解码数据的类型;
- 解码器ID;
- 硬件解码的格式;
- 申请一个硬件相关的帧结构的函数指针;
- 解码开始前针对帧进行内存拷贝之类的操作;
- 解码数据;
- 解析解码器需要的参数比如sps等;
- 送帧结束后的后处理;
- 初始化硬件解码器;
- 销毁硬件解码器;
- 当前硬件解码器的描述结构。
ff_h264_videotoolbox_hwaccel是存储在hw_configs中的,运行时遍历该列表寻找期望的硬件解码器。所以解码工作是先经过FFmpeg内的ff_h264_decoder解码器再进入硬件解码器的。
const AVCodec ff_h264_decoder = {.name = "h264",.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),.type = AVMEDIA_TYPE_VIDEO,.id = AV_CODEC_ID_H264,.priv_data_size = sizeof(H264Context),.init = h264_decode_init,.close = h264_decode_end,.decode = h264_decode_frame,.capabilities = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |AV_CODEC_CAP_FRAME_THREADS,.hw_configs = (const AVCodecHWConfigInternal *const []) {
#if CONFIG_H264_DXVA2_HWACCELHWACCEL_DXVA2(h264),
#endif
#if CONFIG_H264_D3D11VA_HWACCELHWACCEL_D3D11VA(h264),
#endif
#if CONFIG_H264_D3D11VA2_HWACCELHWACCEL_D3D11VA2(h264),
#endif
#if CONFIG_H264_NVDEC_HWACCELHWACCEL_NVDEC(h264),
#endif
#if CONFIG_H264_VAAPI_HWACCELHWACCEL_VAAPI(h264),
#endif
#if CONFIG_H264_VDPAU_HWACCELHWACCEL_VDPAU(h264),
#endif
#if CONFIG_H264_VIDEOTOOLBOX_HWACCELHWACCEL_VIDEOTOOLBOX(h264),
#endifNULL},.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING |FF_CODEC_CAP_ALLOCATE_PROGRESS | FF_CODEC_CAP_INIT_CLEANUP,.flush = h264_decode_flush,.update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),.update_thread_context_for_user = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context_for_user),.profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles),.priv_class = &h264_class,
};
VTContextVT解码过程中描述VT的Context。
typedef struct VTContext {// The current bitstream buffer.uint8_t *bitstream;// The current size of the bitstream.int bitstream_size;// The reference size used for fast reallocation.int allocated_size;// The core video bufferCVImageBufferRef frame;// Current dummy frames context (depends on exact CVImageBufferRef params).struct AVBufferRef *cached_hw_frames_ctx;// Non-NULL if the new hwaccel API is used. This is only a separate struct// to ease compatibility with the old API.struct AVVideotoolboxContext *vt_ctx;// Current H264 parameters (used to trigger decoder restart on SPS changes).uint8_t sps[3];bool reconfig_needed;void *logctx;
} VTContext;
1.2 主要流程

2 每个步骤的具体实现
2.1ff_videotoolbox_common_init
ff_videotoolbox_common_init在初始化解码器时调用,一般是在avcodec_open2时初始化硬件解码器。一般FFmpeg为了更加准确的探测当前视频的媒体信息,在avformat_find_stream_info时就会初始化解码器解码少部分的帧来进行流媒体信息探测。
初始化时首先就时申请VT的Context内存,并设置一些参数,实际上只设置了VT的callback函数和PixFormat。之后及时根据需要初始化AVHWFramesContext,主要就是申请内存并设置帧格式比如宽高,格式等等。
最后就是调用videotoolbox_start创建VT的Session,创建的过程比较简单就是直接调用Apple的API创建Session,需要重点关注的是如何设置的。具体的实现函数为videotoolbox_decoder_config_create,其中设置硬件加速的配置时写死的,无法进行配置。另外就是从当前的CodecCteonxt中取出sps等信息送给解码器,如果没有这些信息,解码器是无法准确识别出时间戳信息的。sps和pps的解析是由FFmpeg完成的。
switch (codec_type) {case kCMVideoCodecType_MPEG4Video :if (avctx->extradata_size)data = videotoolbox_esds_extradata_create(avctx);if (data)CFDictionarySetValue(avc_info, CFSTR("esds"), data);break;case kCMVideoCodecType_H264 :data = ff_videotoolbox_avcc_extradata_create(avctx);if (data)CFDictionarySetValue(avc_info, CFSTR("avcC"), data);break;case kCMVideoCodecType_HEVC :data = ff_videotoolbox_hvcc_extradata_create(avctx);if (data)CFDictionarySetValue(avc_info, CFSTR("hvcC"), data);break;
#if CONFIG_VP9_VIDEOTOOLBOX_HWACCELcase kCMVideoCodecType_VP9 :data = ff_videotoolbox_vpcc_extradata_create(avctx);if (data)CFDictionarySetValue(avc_info, CFSTR("vpcC"), data);break;
#endifdefault:break;}
解码callback的实现比较简单就是Retain一下CVPixelBuffer。
static void videotoolbox_decoder_callback(void *opaque,void *sourceFrameRefCon,OSStatus status,VTDecodeInfoFlags flags,CVImageBufferRef image_buffer,CMTime pts,CMTime duration)
{VTContext *vtctx = opaque;if (vtctx->frame) {CVPixelBufferRelease(vtctx->frame);vtctx->frame = NULL;}if (!image_buffer) {av_log(vtctx->logctx, AV_LOG_DEBUG,"vt decoder cb: output image buffer is null: %i\n", status);return;}vtctx->frame = CVPixelBufferRetain(image_buffer);
}
2.2 videotoolbox_h264_decode_params和ff_videotoolbox_frame_params
&esmp;videotoolbox_h264_decode_params主要的工作就是将上层解码出来额sps和pps信息拷贝到VTContext中。
case H264_NAL_SPS: {GetBitContext tmp_gb = nal->gb;if (avctx->hwaccel && avctx->hwaccel->decode_params) {ret = avctx->hwaccel->decode_params(avctx,nal->type,nal->raw_data,nal->raw_size);if (ret < 0)goto end;}if (ff_h264_decode_seq_parameter_set(&tmp_gb, avctx, &h->ps, 0) >= 0)break;av_log(h->avctx, AV_LOG_DEBUG,"SPS decoding failure, trying again with the complete NAL\n");init_get_bits8(&tmp_gb, nal->raw_data + 1, nal->raw_size - 1);if (ff_h264_decode_seq_parameter_set(&tmp_gb, avctx, &h->ps, 0) >= 0)break;ff_h264_decode_seq_parameter_set(&nal->gb, avctx, &h->ps, 1);break;
ff_videotoolbox_frame_params比较简单就是将CodecContext中的参数传递给HWFramesContext。
ff_videotoolbox_alloc_frame,ff_videotoolbox_h264_start_frame,ff_videotoolbox_h264_decode_slice,videotoolbox_h264_end_frame
这几个函数每一帧都会调用,顺序是alloc_frame->start_frame->decode_frame->end_frame。
ff_videotoolbox_alloc_frame用来申请一块内存,此时的内存只是一块儿裸内存只是将release函数指针设置成了VT的release指针,还未与CVPixelBuffer绑定,绑定是在解码器的Callback中进行的。
ff_videotoolbox_h264_start_frame主要就是将上层传下来的stream数据流拷贝到VTContext中。
videotoolbox_common_decode_slice也是拷贝数据流。
videotoolbox_h264_end_frame才是具体将数据送给解码器的地方,核心的地方就是videotoolbox_session_decode_frame,这里送给解码器的数据流就上上面拷贝的数据流,需要注意的是在初始化时的callback中只是做了拷贝内存其他什么也没有做。这是因为在这里调用了VTDecompressionSessionWaitForAsynchronousFrames等待异步解码完成,能够保证上一帧解码完成后才送下一帧数据。
2.3 ff_videotoolbox_uninit
ff_videotoolbox_uninit比较简单就是释放解码器的Context和缓存中的内存。
- Apple Documentation——VideoToolbox
相关文章:
FFmpeg5.0源码阅读——VideoToobox硬件解码
摘要:本文描述了FFmpeg中videotoobox解码器如何进行解码工作,如何将一个编码的码流解码为最终的裸流。 关键字:videotoobox,decoder,ffmpeg VideoToolbox 是一个低级框架,提供对硬件编码器和解码器的直接访问。 它提供视频…...
IDEA 中Tomcat源码环境搭建
一、从仓库中拉取源代码 配置仓库地址、项目目录;点击Clone按钮,从仓库中拉取代码 Tomcat源码对应的github地址: https://github.com/apache/tomcat.git 二、安装Ant插件 打开 File -> Setting -> Plugins 三、添加Build文件 &…...
MATLAB | 七夕节用MATLAB画个玫瑰花束叭
Hey又是一年七夕节要到了,每年一次直男审美MATLAB绘图大赛开始hiahiahia,真的这些代码越写越不知道咋写,又不想每年把之前的代码翻出来再发一遍,于是今年又对我之前写的老代码进行了点优化组合,整了个花球变花束&#…...
嵌入式开发之configure
1 前述 在Linux的应用或者驱动开发过程中,编写makefile是无法避免的问题,但是由于makefile的各种规则,或显式,或隐式,非常多,不经常写的话,很难写出一个可用的makefile文件。为了“偷懒”&…...
深入浅出Pytorch函数——torch.nn.Module
分类目录:《深入浅出Pytorch函数》总目录 Pytorch中所有网络的基类,我们的模型也应该继承这个类。Modules也可以包含其它Modules,允许使用树结构嵌入他们,我们还可以将子模块赋值给模型属性。 语法 torch.nn.Module(*args, **kwargs)方法 …...
【100天精通python】Day38:GUI界面编程_PyQt 从入门到实战(中)_数据库操作与多线程编程
目录 专栏导读 4 数据库操作 4.1 连接数据库 4.2 执行 SQL 查询和更新: 4.3 使用模型和视图显示数据 5 多线程编程 5.1 多线程编程的概念和优势 5.2 在 PyQt 中使用多线程 5.3 处理多线程间的同步和通信问题 5.3.1 信号槽机制 5.3.2 线程安全的数据访问 Q…...
STM32--TIM定时器(3)
文章目录 输入捕获简介频率测量输入捕获通道输入捕获基本结构PWMI的基本结构输入捕获模式测量PWM频率和占空比代码 编码器接口正交编码器工作模式接口基本结构TIM编码接口器测速代码: 输入捕获简介 输入捕获IC(Input Capture),是处理器捕获外部输入信号…...
爬虫框架- feapder + 爬虫管理系统 - feaplat 的学习简记
文章目录 feapder 的使用feaplat 爬虫管理系统部署 feapder 的使用 feapder是一款上手简单,功能强大的Python爬虫框架 feapder 官方文档 文档写的很详细,可以直接上手。 基本命令: 创建爬虫项目 feapder create -p first-project创建爬虫 …...
设计模式详解-享元模式
类型:结构型模式 实现原理:尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象 目的:减少创建对象的数量以减少内存占用和提高性能。 解决的问题:大量的对象可能造成的内存溢出问题 解决方法&a…...
BDA初级分析——用SQL筛选数据
一、用SQL对数据分组 GROUP BY Group by,按...分组 作用:根据给定字段进行字段的分组,通常和聚合函数配合使用,实现分组的分析 写法:select ...from ...group by 字段名 (也可以是多个字段) GROUP BY的逻辑 SELECT gender,COUNT(user_id) …...
(成功踩坑)electron-builder打包过程中报错
目录 注意:文中的解决方法2,一定全部看完,再进行操作,有坑 背景 报错1: 报错2: 1.原因:网络连接失败 2.解决方法1: 3.解决方法2: 3.1查看缺少什么资源文件 3.2去淘…...
【STM32】 工程
🚩 WRITE IN FRONT 🚩 🔎 介绍:"謓泽"正在路上朝着"攻城狮"方向"前进四" 🔎🏅 荣誉:2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2022博客之星TO…...
Git概述
目录 一、什么是Git 二、什么是版本控制系统 三、Git和SVN对比 SVN集中式 SVN优缺点 Git分布式 Git优缺点 四、Git工作流程 四个工作区域 工作流程 五、Git下载与安装 一、什么是Git 很多人都知道,林纳斯托瓦兹在1991年创建了开源的Linux,从…...
ubuntu 编译安装nginx及安装nginx_upstream_check_module模块
如果有帮助到你,麻烦点个赞呗~ 一、下载安装包 # 下载nginx_upstream_check_module模块 wget https://codeload.github.com/yaoweibin/nginx_upstream_check_module/zip/master# 解压 unzip master# 下载nginx 1.21.6 wget https://github.com/nginx/…...
近 2000 台 Citrix NetScaler 服务器遭到破坏
Bleeping Computer 网站披露在某次大规模网络攻击活动中,一名攻击者利用被追踪为 CVE-2023-3519 的高危远程代码执行漏洞,入侵了近 2000 台 Citrix NetScaler 服务器。 研究人员表示在管理员安装漏洞补丁之前已经有 1200 多台服务器被设置了后门&#x…...
MySQL MVCC的详解之Read View
文章目录 概要一、基于UNDO LOG的版本链1.1、行记录结构1.2、了解UNDO LOG1.3、版本链 二、Read View2.1、判定机制 三、参考 概要 在上文中,我们提到了MVCC(Multi-Version Concurrency Control)多版本并发控制,是通过undo log来实现的。那具…...
基于springboot+vue的考研资讯平台(前后端分离)
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...
学习网络编程No.3【socket理论实战】
引言: 北京时间:2023/8/12/15:32,自前天晚上更新完文章,看了一下鹅厂新出的《扫毒3》摆烂至现在,不知道是长大了,还是近年港片就那样,给我的感觉不是很好,也可能是国内市场对港片不…...
Linux学习之ssh和scp
ls /etc/ssh可以看到这个目录下有一些文件,而/etc/ssh/ssh_config是客户端配置文件,/etc/ssh/sshd_config是服务端配置文件。 cat -n /etc/ssh/sshd_config | grep "Port "可以看一下sshd监听端口的配置信息,发现这个配置端口是22…...
录制游戏视频的软件有哪些?分享3款软件!
“有录制游戏视频的软件推荐吗?最近迷上了网游,想录制点自己高端操作的游戏画面,但是不知道用什么软件录屏比较好,就想问问大家,有没有好用的录制游戏视频软件。” 在游戏领域,玩家们喜欢通过录制游戏视频…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
32单片机——基本定时器
STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源 1、定…...
内窥镜检查中基于提示的息肉分割|文献速递-深度学习医疗AI最新文献
Title 题目 Prompt-based polyp segmentation during endoscopy 内窥镜检查中基于提示的息肉分割 01 文献速递介绍 以下是对这段英文内容的中文翻译: ### 胃肠道癌症的发病率呈上升趋势,且有年轻化倾向(Bray等人,2018&#x…...
Tauri2学习笔记
教程地址:https://www.bilibili.com/video/BV1Ca411N7mF?spm_id_from333.788.player.switch&vd_source707ec8983cc32e6e065d5496a7f79ee6 官方指引:https://tauri.app/zh-cn/start/ 目前Tauri2的教程视频不多,我按照Tauri1的教程来学习&…...
npm安装electron下载太慢,导致报错
npm安装electron下载太慢,导致报错 背景 想学习electron框架做个桌面应用,卡在了安装依赖(无语了)。。。一开始以为node版本或者npm版本太低问题,调整版本后还是报错。偶尔执行install命令后,可以开始下载…...
基于Java项目的Karate API测试
Karate 实现了可以只编写Feature 文件进行测试,但是对于熟悉Java语言的开发或是测试人员,可以通过编程方式集成 Karate 丰富的自动化和数据断言功能。 本篇快速介绍在Java Maven项目中编写和运行测试的示例。 创建Maven项目 最简单的创建项目的方式就是创建一个目录,里面…...
【大厂机试题+算法可视化】最长的指定瑕疵度的元音子串
题目 开头和结尾都是元音字母(aeiouAEIOU)的字符串为元音字符串,其中混杂的非元音字母数量为其瑕疵度。比如: “a” 、 “aa”是元音字符串,其瑕疵度都为0 “aiur”不是元音字符串(结尾不是元音字符) “…...
Kotlin REPL初探
文章目录 1. Kotlin REPL 简介2. 在命令行中玩Kotlin REPL2.1 下载Kotlin编译器压缩包2.2 安装配置Kotlin编译器2.3 启动Kotlin交互式环境2.4 在命令行玩Kotlin REPL 3. 在IDEA里玩Kotlin REPL3.1 打开Kotlin REPL窗口3.2 在Kotlin REPL窗口玩代码 4. Kotlin REPL 的优势 1. Ko…...
SpringBoot3中使用虚拟线程的详细过程
在 Spring Boot 3 中使用 Java 21 的虚拟线程(Virtual Threads)可以显著提升 I/O 密集型应用的并发能力。以下是详细实现步骤: 1. 环境准备 JDK 21:确保安装 JDK 21 或更高版本Spring Boot 3.2:最低要求(p…...
