Синхронизируйте аудио/видео в MP4 с помощью библиотеки AutoGen FFmpeg

В настоящее время у меня возникают проблемы с синхронизацией аудио- и видеопотоков.

Это AVCodecContexts, которые я использую:

Для видео:

AVCodec* videoCodec = ffmpeg.avcodec_find_encoder(AVCodecID.AV_CODEC_ID_H264)
AVCodecContext* videoCodecContext = ffmpeg.avcodec_alloc_context3(videoCodec);
videoCodecContext->bit_rate = 400000;
videoCodecContext->width = 1280;
videoCodecContext->height = 720;
videoCodecContext->gop_size = 12;
videoCodecContext->max_b_frames = 1;
videoCodecContext->pix_fmt = videoCodec->pix_fmts[0];
videoCodecContext->codec_id = videoCodec->id;
videoCodecContext->codec_type = videoCodec->type;
videoCodecContext->time_base = new AVRational
{
    num = 1,
    den = 30
};

Для аудио:

AVCodec* audioCodec = ffmpeg.avcodec_find_encoder(AVCodecID.AV_CODEC_ID_AAC)
AVCodecContext* audioCodecContext = ffmpeg.avcodec_alloc_context3(audioCodec);
audioCodecContext->bit_rate = 1280000;
audioCodecContext->sample_rate = 48000;
audioCodecContext->channels = 2;
audioCodecContext->channel_layout = ffmpeg.AV_CH_LAYOUT_STEREO;
audioCodecContext->frame_size = 1024;
audioCodecContext->sample_fmt = audioCodec->sample_fmts[0];
audioCodecContext->profile = ffmpeg.FF_PROFILE_AAC_LOW;
audioCodecContext->codec_id = audioCodec->id;
audioCodecContext->codec_type = audioCodec->type;

При записи видеокадров я устанавливаю положение PTS следующим образом:

outputFrame->pts = frameIndex;  // The current index of the image frame being written

Затем я кодирую кадр с помощью avcodec_encode_video2(). После этого я вызываю следующее для установки меток времени:

ffmpeg.av_packet_rescale_ts(&packet, videoCodecContext->time_base, videoStream->time_base);

Это отлично играет.

Однако, когда я делаю то же самое для звука, видео воспроизводится в замедленном режиме, сначала воспроизводится звук, а затем продолжается воспроизведение видео без звука.

Я нигде не могу найти пример того, как установить позиции pts/dts для видео/аудио в файле MP4. Любые примеры помощи были бы замечательными!

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

Я загрузил тестовое видео, чтобы показать свои результаты здесь: http://www.filedropper.com/test_124


person williamtroup    schedule 05.07.2016    source источник
comment
неправильный тег, это должно быть С++   -  person    schedule 05.07.2016
comment
Я использую библиотеку AutoGen, которая использует Invoke в С# для доступа к библиотекам!   -  person williamtroup    schedule 05.07.2016
comment
Я не использую API FFmpeg, только скомпилированный .exe как процесс (стандартный ввод/вывод). К сожалению, я не могу протестировать ваш код, но... Будем надеяться, что совет в моем ответе может быть вам чем-то полезен.   -  person VC.One    schedule 06.07.2016


Ответы (2)


PS: ознакомьтесь с этой статьей/руководством по Синхронизация аудио/видео с FFmpeg. Это может помочь вам, если ниже не поможет.

1) Что касается временных меток видео и аудио...

Вместо того, чтобы использовать текущую frameIndex в качестве метки времени, а затем изменить масштаб их. Если возможно, просто пропустите масштабирование.

В качестве альтернативы можно было бы убедиться, что значения PTS (в outputFrame->pts) созданы правильно, в первую очередь, с помощью видеокадров в секунду (FPS). Сделать это...

Для каждого видеокадра : outputFrame->pts = (1000 / FPS) * frameIndex;
(Для видео с частотой 30 кадров в секунду 1-й кадр имеет 0 времени, а к 30-му кадру «часы» достигли 1 секунды.
Таким образом, 1000/30 теперь дает каждый видеокадр представляет собой интервал представления 33,333 мс. Когда frameIndex равно 30, мы можем сказать, что 33,333 x 30 = 1000 мс (или 1 секунда, что подтверждает 30 кадров в секунду).

Для каждого аудиокадра: outputFrame->pts = ((1024 / 48000) * 1000) * frameIndex;
(поскольку кадр AAC 48 кГц имеет продолжительность 21,333 мс, отметка времени увеличивается на это время. Формула: (1024 PCM / SampleRate) x 1000 мс/сек), затем умножить на индекс кадра).

2) Что касается настроек звука...

Битрейт:
audioCodecContext->bit_rate = 64000; кажется странным, если ваш sample_rate равен 48000 Гц (и я предполагаю, что ваша битовая глубина составляет 16 бит на сэмпл?).

Попробуйте 96000 или 128000 в качестве самых низких начальных значений.

Размер кадра:

int AVCodecContext::frame_size означает "Количество выборок на канал в аудиокадре".

Учитывая приведенную выше цитату из Документов и то, что MPEG AAC не делает «на канал» (поскольку данные для обоих каналов L/R содержатся в каждом кадре). Каждый кадр AAC содержит 1024 выборки PCM.

audioCodecContext->frame_size = 88200; для размера, вы можете попробовать = 1024;

Профиль
Я заметил, что вы использовали MAIN для профиля AAC. Я привык видеть Low Complexity в видео. Я попробовал несколько случайных файлов MP4 из разных источников на моем жестком диске, и я не могу найти ни одного, используя профиль «Основной». В крайнем случае не помешает тестирование «низкой сложности».

Попробуйте использовать audioCodecContext->profile = ffmpeg.FF_PROFILE_AAC_LOW;

PS: проверьте это для возможная проблема с AAC (в зависимости от вашей версии FFmpeg).

person VC.One    schedule 06.07.2016
comment
Упс, забыл сказать о временных метках аудио/видео. Надеюсь, поможет. - person VC.One; 07.07.2016
comment
Это очень полезный ответ. Нужно попробовать и уйти оттуда. - person williamtroup; 07.07.2016
comment
outputFrame-›pts = (1000 / FPS) * frameIndex (для видеокадров) заставляет 19-секундное видео воспроизводиться быстро за 1 секунду. - person williamtroup; 07.07.2016
comment
Можете ли вы предоставить временную ссылку на образец видеофайла? Я попробую проверить байты (чтобы найти значение фиксации). Попробуйте использовать 44 кГц и 128 битрейт. FFmpeg не создаст файл, когда я использую ваши настройки, но по умолчанию он автоматически работает нормально с видео, используя частоту дискретизации 44100 + глубину 16 бит + скорость передачи 128 кбит / с. - person VC.One; 09.07.2016
comment
Извините за задержку с ответом. Я загрузил видео сюда: filedropper.com/test_124 - person williamtroup; 11.07.2016

Решил проблему. Я добавил новую функцию для установки позиций видео/аудио после установки позиций кадров PTS.

Видео — это просто обычное приращение (+1 для каждого кадра), тогда как звук делается следующим образом:

outputFrame->pts = ffmpeg.av_rescale_q(m_audioFrameSampleIncrement, new AVRational { num = 1, den = 48000 }, m_audioCodecContext->time_base);

m_audioFrameSampleIncrement += outputFrame->nb_samples;

После того, как кадр закодирован, я вызываю свою новую функцию:

private static void SetPacketProperties(ref AVPacket packet, AVCodecContext* codecContext, AVStream* stream)
{
    packet.pts = ffmpeg.av_rescale_q_rnd(packet.pts, codecContext->time_base, stream->time_base, AVRounding.AV_ROUND_NEAR_INF | AVRounding.AV_ROUND_PASS_MINMAX);
    packet.dts = ffmpeg.av_rescale_q_rnd(packet.dts, codecContext->time_base, stream->time_base, AVRounding.AV_ROUND_NEAR_INF | AVRounding.AV_ROUND_PASS_MINMAX);
    packet.duration = (int)ffmpeg.av_rescale_q(packet.duration, codecContext->time_base, stream->time_base);
    packet.stream_index = stream->index;
}
person williamtroup    schedule 12.07.2016