这也是ffmpeg解码器中比较重要的一个模块,很多人认识它应该是通过一条命令
ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i input.mp4 -c:v h264_nvenc -b:v 5M output.mp4
命令地址:英伟达ffmpeg
大家可能觉得这就是nvcodec了,后来发现专门还有一个cuvid呢?瞬间不开心了,不知道这两个倒地时什么关系?如果你时看这个问题的,恭喜你来对了。
下面我们就说道说道。
ffmpeg是通过解码起家,所以它内部有很多自己写的软解码器,在这些软解码器的解码过程当中,比如说对于码流中的某些反量化,反变换等操作,把这些操作挪到一块硬件上,这块硬件就是加速设备。
我们可以看看ffmpeg的h264解码器,红框内部的都是h264解码器的加速插件,NVDEC只是其中之一。
再来看看代码
static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
{AVCodecContext *const avctx = h->avctx;...switch (nal->type) {case H264_NAL_IDR_SLICE:...if (h->avctx->hwaccel &&//这里就进入了加速解码的分支(ret = h->avctx->hwaccel->start_frame(h->avctx, buf, buf_size)) < 0)goto end;}...max_slice_ctx = avctx->hwaccel ? 1 : h->nb_slice_ctx;if (h->nb_slice_ctx_queued == max_slice_ctx) {//这里进入加速解码的分支中if (h->avctx->hwaccel) {ret = avctx->hwaccel->decode_slice(avctx, nal->raw_data, nal->raw_size);h->nb_slice_ctx_queued = 0;} elseret = ff_h264_execute_decode_slices(h);if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))goto end;}break;...
这就是hwaccel加速解码的原理,它是在ffmpeg软解码的基础上将一些特定算法加载到硬件上去做。而cuvid是通过ffmpeg第三方插件库的方式去实现的。
可以看看AVHWAccel结构体定义,发现有几个关键的函数指针,这些指针就是你需要去实现的。
可能你又要问了,为什么英伟达解码器要实现两套?
这个问题留给你吧。或者在我博客里面找,一定有的。
/*** @defgroup lavc_hwaccel AVHWAccel** @note Nothing in this structure should be accessed by the user. At some* point in future it will not be externally visible at all.** @{*/
typedef struct AVHWAccel {/*** Name of the hardware accelerated codec.* The name is globally unique among encoders and among decoders (but an* encoder and a decoder can share the same name).*/const char *name;/*** Type of codec implemented by the hardware accelerator.** See AVMEDIA_TYPE_xxx*/enum AVMediaType type;/*** Codec implemented by the hardware accelerator.** See AV_CODEC_ID_xxx*/enum AVCodecID id;/*** Supported pixel format.** Only hardware accelerated formats are supported here.*/enum AVPixelFormat pix_fmt;/*** Hardware accelerated codec capabilities.* see AV_HWACCEL_CODEC_CAP_**/int capabilities;/****************************************************************** No fields below this line are part of the public API. They* may not be used outside of libavcodec and can be changed and* removed at will.* New public fields should be added right above.******************************************************************//*** Allocate a custom buffer*/int (*alloc_frame)(AVCodecContext *avctx, AVFrame *frame);/*** Called at the beginning of each frame or field picture.** Meaningful frame information (codec specific) is guaranteed to* be parsed at this point. This function is mandatory.** Note that buf can be NULL along with buf_size set to 0.* Otherwise, this means the whole frame is available at this point.** @param avctx the codec context* @param buf the frame data buffer base* @param buf_size the size of the frame in bytes* @return zero if successful, a negative value otherwise*/int (*start_frame)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size);/*** Callback for parameter data (SPS/PPS/VPS etc).** Useful for hardware decoders which keep persistent state about the* video parameters, and need to receive any changes to update that state.** @param avctx the codec context* @param type the nal unit type* @param buf the nal unit data buffer* @param buf_size the size of the nal unit in bytes* @return zero if successful, a negative value otherwise*/int (*decode_params)(AVCodecContext *avctx, int type, const uint8_t *buf, uint32_t buf_size);/*** Callback for each slice.** Meaningful slice information (codec specific) is guaranteed to* be parsed at this point. This function is mandatory.* The only exception is XvMC, that works on MB level.** @param avctx the codec context* @param buf the slice data buffer base* @param buf_size the size of the slice in bytes* @return zero if successful, a negative value otherwise*/int (*decode_slice)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size);/*** Called at the end of each frame or field picture.** The whole picture is parsed at this point and can now be sent* to the hardware accelerator. This function is mandatory.** @param avctx the codec context* @return zero if successful, a negative value otherwise*/int (*end_frame)(AVCodecContext *avctx);/*** Size of per-frame hardware accelerator private data.** Private data is allocated with av_mallocz() before* AVCodecContext.get_buffer() and deallocated after* AVCodecContext.release_buffer().*/int frame_priv_data_size;/*** Called for every Macroblock in a slice.** XvMC uses it to replace the ff_mpv_reconstruct_mb().* Instead of decoding to raw picture, MB parameters are* stored in an array provided by the video driver.** @param s the mpeg context*/void (*decode_mb)(struct MpegEncContext *s);/*** Initialize the hwaccel private data.** This will be called from ff_get_format(), after hwaccel and* hwaccel_context are set and the hwaccel private data in AVCodecInternal* is allocated.*/int (*init)(AVCodecContext *avctx);/*** Uninitialize the hwaccel private data.** This will be called from get_format() or avcodec_close(), after hwaccel* and hwaccel_context are already uninitialized.*/int (*uninit)(AVCodecContext *avctx);/*** Size of the private data to allocate in* AVCodecInternal.hwaccel_priv_data.*/int priv_data_size;/*** Internal hwaccel capabilities.*/int caps_internal;/*** Fill the given hw_frames context with current codec parameters. Called* from get_format. Refer to avcodec_get_hw_frames_parameters() for* details.** This CAN be called before AVHWAccel.init is called, and you must assume* that avctx->hwaccel_priv_data is invalid.*/int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx);
} AVHWAccel;