Извлечение необработанных данных изображения I-кадра из байтового потока транспортного потока MPEG-2 (H.264 - Приложение B) - PullRequest
2 голосов
/ 05 марта 2020

Контекст

Я пытаюсь извлечь необработанные данные изображения для каждого 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% площади изображения.

Например:

Ouput jpg image

Вопросы

  1. Есть ли шаг декодирования, который необходимо выполнить?
  2. Правильно ли я подхожу к этому и возможно ли это попробовать самостоятельно, или я должен полагаться на другого? lib?

Любая помощь будет принята с благодарностью!

1 Ответ

1 голос
/ 05 марта 2020

«Есть ли этап декодирования, который необходимо выполнить?»

Да. И написание декодера с нуля Крайне сложно. Документ, который описывает его (ISO 14496-10), имеет длину более 750 страниц. Вы должны использовать библиотеку. Libavcode c из ffmpeg - действительно единственный вариант. (Если вам не нужен только базовый профиль, в котором вы можете использовать декодер с открытым исходным кодом из android)

Вы можете скомпилировать собственную версию libavcode c, чтобы исключить ненужные вещи.

...