Перекодирование видео Media Foundation для получения смещения синхронизации аудиопотока - PullRequest
2 голосов
/ 08 марта 2019

Я пытаюсь написать простой инструмент командной строки Windows Media Foundation для использования IMFSourceReader и IMFSyncWriter для загрузки видео, чтения видео и аудио в виде несжатых потоков и перекодирования их в H.246 /AAC с некоторыми жестко заданными настройками.

Простая программа Gist находится здесь

образец видео 1

образец видео 2

пример видео 3

(Примечание: видео, на которых я тестировал, все стерео, частота дискретизации 48000 КБ)

Программа работает, однако вВ некоторых случаях при сравнении вновь выведенного видео с оригиналом в программе редактирования я вижу, что скопированные видеопотоки совпадают, но аудиопоток копии предварительно фиксируется с некоторой тишиной, и звук смещается, что недопустимов моей ситуации.

audio samples:
original - |[audio1] [audio2] [audio3] [audio4] [audio5] ... etc
copy     - |[silence] [silence] [silence] [audio1] [audio2] [audio3] ... etc

В подобных случаях первые поступающие видеокадры имеют ненулевую метку времени, но первые аудиокадры имеют 0 метку.

Я хотел бы иметь возможность создавать скопированное видео, у которого первый кадр из потоков видео и аудио равен 0, поэтому я сначала попытался вычесть эту начальную временную метку (videoOffset) из всех последующих видеокадров, которые произвели видео, которое я хотел,но результатd в этой ситуации со звуком:

original - |[audio1] [audio2] [audio3] [audio4] [audio5] ... etc
copy     - |[audio4] [audio5] [audio6] [audio7] [audio8] ... etc

Звуковая дорожка теперь немного смещена в другом направлении и все еще не выравнивается.Это также может иногда происходить, когда видеопоток имеет начальную временную метку 0 , но WMF все равно обрезает некоторые аудиосэмплы в начале (см. Пример видео 3)!

У меня естьбыл в состоянии исправить это синхронизирующее выравнивание и сместить видеопоток, чтобы начать с 0 со следующим кодом, вставленным в точке передачи данных аудиосэмпла в IMFSinkWriter:

//inside read sample while loop
...

// LONGLONG llDuration has the currently read sample duration
// DWORD audioOffset has the global audio offset, starts as 0
// LONGLONG audioFrameTimestamp has the currently read sample timestamp

//add some random amount of silence in intervals of 1024 samples
static bool runOnce{ false };
if (!runOnce)
{
    size_t numberOfSilenceBlocks = 1; //how to derive how many I need!?  It's aribrary
    size_t samples = 1024 * numberOfSilenceBlocks; 
    audioOffset = samples * 10000000 / audioSamplesPerSecond;
    std::vector<uint8_t> silence(samples * audioChannels * bytesPerSample, 0);
    WriteAudioBuffer(silence.data(), silence.size(), audioFrameTimeStamp, audioOffset);

    runOnce= true;
}

LONGLONG audioTime = audioFrameTimeStamp + audioOffset;
WriteAudioBuffer(dataPtr, dataSize, audioTime, llDuration);

Как ни странно, это создаетвыходной видеофайл, который соответствует оригиналу.

original - |[audio1] [audio2] [audio3] [audio4] [audio5] ... etc
copy     - |[audio1] [audio2] [audio3] [audio4] [audio5] ... etc

Решением было вставить дополнительная тишина с размерами блоков 1024 в начале аудиопотока.Неважно, какие размеры аудиоблока предоставлены IMFSourceReader, отступы кратны 1024.

A screen shot of the audio track offsets of the different attempts for sample video 2

Моя проблема в том, что, похоже, не обнаруживаетсяпричина молчания смещена.Зачем мне это нужно?Как я знаю, сколько мне нужно?После нескольких дней борьбы с этой проблемой я наткнулся на решение с блоком тишины 1024.

Некоторые видео, похоже, нуждаются только в 1 блоке заполнения, некоторые в 2 или более, а некоторые вообще не нуждаются в дополнительном заполнении!

Мой вопрос здесь:

  • Кто-нибудь знает, почему это происходит?

  • Не правильно ли я использую Media Foundation в этой ситуации, чтобы вызвать это?

  • Если я прав, как я могу использовать метаданные видео дляопределить, нужно ли мне заполнить аудиопоток и сколько в нем должно быть 1024 блоков молчания?

РЕДАКТИРОВАТЬ:

Дляпримеры видео выше:

  • образец видео 1 : видеопоток начинается с 0 и не требует дополнительных блоков , работает с исходными даннымив порядке.

  • пример видео 2 : поток видео начинается с 834166 (hns) и требуется 1 1024 блок молчания для синхронизации

  • образец видео 3 : видеопоток начинается с 0 и требуется 2 1024 блоков молчания для синхронизации.

ОБНОВЛЕНИЕ:

Другие вещи, которые я пробовал:

  • Увеличение длительности первого видеокадра для учета смещения: Не дает эффекта.

1 Ответ

0 голосов
/ 10 марта 2019

Я написал другую версию вашей программы для корректной обработки формата NV12 (ваша не работала):

EncodeWithSourceReaderSinkWriter

Я использую Blender как инструмент для редактирования видео. Вот мои результаты с Tuning_against_a_window.mov:

enter image description here

снизу вверх:

  • Оригинальный файл
  • Закодированный файл
  • Я изменил исходный файл настройками «elst» атомов со значением 0 для числовых записей (я использовал гекса-редактор Visual Studio)

Как сказал Роман Р., источник MediaFoundation mp4 не использует атомы "edts / elst". Но Blender и ваши инструменты для редактирования видео делают. Также дорожка "tmcd" игнорируется источником mp4.

"edts / elst":

Edits Atom ('edts')

Редактировать списки можно использовать для подсказок ...

Источник файла MPEG-4

Источник файла MPEG-4 молча игнорирует дорожки подсказок.

Так что на самом деле кодировка хорошая. Я думаю, что нет смещения синхронизации аудиопотока, по сравнению с реальными аудио / видео данными. Например, вы можете добавить «edts / elst» в закодированный файл, чтобы получить тот же результат.

PS: в кодированном файле я добавил «edts / elst» для обеих аудио / видео дорожек. Я также увеличил размер для атомов trak и moov. Я подтверждаю, что Blender показывает одинаковую форму волны как для исходного, так и для закодированного файла.

EDIT

Я попытался понять связь между атомами mvhd / tkhd / mdhd / elst в 3-х видео-образцах (Да, я знаю, я должен прочитать спецификацию. Но я ленивый ...)

Вы можете использовать инструмент mp4 explorer для получения значений атомов или использовать анализатор mp4 из моего проекта H264Dxva2Decoder:

H264Dxva2Decoder

Tuning_against_a_window.mov

  • elst (медиа время) из тхд видео: 20689
  • elst (медиа время) от тхд аудио: 1483

GREEN_SCREEN_ANIMALS__ALPACA.mp4

  • elst (медиа время) из тхд видео: 2002
  • elst (медиа время) от тхд аудио: 1024

GOPR6239_1.mov

  • elst (медиа время) из тхд видео: 0
  • elst (медиа время) из тхд аудио: 0

Как вы можете видеть, в GOPR6239_1.mov время мультимедиа из elst равно 0. Поэтому с этим файлом нет проблем с синхронизацией видео / аудио.

Для Tuning_against_a_window.mov и GREEN_SCREEN_ANIMALS__ALPACA.mp4 я пытался вычислить смещение видео / аудио. Я изменил свой проект, чтобы учесть это:

EncodeWithSourceReaderSinkWriter

Пока я не нашел общий расчет для всех файлов.

Я просто нашел смещение видео / аудио, необходимое для правильного кодирования обоих файлов.

Для Tuning_against_a_window.mov я начинаю кодирование после (время фильма - видео / аудио время mdhd). Для GREEN_SCREEN_ANIMALS__ALPACA.mp4 я начинаю кодирование по истечении времени видео / аудио.

Все нормально, но мне нужно найти правильный уникальный расчет для всех файлов.

Итак, у вас есть 2 варианта:

  • кодировать файл и добавить Elst Atom
  • кодировать файл с помощью вычисления смещения вправо

это зависит от ваших потребностей:

  • Первый вариант позволяет вам сохранить исходный файл. Но вы должны добавить Elst Atom
  • При втором варианте вы должны прочитать атом из файла перед кодированием, и закодированный файл потеряет несколько оригинальных кадров

Если вы выберете первый вариант, я объясню, как я добавляю атом атома.

PS: меня интересует этот вопрос, потому что в моем проекте H264Dxva2Decoder атом edts / elst находится в моем списке задач. Я разбираю это, но я не использую это ...

PS2: эта ссылка звучит интересно: Audio Priming - Обработка задержки кодера в AAC

...