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