如何将像素格式为AV_PIX_FMT_CUDA的FFmpeg AVFrame转换为像素格式为AV_PIX_FMT_RGB的新AVFrame

cos*_*tef 8 c++ ffmpeg h.264 cuvid

我有一个使用FFmpeg 3.2接收H264 RTP流的简单C ++应用程序。为了节省CPU,我正在使用编解码器h264_cuvid进行解码。我的FFmpeg 3.2是在启用硬件加速的情况下编译的。实际上,如果我执行以下命令:

ffmpeg -hwaccels
Run Code Online (Sandbox Code Playgroud)

我懂了

cuvid
Run Code Online (Sandbox Code Playgroud)

这意味着我的FFmpeg设置可以与NVIDIA卡“通话”。该功能avcodec_decode_video2提供给我的帧具有像素格式AV_PIX_FMT_CUDA。我需要使用将这些框架转换为新的框架AV_PIX_FMT_RGB。不幸的是,我可以用很好knwon功能不进行转换sws_getContext,并sws_scale因为像素格式AV_PIX_FMT_CUDA不被支持。如果我尝试使用swscale,则会收到错误消息:

“不支持cuda作为输入像素格式”

您知道如何将FFmpeg AVFrame从转换AV_PIX_FMT_CUDAAV_PIX_FMT_RGB吗?(代码片段将不胜感激)

lp3*_*p35 6

这是我对最新 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);
Run Code Online (Sandbox Code Playgroud)

列出支持的“软件”像素格式

如果要选择像素格式,请注意这里,并非所有 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!
Run Code Online (Sandbox Code Playgroud)

最后,一种更快的方法可能是av_hwframe_transfer_get_formats()函数,但您至少需要解码一帧。

希望这会有所帮助!


HMD*_*HMD 2

您必须使用vf_scale_npp来执行此操作。您可以使用其中之一nppscale_deinterleave,也nppscale_resize可以根据您的需要而定。

两者都有相同的输入参数,即应使用AVFilterContext进行初始化nppscale_initNPPScaleStageContext接受您的输入/输出像素格式和两个AVFrame,这当然是您的输入和输出帧。

有关更多信息,您可以查看npplib\nppscale定义,它将自 ffmpeg 3.1 起执行 CUDA 加速格式转换和缩放。

无论如何,我建议直接使用NVIDIA Video Codec SDK来实现此目的。