背景
我正在尝试从带有H.264 annex B编解码器的MPEG-2传输流中提取每个I帧的原始图像数据。该视频在每隔2秒的间隔内包含I帧。据我所知,可以在类型为5(例如IDR图片的编码切片)的NALu起始码后找到I帧。这些NALu的字节有效负载包含构建完整帧所需的所有数据。虽然对于我来说是以H.264编码格式。
我想要构建一个方案,从输入的字节流中提取这些I帧,通过查找包含I帧的NALu,保存有效负载并将有效负载解码为某种普遍的原始图像格式,以访问像素数据等。
注意:如有可能,我想避免使用类似于ffmpeg的依赖文件系统的二进制文件,并且更重要的是如果可行的话!
概念验证
到目前为止,我已经使用Rust构建了一个概念验证,以查找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%的图像区域。
例如:
问题
- 是否需要进行解码步骤?
- 我是否正确处理,并且自己尝试这样做是可行的,还是应该依赖于另一个库?
任何帮助都将不胜感激!