【发布时间】:2020-03-05 14:30:09
【问题描述】:
上下文
我正在尝试使用 H.264 附件 B 编解码器从 MPEG-2 传输流中提取每个 I 帧的原始图像数据。该视频包含每 2 秒间隔的 I 帧。我读过可以在类型为 5 的 NALu 开始代码之后找到 I 帧(例如 IDR 图片的编码切片)。这些 NALu 的字节有效载荷包含构建完整帧所需的所有数据。尽管我的理解是采用 H.264 编码格式。
我想构建一个解决方案来从传入的字节流中提取这些 I 帧,方法是找到包含 I 帧的 NALu,保存有效负载并将有效负载解码为一些普遍存在的原始图像格式以访问像素数据等。
注意:如果可能,我想避免使用像 ffmpeg 这样的文件系统依赖二进制文件,如果可行,更重要的是!
概念验证
到目前为止,我已经在 rust 中构建了一个 PoC 来查找字节偏移量和 I 帧的字节大小:
use std::fs::File;
use std::io::{prelude::*, BufReader};
extern crate image;
fn main() {
let file = File::open("vodpart-0.ts").unwrap();
let reader = BufReader::new(file);
let mut idr_payload = Vec::<u8>::new();
let mut total_idr_frame_count = 0;
let mut is_idr_payload = false;
let mut is_nalu_type_code = false;
let mut start_code_vec = Vec::<u8>::new();
for (pos, byte_result) in reader.bytes().enumerate() {
let byte = byte_result.unwrap();
if is_nalu_type_code {
is_idr_payload = false;
is_nalu_type_code = false;
start_code_vec.clear();
if byte == 101 {
is_idr_payload = true;
total_idr_frame_count += 1;
println!("Found IDR picture at byte offset {}", pos);
}
continue;
}
if is_idr_payload {
idr_payload.push(byte);
}
if byte == 0 {
start_code_vec.push(byte);
continue;
}
if byte == 1 && start_code_vec.len() >= 2 {
if is_idr_payload {
let payload = idr_payload.len() - start_code_vec.len() + 1;
println!("Previous NALu payload is {} bytes long\n", payload);
save_image(&idr_payload.as_slice(), total_idr_frame_count);
idr_payload.clear();
}
is_nalu_type_code = true;
continue;
}
start_code_vec.clear();
}
println!();
println!("total i frame count: {}", total_idr_frame_count);
println!();
println!("done!");
}
fn save_image(buffer: &[u8], index: u16) {
let image_name = format!("image-{}.jpg", index);
image::save_buffer(image_name, buffer, 858, 480, image::ColorType::Rgb8).unwrap()
}
结果如下:
Found IDR picture at byte offset 870
Previous NALu payload is 202929 bytes long
Found IDR picture at byte offset 1699826
Previous NALu payload is 185069 bytes long
Found IDR picture at byte offset 3268686
Previous NALu payload is 145218 bytes long
Found IDR picture at byte offset 4898270
Previous NALu payload is 106114 bytes long
Found IDR picture at byte offset 6482358
Previous NALu payload is 185638 bytes long
total i frame count: 5
done!
这是正确的,基于我使用 H.264 比特流查看器等的研究。在这些字节偏移处肯定有 5 个 I 帧!
问题是我不明白如何从 H.264 字节流有效负载转换为原始图像 RBG 数据格式。转换为 jpg 后生成的图像只是一个模糊的混乱,占据了大约 10% 的图像区域。
例如:
问题
- 是否需要执行解码步骤?
- 我是否正确地处理了这个问题,我自己尝试这样做是否可行,还是应该依赖另一个库?
任何帮助将不胜感激!
【问题讨论】:
标签: image rust frame h.264 mpeg