这是我对最新FFMPeg 4.1版本的硬件解码的理解。以下是我研究源代码后的结论。
首先,我建议从 hw_decode 示例中启发自己:
https://github.com/FFmpeg/FFmpeg/blob/release/4.1/doc/examples/hw_decode.c
使用新的 API,当您使用avcodec_send_packet()将数据包发送到编码器时,然后使用avcodec_receive_frame()检索解码的帧。
有两种不同的类型AVFrame
:一种是存储在“CPU”内存(又名 RAM)中的软件,另一种是存储在显卡内存中的硬件。
从硬件获取 AVFrame
要检索硬件帧并将其转换为可读的、可转换的(使用 swscaler)AVFrame
,需要使用av_hwframe_transfer_data()从图形卡中检索数据。然后看检索帧的像素格式,使用nVidia解码时通常是NV12格式。
// According to the API, if the format of the AVFrame is set before calling
// av_hwframe_transfer_data(), the graphic card will try to automatically convert
// to the desired format. (with some limitation, see below)
m_swFrame->format = AV_PIX_FMT_NV12;
// retrieve data from GPU to CPU
err = av_hwframe_transfer_data(
m_swFrame, // The frame that will contain the usable data.
m_decodedFrame, // Frame returned by avcodec_receive_frame()
0);
const char* gpu_pixfmt = av_get_pix_fmt_name((AVPixelFormat)m_decodedFrame->format);
const char* cpu_pixfmt = av_get_pix_fmt_name((AVPixelFormat)m_swFrame->format);
列出支持的“软件”像素格式
如果要选择像素格式,请注意此处,并非所有 AVPixelFormat 都受支持。AVHWFramesConstraints是你的朋友:
AVHWDeviceType type = AV_HWDEVICE_TYPE_CUDA;
int err = av_hwdevice_ctx_create(&hwDeviceCtx, type, nullptr, nullptr, 0);
if (err < 0) {
// Err
}
AVHWFramesConstraints* hw_frames_const = av_hwdevice_get_hwframe_constraints(hwDeviceCtx, nullptr);
if (hw_frames_const == nullptr) {
// Err
}
// Check if we can convert the pixel format to a readable format.
AVPixelFormat found = AV_PIX_FMT_NONE;
for (AVPixelFormat* p = hw_frames_const->valid_sw_formats;
*p != AV_PIX_FMT_NONE; p++)
{
// Check if we can convert to the desired format.
if (sws_isSupportedInput(*p))
{
// Ok! This format can be used with swscale!
found = *p;
break;
}
}
// Don't forget to free the constraint object.
av_hwframe_constraints_free(&hw_frames_const);
// Attach your hw device to your codec context if you want to use hw decoding.
// Check AVCodecContext.hw_device_ctx!
最后,一种更快的方法可能是av_hwframe_transfer_get_formats()函数,但您至少需要解码一帧。
希望这会有所帮助!