硬件编解码
如何利用日益强大的硬件来实现高效的编解码有则非比寻常的意义,对于开发者来说,面对日益增加的不同硬件,带来的不同的接口,如何快速的使用和对接,本身就是一个问题,还好 ffmpeg 为了我们提供非常好的解决方案,完全不用去关心底层的实现,一切面向接口的良好体现。
hwcontext 模块
支持的平台
static const char *const hw_type_names[] = {[AV_HWDEVICE_TYPE_CUDA] = "cuda", //CUDA 是 Nvidia 出的一个 GPU 计算库
[AV_HWDEVICE_TYPE_DRM] = "drm", //DRM 是 linux 下的图形渲染架构(Direct Render Manager)
[AV_HWDEVICE_TYPE_DXVA2] = "dxva2",// 微软 dx 套件,使用 D3D9
[AV_HWDEVICE_TYPE_D3D11VA] = "d3d11va",// 微软 dx 套件,使用 D3D11
[AV_HWDEVICE_TYPE_OPENCL] = "opencl",// 第一个面向异构系统 (此系统中可由 CPU,GPU 或其它类型的处理器架构组成) 的并行编程的开放式标准。面向 GPU 编程
[AV_HWDEVICE_TYPE_QSV] = "qsv",// 英特尔 Quick Sync Video,号称地球最强
[AV_HWDEVICE_TYPE_VAAPI] = "vaapi", //Video Acceleration Api,UNINX 下的编码接口,intel 提供
[AV_HWDEVICE_TYPE_VDPAU] = "vdpau", //Video Decode and Presentation API for Unix,NVIDIA 提供的
[AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox", // mac iOS
[AV_HWDEVICE_TYPE_MEDIACODEC] = "mediacodec", // Android
};
查看状态或者指定使用某种解码器
ffmpeg -hwaccels
hwcontext 核心接口
创建初始化 av_hwdevice_ctx_create
- av_hwdevice_ctx_alloc
- device_ctx->internal->hw_type->device_create 绑定到具体硬件的指针函数上
- av_hwdevice_ctx_init
创建初始化 AVHWFramesContext
描述硬件 frame 的一个 pool,包括大小,格式,AVHWDeviceContext,长 / 宽等内容
- av_hwframe_ctx_alloc
- av_hwframe_ctx_init
- hwframe_ctx_free
AVFrame 操作
- av_hwframe_transfer_data //gpu frame 和 memory freme 之间的拷贝
- av_hwframe_get_buffer // 分配 AVFrame 空间
硬件解码流程
av_register_all && av_format_init_next && 初始化 outdev_list/indev_list(已经被废弃了)
avdevice_register_all && avpriv_register_device && av_format_init_next && 初始化 outdev_list/indev_list;
void av_register_all(void)
{ff_thread_once(&av_format_next_init, av_format_init_next);
// 保证多线程下只执行一次,屏蔽不同平台的多线程代码差异,不同的平台下映射到不同线程库中的 pthread_once 函数
}
#define PTHREAD_ONCE_INIT {0, _FMUTEX_INITIALIZER}
#define AV_ONCE_INIT PTHREAD_ONCE_INIT
static AVOnce av_format_next_init = AV_ONCE_INIT;
// 实际上 av_format_next_init 就是{0}
static void av_format_init_next(void)
{
// 输入输出结构封装成链表,加锁处理
AVOutputFormat *prevout = NULL, *out;
AVInputFormat *previn = NULL, *in;
ff_mutex_lock(&avpriv_register_devices_mutex);
for (int i = 0; (out = (AVOutputFormat*)muxer_list[i]); i++) {if (prevout)
prevout->next = out;
prevout = out;
}
if (outdev_list) {for (int i = 0; (out = (AVOutputFormat*)outdev_list[i]); i++) {if (prevout)
prevout->next = out;
prevout = out;
}
}
for (int i = 0; (in = (AVInputFormat*)demuxer_list[i]); i++) {if (previn)
previn->next = in;
previn = in;
}
if (indev_list) {for (int i = 0; (in = (AVInputFormat*)indev_list[i]); i++) {if (previn)
previn->next = in;
previn = in;
}
}
ff_mutex_unlock(&avpriv_register_devices_mutex);
}
//h264 的 AVOutputFormat 结构
AVOutputFormat ff_h264_muxer = {
.name = "h264",
.long_name = NULL_IF_CONFIG_SMALL("raw H.264 video"),
.extensions = "h264,264",
.audio_codec = AV_CODEC_ID_NONE,
.video_codec = AV_CODEC_ID_H264,
.write_header = force_one_stream,
.write_packet = ff_raw_write_packet,
.check_bitstream = h264_check_bitstream,
.flags = AVFMT_NOTIMESTAMPS,
};
如何关联硬件解码器
通过 hw_device_setup_for_encode/hw_device_setup_for_decode 进行设置,和 4.x 之前差异较大。
查找 hw_devices 里面的解码器。