Контекст
Я пытаюсь извлечь необработанные данные изображения для каждого I-кадра из транспортного потока MPEG-2 с кодеком приложения B H.264. Это видео содержит I-кадры через каждые 2 секунды. Я читал, что I-кадр можно найти после начального кода NALu с типом 5 (например, закодированный фрагмент изображения IDR). Полезная нагрузка байтов этих NALU содержит все необходимые данные для создания полного кадра. Хотя, насколько я понимаю, в формате с кодировкой H.264.
Я хотел бы создать решение для извлечения этих I-кадров из входящего потока байтов, находя NALu, которые содержат I-кадры, сохраняя полезную нагрузку и декодируя полезную нагрузку в какой-то вездесущий необработанный формат изображения для доступа к данным пикселей и т. д.
Примечание. Я хотел бы избегать использования бинарных зависимостей файловой системы, таких как ffmpeg, если это возможно и, что более важно, если это возможно!
PoC
До сих пор я построил 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% площади изображения.
Например:
Вопросы
- Есть ли шаг декодирования, который необходимо выполнить?
- Правильно ли я подхожу к этому, и можно ли попробовать самому, или мне следует полагаться на другую библиотеку?
Любая помощь будет принята с благодарностью!