从 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 转换产生奇怪的输出的主要内容,如果未能解决你的问题,请参考以下文章
视频学习笔记:Android OpenGL渲染YUV420P图像