FFMPEG 硬件解码API介绍 | 您所在的位置:网站首页 › drm硬解什么意思 › FFMPEG 硬件解码API介绍 |
https://zhuanlan.zhihu.com/p/168240163 软解和硬解的区别: 一般来说我们把使用CPU通用计算单元(无论是Intel还是AMD)就是软解;用专用芯片模组(GPU、QSV等)就是硬解。 主要区别: 底层接口不同、指令集不同、硬件驱动不同。 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是Intel提供的一套集显上的硬件加速方案。 那么,究竟要怎么知道系统当前的FFMPEG究竟支持哪些硬件库?
可以通过命令行查看:ffmpeg -hwaccel。在hardware acceleration methods:下面可以看到当前FFMPEG集成的硬解码库。
FFMPEG硬解码API: 软解码和硬解码差不多,FFMPEG硬件解码流程图:图中橙色部分是硬解码中有而软解码没有的部分:
硬解码Step1. 寻找硬解codec AVCodec* avcodec_find_decoder_by_name(const char *name) 通过名字来寻找对应的AVCodec。每一个解码器的名字一定是全局唯一的,在AVCodec头文件中有相应的描述: 其实在FFMPEG内部每一个解码器codec都是一个结构体,维护了该解码器自己的信息、具体执行的函数等信息。比如Intel的QSV解码器(在libavcodec/qsvdec_h2645.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", }; const AVCodecHWConfig * avcodec_get_hw_config (const AVCodec *codec, int index) 紧接着,调用这个函数去获取到该解码器codec的硬件属性,比如可以支持的目标像素格式等。而这个信息就存储在AVCodecHWConfig中: 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;
enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt); 这是一个回调函数,它的作用就是告诉解码器codec自己的目标像素格式是什么。在上一步骤获取到了硬解码器codec可以支持的目标格式之后,就通过这个回调函数告知给codec,具体做法: enum AVPixelFormat get_hw_format(struct AVCodecContext *s, const enum AVPixelFormat *fmt) { for (const enum AVPixelFormat *p = fmt; *p != -1; p++) { if (*p == hw_pix_fmt) return *p; } return AV_PIX_FMT_NONE; } 我们也可以通过阅读AVCodec结构内对于这个回调函数的定义,可以知道: fmt是这个解码器codec支持的像素格式,且按照质量优劣进行排序;如果没有特别的需要,这个步骤是可以省略的。内部默认会使用“native”的格式。* callback to negotiate the pixelFormat * @param fmt is the list of formats which are supported by the codec, it is terminated by -1 as 0 is a valid format, the formats are ordered by quality. The first is always the native one. * @note The callback may be called again immediately if initialization for the selected (hardware-accelerated) pixel format failed. * @warning Behavior is undefined if the callback returns a value not in the fmt list of formats. * @return the chosen format * - encoding: unused * - decoding: Set by user, if not set the native format will be chosen.
硬解码Step3. 准备和打开硬解码: int av_hwdevice_ctx_create(AVBufferRef **pdevice_ref, enum AVHWDeviceType type, const char *device, AVDictionary *opts, int flags) 这个函数的作用是,创建硬件设备相关的上下文信息AVHWDeviceContext,包括分配内存资源、对硬件设备进行初始化。 准备好硬件设备上下文AVHWDeviceContext后,需要把这个信息绑定到AVCodecContext,就可以像软解一样的流程执行解码操作了。 硬解码Step4. 取回数据: 按照一般软解的流程,在调用avcodec_receive_frame()之后,得到的数据其实还在硬件模组/芯片上,也就是说,如果是用CUDA解码,数据是在显存上(或者说是在显卡encoder/decoder的buffer上)的。对于很多应用而言,解码之后往往还要进行后续操作,比如保存成一幅幅图片之类的,那么就需要把数据取回。 int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags) 这个函数是负责在cpu内存和硬件内存(原文是hw surface)之间做数据交换的。也就是说,它不但可以把数据从硬件surface上搬回系统内存,反向操作也支持;甚至可以直接在硬件内存之间做数据交互。
|
CopyRight 2018-2019 实验室设备网 版权所有 |