显卡是个人计算机基础的组成部分之一,将计算机系统需要的显示信息进行转换驱动显示器,并向显示器提供逐行或隔行扫描信号,控制显示器的正确显示,是连接显示器和个人计算机主板的重要组件,是“人机”的重要设备之一,其内置的并行计算能力现阶段也用于深度学习等运算。 写在前面 本文将介绍如何使用FFMPEG API进行硬件解码。如果您不熟悉解码过程,请先阅读: 我是Xiaobei Dig Haha:视频和视频帧:FFMPEG CPU解码API简介 
作者之前也看到过类似的问题:视频硬解码和软解码有什么区别? 本质上没有什么区别,该芯片用于执行编计算。 “软”和“硬”一词很容易引起歧义。从本质上讲,使用CPU通用计算单元(无论是Intel还是AMD)都是一种软解决方案。使用专用芯片模块(GPU,QSV等)是一个很难的解决方案。 因此产生了差异:基础接口不同,指令集不同,硬件驱动程序也不同。由此产生的问题很明显: 首先,因为CPU是通用计算单元,所以接口是通用的,并且可移植性好;专用芯片模块不能移植和互操作;其次,由于CPU接口是通用的,因此编中的许多细节都便于开发人员进行修改。专用的芯片模块,接口和驱动程序都是由不同的制造商提供的,并且其中许多都是非开源的,因此控制内部细节更加困难。最后,在实际测试中,当前使用CPU进行编码和解码的效果将优于专用芯片模块。但是,可以通过优化算法和芯片来解决此问题。这是制造商的业务,我们无法控制。 在现实生活中,是选择硬解码还是软解码? 这取决于不同的情况。例如: CPU过剩,需要对解码过程进行精确控制,对解码算法进行优化,对通用性要求较高,直接使用软解决方案(即CPU解码);如果还有其他编芯片/模块,而CPU不足,则必须切换到硬解码(即专用芯片解码)。 本文将介绍如何使用FFMPEG API进行常规的硬解码。 “常规硬解码”是指主流FFMEG支持的硬解码类型。作者将结合自己在QSV和CUDA解码方面的经验,最后分享我所涉足的一些陷阱以及一些扩展方面的小问题。 I。 FFMPEG支持的硬解码 首先,让我们看一下FFMPEG原生支持哪些硬解码类型。列出AVHWDeviceType(libavutil / hwcontext.h)本机支持的所有硬解码类型: enum AVHWDeviceType {
AV_HWDEVICE_TYPE_NONE,
AV_HWDEVICE_TYPE_VDPAU,
AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_DXVA2,
AV_HWDEVICE_TYPE_QSV,
AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_DRM,
AV_HWDEVICE_TYPE_OPENCL,
AV_HWDEVICE_TYPE_MEDIACODEC,
};
上面的AV_HWDEVICE_TYPE_CUDA是作者当前正在使用的CUDA,是NVIDIA的硬件加速库,而AV_HWDEVICE_TYPE_QSV是以前的QSV,是英特尔提供的一组硬件加速解决方案。 
那么,您如何知道当前FFMPEG支持哪些硬件库? 您可以通过命令行查看它:ffmpeg -hwaccel。在硬件加速方法中:您可以在下面看到当前的FFMPEG集成硬解码库。 然后,如果发现所需的硬件库不在当前的FFMPEG中,该怎么办? 答案是:您可能需要自己重新编译源代码。每个硬件解码库的集成方法都不同。如果是QSV,请参见 我是小贝挖哈哈:视频和视频帧:FFMPEG + Intel QSV硬件解决方案环境安装文章 
如果它是另一个硬解码库,请自己在Internet上搜索。 (作者将在稍后添加如何将CUDA库集成到FFMPEG中,敬请期待) II。 FFMPEG硬解码API 硬解码步骤类似于软解码步骤。作者绘制了FFMPEG硬件解码流程图:图中的橙色部分是硬解码中包含的部分,软解码中没有。该图的灵感来自博客“ FFmpeg示例硬件解码hw_decode”,这篇文章是推荐的,简洁的和详细的。 

FFMPEG硬解码流程图 接下来,我将详细介绍上图的橙色部分以及与硬解码有关的API函数。 硬解码步骤1.查找硬解码codec编实现的名称。该名称在编码器和之间是全局唯一的(但编码器和可以共享相同的名称)。这是从用户角度查找编的主要方法。 实际上,FFMPEG中的每个编都是一种结构,它维护自己的信息,特定功能和其他信息。例如,英特尔的QSV(在libavcodec / qsvdec_h264 5. c中)为: AVCodec ff_h264_qsv_decoder = {
.name = "h264_qsv",
.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (Intel Quick Sync Video acceleration)"),
.priv_data_size = sizeof(QSVH2645Context),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.init = qsv_decode_init,
.decode = qsv_decode_frame,
.flush = qsv_decode_flush,
.close = qsv_decode_close,
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HYBRID,
.priv_class = &class,
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12,
AV_PIX_FMT_P010,
AV_PIX_FMT_QSV,
AV_PIX_FMT_NONE },
.hw_configs = ff_qsv_hw_configs,
.bsfs = "h264_mp4toannexb",
.wrapper_name = "qsv",
};

您可以看到此编支持的编ID为AV_CODEC_ID_H264,支持的目标像素格式为{AV_PIX_FMT_NV12,AV_PIX_FMT_P010,AV_PIX_FMT_QSV,AV_PIX_FMT_NONE}。 是的,硬件与通用不同,只能支持有限的目标像素格式。 让我们看一下CUDA(在libavcodec / cuviddec.c中)。同样,它只能支持有限的目标像素格式: AVCodec ff_##x##_cuvid_decoder = { \
.name = #x "_cuvid", \
.long_name = NULL_IF_CONFIG_SMALL("Nvidia CUVID " #X " decoder"), \
.type = AVMEDIA_TYPE_VIDEO, \
.id = AV_CODEC_ID_##X, \
.priv_data_size = sizeof(CuvidContext), \
.priv_class = &x##_cuvid_class, \
.init = cuvid_decode_init, \
.close = cuvid_decode_end, \
.decode = cuvid_decode_frame, \
.receive_frame = cuvid_output_frame, \
.flush = cuvid_flush, \
.bsfs = bsf_name, \
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, \
AV_PIX_FMT_NV12, \
AV_PIX_FMT_P010, \
AV_PIX_FMT_P016, \
AV_PIX_FMT_NONE }, \
.hw_configs = cuvid_hw_configs, \
.wrapper_name = "cuvid", \
};
硬解码步骤2.找到硬解目标像素 在上一节中,我了解到一个事实:硬解码编支持的目标像素是有限的,并且它们可能不相同。因此,在找到经过硬解码的编之后,您必须设置其目标像素(像素格式)。 enum AVHWDeviceType {
AV_HWDEVICE_TYPE_NONE,
AV_HWDEVICE_TYPE_VDPAU,
AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_DXVA2,
AV_HWDEVICE_TYPE_QSV,
AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_DRM,
AV_HWDEVICE_TYPE_OPENCL,
AV_HWDEVICE_TYPE_MEDIACODEC,
AV_HWDEVICE_TYPE_VULKAN,
};
此类型和名称之间的关系表要简单得多。它由FFMPEG代码中的hw_type_names关系表维护(在libavutil / hwcontext.c文件中定义): static const char *const hw_type_names[] = {
[AV_HWDEVICE_TYPE_CUDA] = "cuda",
[AV_HWDEVICE_TYPE_DRM] = "drm",
[AV_HWDEVICE_TYPE_DXVA2] = "dxva2",
[AV_HWDEVICE_TYPE_D3D11VA] = "d3d11va",
[AV_HWDEVICE_TYPE_OPENCL] = "opencl",
[AV_HWDEVICE_TYPE_QSV] = "qsv",
[AV_HWDEVICE_TYPE_VAAPI] = "vaapi",
[AV_HWDEVICE_TYPE_VDPAU] = "vdpau",
[AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox",
[AV_HWDEVICE_TYPE_MEDIACODEC] = "mediacodec",
[AV_HWDEVICE_TYPE_VULKAN] = "vulkan",
};
typedef struct AVCodecHWConfig {
/**
* A hardware pixel format which the codec can use. !!!硬解码codec支持的像素格式!!!
*/
enum AVPixelFormat pix_fmt;
/**
* Bit set of AV_CODEC_HW_CONFIG_METHOD_* flags, describing the possible
* setup methods which can be used with this configuration.
*/
int methods;
/**
* The device type associated with the configuration.
*
* Must be set for AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX and
* AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX, otherwise unused.
*/
enum AVHWDeviceType device_type;
} AVCodecHWConfig;
本文来自本站,转载请注明本文网址: http://www.pc-fly.com/a/shenmilingyu/article-372911-1.html
|