This my understanding of the hardware decoding on the latest FFMPeg 4.1 version. Below are my conclusion after studying the source code.
First I recommend to inspire yourself from the hw_decode example:
https://github.com/FFmpeg/FFmpeg/blob/release/4.1/doc/examples/hw_decode.c
With the new API, when you send a packet to the encoder using avcodec_send_packet(), then use avcodec_receive_frame() to retrieve the decoded frame.
There are two different kinds of AVFrame
: software one, which is stored in the "CPU" memory (a.k.a RAM), and hardware one, which is stored in the graphic card memory.
Getting AVFrame from the hardware
To retrieve the hardware frame and get it into a readable, convertible (with swscaler) AVFrame
, av_hwframe_transfer_data() needs to be used to retrieve the data from the graphic card. Then look at the pixel format of the retrieved frame, it is usually NV12 format when using nVidia decoding.
// 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);
Listing supported "software" pixel formats
Side note here if you want to select the pixel format, not all AVPixelFormat are supported. AVHWFramesConstraints is your friend here:
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!
Finally, a quicker way is probably the av_hwframe_transfer_get_formats() function, but you need to decode at least one frame.
Hope this will help!