繁体   English   中英

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

[英]How can I convert an FFmpeg AVFrame with pixel format AV_PIX_FMT_CUDA to a new AVFrame with pixel format AV_PIX_FMT_RGB

我有一个简单的C ++应用程序,它使用FFmpeg 3.2来接收H264 RTP流。 为了节省CPU,我正在使用编解码器h264_cuvid进行解码部分。 我的FFmpeg 3.2编译时启用了hw加速。 事实上,如果我执行命令:

ffmpeg -hwaccels

我明白了

cuvid

这意味着我的FFmpeg设置可以与我的NVIDIA卡“说话”。 函数avcodec_decode_video2提供给我的帧具有像素格式AV_PIX_FMT_CUDA 我需要使用AV_PIX_FMT_RGB将这些帧转换为新帧。 不幸的是,我无法使用井sws_getContext函数sws_getContextsws_scale进行转换,因为不支持像素格式AV_PIX_FMT_CUDA 如果我尝试swscale我得到错误:

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

你知道怎么把FFmpeg AVFrameAV_PIX_FMT_CUDA转换成AV_PIX_FMT_RGB吗? (非常感谢代码片段)

这是我对最新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()函数,但您需要解码至少一个帧。

希望这会有所帮助!

您必须使用vf_scale_npp执行此操作。 您可以根据需要使用nppscale_deinterleavenppscale_resize

既具有相同的输入参数,其是AVFilterContext应与被初始化nppscale_initNPPScaleStageContext这需要你的输入/输出像素格式和两个AVFrame航向的其是您的输入和输出的帧。

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

无论如何,我建议直接使用NVIDIA Video Codec SDK

我不是一个ffmpeg专家,但我有类似的问题,并设法解决它。 我从cuvid(mjpeg_cuvid解码器)获得AV_PIX_FMT_NV12 ,并希望AV_PIX_FMT_CUDA用于cuda处理。

我发现在解码帧之前设置像素格式是有效的。

    pCodecCtx->pix_fmt = AV_PIX_FMT_CUDA; // change format here
    avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
    // do something with pFrame->data[0] (Y) and pFrame->data[1] (UV)

您可以使用pix_fmts检查解码器支持哪些像素格式:

    AVCodec *pCodec = avcodec_find_decoder_by_name("mjpeg_cuvid");
    for (int i = 0; pCodec->pix_fmts[i] != AV_PIX_FMT_NONE; i++)
            std::cout << pCodec->pix_fmts[i] << std::endl;

我确信有更好的方法,但我使用列表将整数像素格式ID映射到人类可读的像素格式。

如果这不起作用,您可以使用cudaMemcpy将像素从设备传输到主机:

    cudaMemcpy(pLocalBuf pFrame->data[0], size, cudaMemcpyDeviceToHost);

从YUV到RGB / RGBA的转换可以通过多种方式完成。 此示例使用libavdevice API执行此操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM