You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在无NVIDIA GPU设备上解码NVENC编码包并转为AV_PIX_FMT_YUV420P?

解决NVENC编码H.264转YUV420P的问题(无NVIDIA GPU解码端)

我来帮你理清思路并给出可行方案:你现在的问题出在错误地尝试用CUDA硬件解码流程,但解码端根本没有NVIDIA GPU,而且其实完全没必要——NVENC编码的H.264是标准的H.264视频流,任何标准H.264软件解码器都能正常解码,之后再根据需要转成YUV420P格式即可。

核心修正点

  • 放弃CUDA硬件解码配置:因为没有GPU,所有和AV_PIX_FMT_CUDA相关的设置都要去掉,改用纯软件解码流程。
  • 让解码器输出软件格式:不需要手动指定硬件格式,解码器会自动输出兼容的软件像素格式(多数情况下默认就是YUV420P,省去转换步骤)。
  • 按需格式转换:如果解码器输出的不是YUV420P,再用sws_scale做转换,此时源格式是软件格式,sws完全支持。

修正后的完整代码示例

#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <stdio.h>

int main() {
    AVFrame *decoded_frame = NULL;
    AVCodecContext *codec_ctx = NULL;
    AVCodec *h264_decoder = NULL;
    AVPacket encoded_packet; // 假设已填充NVENC编码的H.264数据包

    // 初始化FFmpeg组件
    avcodec_register_all();

    // 查找标准H.264软件解码器
    h264_decoder = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!h264_decoder) {
        fprintf(stderr, "Error: Could not find H.264 decoder\n");
        return -1;
    }

    // 分配解码器上下文
    codec_ctx = avcodec_alloc_context3(h264_decoder);
    if (!codec_ctx) {
        fprintf(stderr, "Error: Could not allocate codec context\n");
        return -1;
    }

    // 打开解码器(纯软件模式,无需硬件参数)
    if (avcodec_open2(codec_ctx, h264_decoder, NULL) < 0) {
        fprintf(stderr, "Error: Could not open decoder\n");
        avcodec_free_context(&codec_ctx);
        return -1;
    }

    // 分配解码帧内存
    decoded_frame = av_frame_alloc();
    if (!decoded_frame) {
        fprintf(stderr, "Error: Could not allocate AVFrame\n");
        avcodec_close(codec_ctx);
        avcodec_free_context(&codec_ctx);
        return -1;
    }

    // 解码数据包
    int got_frame = 0;
    int decode_ret = avcodec_decode_video2(codec_ctx, decoded_frame, &got_frame, &encoded_packet);
    if (decode_ret < 0) {
        fprintf(stderr, "Decode failed: %s\n", av_err2str(decode_ret));
        goto cleanup;
    }

    AVFrame *yuv420p_frame = NULL;
    struct SwsContext *sws_ctx = NULL;

    if (got_frame) {
        // 检查解码后的格式是否已经是YUV420P
        if (decoded_frame->format == AV_PIX_FMT_YUV420P) {
            // 直接使用decoded_frame即可,无需转换
            printf("Decoded frame is already YUV420P\n");
        } else {
            // 分配YUV420P格式的目标帧
            yuv420p_frame = av_frame_alloc();
            if (!yuv420p_frame) {
                fprintf(stderr, "Error: Could not allocate YUV420P frame\n");
                goto cleanup;
            }
            yuv420p_frame->format = AV_PIX_FMT_YUV420P;
            yuv420p_frame->width = decoded_frame->width;
            yuv420p_frame->height = decoded_frame->height;

            // 分配帧缓冲区
            if (av_frame_get_buffer(yuv420p_frame, 0) < 0) {
                fprintf(stderr, "Error: Could not allocate buffer for YUV frame\n");
                av_frame_free(&yuv420p_frame);
                goto cleanup;
            }

            // 创建格式转换上下文
            sws_ctx = sws_getContext(decoded_frame->width, decoded_frame->height,
                                     (enum AVPixelFormat)decoded_frame->format,
                                     decoded_frame->width, decoded_frame->height,
                                     AV_PIX_FMT_YUV420P,
                                     SWS_BILINEAR, NULL, NULL, NULL);
            if (!sws_ctx) {
                fprintf(stderr, "Error: Could not create SWS context\n");
                av_frame_free(&yuv420p_frame);
                goto cleanup;
            }

            // 执行格式转换
            sws_scale(sws_ctx, decoded_frame->data, decoded_frame->linesize, 0, decoded_frame->height,
                      yuv420p_frame->data, yuv420p_frame->linesize);

            // 现在yuv420p_frame就是你需要的格式,可以进行后续处理
            printf("Successfully converted to YUV420P\n");
        }
    }

cleanup:
    // 释放所有资源
    if (sws_ctx) sws_freeContext(sws_ctx);
    if (yuv420p_frame) av_frame_free(&yuv420p_frame);
    av_frame_free(&decoded_frame);
    avcodec_close(codec_ctx);
    avcodec_free_context(&codec_ctx);
    return 0;
}

额外提示

  • 提前指定输出格式:你可以在打开解码器前,设置codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P,尝试让解码器直接输出YUV420P,这样就不用做格式转换了(大部分H.264软件解码器都支持这个设置)。
  • NVENC的兼容性:NVENC编码的H.264完全遵循H.264标准,所以不管是用FFmpeg的自带解码器还是libx264的解码器,都能正常解码,和解码端有没有NVIDIA硬件无关。

内容的提问来源于stack exchange,提问作者M. Ying

火山引擎 最新活动