从 YUV420P 到 RGB 的 FFMPEG Api 转换产生奇怪的输出

Posted

技术标签:

【中文标题】从 YUV420P 到 RGB 的 FFMPEG Api 转换产生奇怪的输出【英文标题】:FFMPEG Api conversion from YUV420P to RGB produces strange output 【发布时间】:2021-11-06 11:43:06 【问题描述】:

我在 Rust 中使用 FFMPEG Api 从视频文件中获取 RGB 图像。

虽然有些视频可以正常工作并且我按预期恢复了帧,但有些视频不能正常工作。或者至少结果不是我预期的那样。

我在 Rust 中使用的代码:

ffmpeg::init().unwrap();

let in_ctx = input(&Path::new(source)).unwrap();
let input = in_ctx
    .streams()
    .best(Type::Video)
    .ok_or(ffmpeg::Error::StreamNotFound)?;

let decoder = input.codec().decoder().video()?;

let scaler = Context::get(
    decoder.format(),
    decoder.width(),
    decoder.height(),
    Pixel::RGB24,
    decoder.width(),
    decoder.height(),
    Flags::FULL_CHR_H_INT | Flags::ACCURATE_RND,
)?; // <--- Is basically sws_getContext

// later to get the actual frame
let mut decoded = Video::empty();
if self.decoder.receive_frame(&mut decoded).is_ok() 
    let mut rgb_frame = Video::empty();
    self.scaler.run(&decoded, &mut rgb_frame)?; // <--- Does sws_scale
    println!("Converted Pixel Format: ", rgb_frame.format() as i32);
    Ok(Some(rgb_frame))

这应该大致翻译成C:

// Get the context and video stream
SwsContext * ctx = sws_getContext(imgWidth, imgHeight,
                              imgFormat, imgWidth, imgHeight,
                              AV_PIX_FMT_RGB24, 0, 0, 0, 0);
sws_scale(ctx, decoded.data, decoded.linesize, 0, decoded.height, rgb_frame.data, rbg_frame.linesize);

就像我之前所说的,有时它可以正常工作,并且我会恢复预期的帧。但有时我会得到这样的结果: Weird result image

我将图像保存为 .ppm 文件,以便快速进行视觉比较。我使用了这种方法,它基本上将字节写入具有简单 .ppm 标头的文件:

fn save_file(frame: &Video, index: usize) -> std::result::Result<(), std::io::Error> 

    let mut file = File::create(format!("frame.ppm", index))?;
    file.write_all(format!("P6\n \n255\n", frame.width(), frame.height()).as_bytes())?;
    file.write_all(frame.data(0))?;
    Ok(())

在这里,您可以看到左侧的图像结果很好,而右侧的图像结果很差。 Comparison of the .ppm files

现在来回答这个问题:

为什么会这样。我测试了我这边的所有东西,唯一剩下的就是 ffmpeg 转换。 FFMPEG 似乎以不同的方式转换这两个测试文件,即使它报告 YUV420P 作为两者的格式。我无法弄清楚可能有什么区别......

这里是我使用的两个视频文件的信息:

好的视频文件:

General
Complete name                            : /mnt/smb/Snapchat-174933781.mp4
Format                                   : MPEG-4
Format profile                           : Base Media / Version 2
Codec ID                                 : mp42 (isom/mp42)
File size                                : 1.90 MiB
Duration                                 : 9 s 612 ms
Overall bit rate                         : 1 661 kb/s
Encoded date                             : UTC 2021-07-28 22:09:36
Tagged date                              : UTC 2021-07-28 22:09:36
eng                                      : -180.00

Video
ID                                       : 512
Format                                   : AVC
Format/Info                              : Advanced Video Codec
Format profile                           : High@L3.1
Format settings                          : CABAC / 1 Ref Frames
Format settings, CABAC                   : Yes
Format settings, Reference frames        : 1 frame
Format settings, GOP                     : M=1, N=30
Codec ID                                 : avc1
Codec ID/Info                            : Advanced Video Coding
Duration                                 : 9 s 598 ms
Bit rate                                 : 1 597 kb/s
Width                                    : 480 pixels
Height                                   : 944 pixels
Display aspect ratio                     : 0.508
Frame rate mode                          : Variable
Frame rate                               : 29.797 FPS
Minimum frame rate                       : 15.000 FPS
Maximum frame rate                       : 30.000 FPS
Color space                              : YUV
Chroma subsampling                       : 4:2:0
Bit depth                                : 8 bits
Scan type                                : Progressive
Bits/(Pixel*Frame)                       : 0.118
Stream size                              : 1.83 MiB (96%)
Title                                    : Snap Video
Language                                 : English
Encoded date                             : UTC 2021-07-28 22:09:36
Tagged date                              : UTC 2021-07-28 22:09:36
Color range                              : Full
colour_range_Original                    : Limited
Color primaries                          : BT.709
Transfer characteristics                 : BT.601
transfer_characteristics_Original        : BT.709
Matrix coefficients                      : BT.709
Codec configuration box                  : avcC

Audio
ID                                       : 256
Format                                   : AAC LC
Format/Info                              : Advanced Audio Codec Low Complexity
Codec ID                                 : mp4a-40-2
Duration                                 : 9 s 612 ms
Bit rate mode                            : Constant
Bit rate                                 : 62.0 kb/s
Channel(s)                               : 1 channel
Channel layout                           : C
Sampling rate                            : 44.1 kHz
Frame rate                               : 43.066 FPS (1024 SPF)
Compression mode                         : Lossy
Stream size                              : 73.3 KiB (4%)
Title                                    : Snap Audio
Language                                 : English
Encoded date                             : UTC 2021-07-28 22:09:36
Tagged date                              : UTC 2021-07-28 22:09:36

错误的视频文件:

General
Complete name                            : /mnt/smb/Snapchat-1989594918.mp4
Format                                   : MPEG-4
Format profile                           : Base Media / Version 2
Codec ID                                 : mp42 (isom/mp42)
File size                                : 2.97 MiB
Duration                                 : 6 s 313 ms
Overall bit rate                         : 3 948 kb/s
Encoded date                             : UTC 2019-07-11 06:43:04
Tagged date                              : UTC 2019-07-11 06:43:04
com.android.version                      : 9

Video
ID                                       : 1
Format                                   : AVC
Format/Info                              : Advanced Video Codec
Format profile                           : Baseline@L3.1
Format settings                          : 1 Ref Frames
Format settings, CABAC                   : No
Format settings, Reference frames        : 1 frame
Format settings, GOP                     : M=1, N=30
Codec ID                                 : avc1
Codec ID/Info                            : Advanced Video Coding
Duration                                 : 6 s 313 ms
Bit rate                                 : 3 945 kb/s
Width                                    : 496 pixels
Height                                   : 960 pixels
Display aspect ratio                     : 0.517
Frame rate mode                          : Variable
Frame rate                               : 29.306 FPS
Minimum frame rate                       : 19.767 FPS
Maximum frame rate                       : 39.508 FPS
Color space                              : YUV
Chroma subsampling                       : 4:2:0
Bit depth                                : 8 bits
Scan type                                : Progressive
Bits/(Pixel*Frame)                       : 0.283
Stream size                              : 2.97 MiB (100%)
Title                                    : VideoHandle
Language                                 : English
Encoded date                             : UTC 2019-07-11 06:43:04
Tagged date                              : UTC 2019-07-11 06:43:04
Color range                              : Limited
Color primaries                          : BT.709
Transfer characteristics                 : BT.709
Matrix coefficients                      : BT.709
Codec configuration box                  : avcC

或作为差异图像:image diff

问题是我对 ffmpeg 不太熟悉,但我不知道它的所有怪癖。

我希望有人能指出我正确的方向。

【问题讨论】:

嗨 fasc8,我会尝试使用编码宽度/高度。尝试将 sws_getContext 中的前两个参数(imgWidth,imgHeight)替换为 codecCtx->coded_width,codecCtx->coded_height。输出宽度/高度和线条大小似乎有问题。 好视频的宽度是32的倍数,但坏视频的宽度只有16的倍数。我怀疑坏视频有一些添加的填充使其成为倍数32,但您不考虑该填充。 【参考方案1】:

感谢@SuRGeoNix 和@Jmb 的建议,我玩弄了输入的线条大小和宽度。

过了一会儿,我了解到 ffmpeg 需要 32 位对齐的数据才能获得最佳性能。所以我将缩放器调整为 32 位对齐宽度,现在输出很好。

ffmpeg::init().unwrap();

let in_ctx = input(&Path::new(source)).unwrap();
let input = in_ctx
    .streams()
    .best(Type::Video)
    .ok_or(ffmpeg::Error::StreamNotFound)?;

let decoder = input.codec().decoder().video()?;

// Round to the next 32bit divisible width
let width = if decoder.width() % 32 != 0 
    decoder.width() + 32 - (decoder.width() % 32)
 else 
    decoder.width()
;

let scaler = Context::get(
    decoder.format(),
    decoder.width(),
    decoder.height(),
    Pixel::RGB24,
    width, // Use the calculated width here
    decoder.height(),
    Flags::FULL_CHR_H_INT | Flags::ACCURATE_RND,
)?;

【讨论】:

以上是关于从 YUV420P 到 RGB 的 FFMPEG Api 转换产生奇怪的输出的主要内容,如果未能解决你的问题,请参考以下文章

这个 YUV420P 到 RGB 着色器的转换从何而来?

ffmpeg YUV420 转 RGB24 只转换一行

视频学习笔记:Android OpenGL渲染YUV420P图像

来自不同平台/硬件的不同 GL 着色器结果(YUV 到 RGB)

yuv420p 转rgb计算

基于FFmpeg的视频播放器之五:使用SDL2渲染yuv420p