我使用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;
}
}
}
同时ffmpeg工具从libvpx解码器的命令行启动,告诉我的视频有YUVA420p
像素格式。输出如下:
D:Temp4>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
<标题>问题如下:
- 如何用C代码可靠地检测VP9视频的真实像素格式?
- 为什么C代码不检测实际的像素格式,即使编解码器覆盖libvpx ?
谢谢。
标题>感谢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;
}