FFmpeg 库无法正确检测 VP9 视频流的像素格式

问题描述 投票:0回答:1

我正在使用 C 代码检测 WebM 容器中 VP9 视频流的像素格式。 FFmpeg 版本 6.0,完整的共享库构建,从官方网站下载。操作系统是 Windows 10。我给图书馆提供了一个用 alpha 通道编码的 VP9 视频,像素格式是YUVA420p。它检测像素格式为YUV420p.

我在 StackOverflow.com 上发现了一个类似的问题,有没有办法强制 FFMPEG 从使用 libvpx-vp9 编码的 WebM 视频解码带有 alpha 的视频流?,但它实际上没有帮助。

当我用

libvpx
覆盖解码器时,它会继续检测像素格式为 YUV420p 而不是 YUVA420p

C代码如下。请注意,为了缩短 StackOverflow 问题,此处省略了代码中的错误处理。

AVFormatContext *fmt_ctx = NULL;
int err = avformat_open_input(&fmt_ctx, infp, NULL, NULL);
err = avformat_find_stream_info(fmt_ctx, NULL);
int stream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
AVCodecParameters *codecpar = fmt_ctx->streams[stream]->codecpar;

const AVCodec *codec = NULL;
if (codecpar->codec_id == AV_CODEC_ID_VP9) {
    codec = avcodec_find_decoder_by_name(CODEC_LIBVPX_VP9);
} else {
    codec = avcodec_find_decoder(codecpar->codec_id);
}

AVCodecContext *ctx = avcodec_alloc_context3(codec);
err = avcodec_parameters_to_context(ctx, codecpar);
av_log(NULL, AV_LOG_DEBUG, "Pixel format: %d.\n", ctx->pix_fmt); //TODO:DEBUG.
err = avcodec_open2(ctx, codec, NULL);

程序告诉

Pixel format: 0.
,意思是
AV_PIX_FMT_YUV420P
,不是
AV_PIX_FMT_YUVA420P

如果我手动覆盖像素格式,我能够解码带有 alpha 通道的视频并看到透明背景,但这打破了逻辑,因为当真正的 YUV420p 像素格式出现并被 YUVA420p 覆盖时,这将是一个问题.

if (codecpar->codec_id == AV_CODEC_ID_VP9) {
    if (strcmp(codec->name, CODEC_LIBVPX_VP9) == 0) {
        if (ctx->pix_fmt == AV_PIX_FMT_YUV420P) {
            ctx->pix_fmt = AV_PIX_FMT_YUVA420P;
        }
    }
}

同时使用 libvpx 解码器从命令行启动的 ffmpeg 工具表明我的视频具有

YUVA420p
像素格式。输出如下。

D:\Temp\4>ffmpeg -c:v libvpx-vp9 -i yuva.webm
ffmpeg version 6.0-full_build-www.gyan.dev Copyright (c) 2000-2023 the FFmpeg developers
  built with gcc 12.2.0 (Rev10, Built by MSYS2 project)
  configuration: --enable-gpl --enable-version3 --enable-shared --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libaribb24 --enable-libdav1d --enable-libdavs2 --enable-libuavs3d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libaom --enable-libjxl --enable-libopenjpeg --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va --enable-dxva2 --enable-libvpl --enable-libshaderc --enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
  libavutil      58.  2.100 / 58.  2.100
  libavcodec     60.  3.100 / 60.  3.100
  libavformat    60.  3.100 / 60.  3.100
  libavdevice    60.  1.100 / 60.  1.100
  libavfilter     9.  3.100 /  9.  3.100
  libswscale      7.  1.100 /  7.  1.100
  libswresample   4. 10.100 /  4. 10.100
  libpostproc    57.  1.100 / 57.  1.100
[libvpx-vp9 @ 000001ecdf6002c0] v1.13.0-71-g45dc0d34d
    Last message repeated 1 times
Input #0, matroska,webm, from 'yuva.webm':
  Metadata:
    ENCODER         : Lavf60.3.100
  Duration: 00:00:05.55, start: 0.000000, bitrate: 227 kb/s
  Stream #0:0: Video: vp9 (Profile 0), yuva420p(tv, progressive), 1920x1080, SAR 1:1 DAR 16:9, 60 fps, 60 tbr, 1k tbn
    Metadata:
      alpha_mode      : 1
      ENCODER         : Lavc60.3.100 libvpx-vp9
      DURATION        : 00:00:05.550000000
At least one output file must be specified

这是我在控制台输出末尾的第一个视频流中的 YUVA420p:

Stream #0:0: Video: vp9 (Profile 0), yuva420p(tv, progressive), 1920x1080, SAR 1:1 DAR 16:9, 60 fps, 60 tbr, 1k tbn

问题如下。

  1. 如何使用 C 代码中的 FFmpeg 库可靠地检测 VP9 视频的真实像素格式?
  2. 为什么即使将编解码器覆盖为 libvpx,C 代码也没有检测到实际的像素格式?

谢谢。

ffmpeg format pixel transparency vp9
1个回答
0
投票

感谢 Gyan,我最终得到了以下解决方案,由两种方法(功能)组成。错误处理是隐藏的。

errno_t prepare_decoder_normal(
                               AVFormatContext **fmt_ctx,  // Output parameter.
                               AVCodec **codec,            // Output parameter.
                               AVCodecContext **ctx,       // Output parameter.
                               int *stream,                // Output parameter.
                               const char *infp,           // Input parameter.
                               int si,                     // Input parameter.
                               int pix_fmt_id)             // Input parameter.
{
    *fmt_ctx = avformat_alloc_context(); // [!] -> avformat_free_context().
    
    errno_t err = avformat_open_input(fmt_ctx, infp, NULL, NULL);
    
    err = avformat_find_stream_info(*fmt_ctx, NULL);
    

    if (si >= 0) { // Stream index override.
        *stream = si;
    } else {
        *stream = av_find_best_stream(*fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); // Video stream index.
    }

    AVCodecParameters *codecpar = (*fmt_ctx)->streams[*stream]->codecpar;
    
    if (codecpar->codec_id == AV_CODEC_ID_VP9) {
        // VP9 requires a non-standard approach for decoding.
        avformat_close_input(fmt_ctx);
        return prepare_decoder_vp9(fmt_ctx, codec, ctx, infp, pix_fmt_id, *stream);
    } else {
        *codec = (AVCodec *) avcodec_find_decoder(codecpar->codec_id);
    }

    *ctx = avcodec_alloc_context3(*codec); // [!] The resulting struct should be freed with avcodec_free_context().
    
    err = avcodec_parameters_to_context(*ctx, codecpar);
    
    // Unfortunately, FFmpeg library may see a pixel format incorrectly.
    // Here we provide a method to override the automatically selected pixel
    // format.
    if (pix_fmt_id >= 0) {
        (*ctx)->pix_fmt = pix_fmt_id;
        av_log(NULL, AV_LOG_INFO, "Overriding pixel format ID with: %d.\n", pix_fmt_id);
    }

    err = avcodec_open2(*ctx, *codec, NULL);

    return SUCCESS;
}
errno_t prepare_decoder_vp9(
                            AVFormatContext **fmt_ctx,  // Output parameter.
                            AVCodec **codec,            // Output parameter.
                            AVCodecContext **ctx,       // Output parameter.
                            const char *infp,           // Input parameter.
                            int pix_fmt_id,             // Input parameter.
                            int stream)                 // Input parameter.
{
    *fmt_ctx = avformat_alloc_context(); // [!] -> avformat_free_context().

    *codec = (AVCodec *) avcodec_find_decoder_by_name(CODEC_LIBVPX_VP9);
    
    (*fmt_ctx)->video_codec = *codec;

    int err = avformat_open_input(fmt_ctx, infp, NULL, NULL); // [!] The stream must be closed with avformat_close_input().

    err = avformat_find_stream_info(*fmt_ctx, NULL);

    AVCodecParameters *codecpar = (*fmt_ctx)->streams[stream]->codecpar;

    *ctx = avcodec_alloc_context3(*codec); // [!] The resulting struct should be freed with avcodec_free_context().

    err = avcodec_parameters_to_context(*ctx, codecpar);

    // Unfortunately, FFmpeg library may see a pixel format incorrectly.
    // Here we provide a method to override the automatically selected pixel
    // format.
    if (pix_fmt_id >= 0) {
        (*ctx)->pix_fmt = pix_fmt_id;
        av_log(NULL, AV_LOG_INFO, "Overriding pixel format ID with: %d.\n", pix_fmt_id);
    }

    err = avcodec_open2(*ctx, *codec, NULL);
    
    return SUCCESS;
}
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.