Руководство по FFmpeg libav. Синхронизация аудио и видео

Добавлено 26 февраля 2022 в 23:25

Продолжение серии статей об основах работы с FFmpeg libav.

Прежде чем мы перейдем к написанию кода для примера перекодирования, давайте поговорим о времени или о том, как видеоплеер определяет правильное время для воспроизведения кадра.

В предыдущем примере мы сохранили несколько кадров, которые можно увидеть ниже:

кадр 0
кадр 1
кадр 2
кадр 3
кадр 4
кадр 5

Когда мы разрабатываем видеоплеер, нам нужно воспроизводить каждый кадр в заданном темпе, иначе было бы трудно комфортно смотреть видео из-за того, что оно воспроизводится очень быстро или очень медленно.

Поэтому нам нужно ввести некоторую логику для плавного воспроизведения каждого кадра. В этом отношении каждый кадр имеет временную метку представления (воспроизведения) (PTS, presentation timestamp), которая представляет собой число, возрастающее с учетом временной базы (timebase), которая является дробью (где знаменатель – временной масштаб, timescale), и частоты кадров (fps). Таким образом, значение PTS между кадрами будет увеличиваться на значение timescale/fps.

Это будет легче понять на примерах. Давайте смоделируем несколько сценариев.

Для fps = 60/1 и timebase = 1/60000 каждая метка PTS будет увеличиваться на timescale/fps = 1000, поэтому реальное время PTS для каждого кадра может быть следующим (предположим, что оно началось с 0):

  • кадр = 0, PTS = 0, PTS_TIME = 0
  • кадр = 1, PTS = 1000, PTS_TIME = PTS * timebase = 0,016
  • кадр = 2, PTS = 2000, PTS_TIME = PTS * timebase = 0,033

Практически для такого же сценария, но с временной базой равной 1/60.

  • кадр = 0, PTS = 0, PTS_TIME = 0
  • кадр = 1, PTS = 1, PTS_TIME = PTS * timebase = 0,016
  • кадр = 2, PTS = 2, PTS_TIME = PTS * timebase = 0,033
  • кадр = 3, PTS = 3, PTS_TIME = PTS * timebase = 0,050

Для fps = 25/1 и timebase = 1/75 каждая метка PTS будет увеличиваться timescale/fps = 3, и время PTS может быть следующим:

  • кадр = 0, PTS = 0, PTS_TIME = 0
  • кадр = 1, PTS = 3, PTS_TIME = PTS * timebase = 0,04
  • кадр = 2, PTS = 6, PTS_TIME = PTS * timebase = 0,08
  • кадр = 3, PTS = 9, PTS_TIME = PTS * timebase = 0,12
  • ...
  • кадр = 24, PTS = 72, PTS_TIME = PTS * timebase = 0,96
  • ...
  • кадр = 4064, PTS = 12192, PTS_TIME = PTS * timebase = 162,56

Теперь с помощью pts_time мы можем найти способ синхронизировать видео с pts_time аудиосигнала или с системными часами. FFmpeg libav предоставляет эту информацию через свой API:

Просто из любопытства, кадры, которые мы сохранили, были отправлены в порядке DTS (кадры: 1,6,4,2,3,5), но воспроизведены в порядке PTS (кадры: 1,2,3,4,5). Также обратите внимание, насколько дешевы B-кадры по сравнению с P или I-кадрами.

LOG: AVStream->r_frame_rate 60/1
LOG: AVStream->time_base 1/60000
...
LOG: Frame 1 (type=I, size=153797 bytes) pts 6000 key_frame 1 [DTS 0]
LOG: Frame 2 (type=B, size=8117 bytes) pts 7000 key_frame 0 [DTS 3]
LOG: Frame 3 (type=B, size=8226 bytes) pts 8000 key_frame 0 [DTS 4]
LOG: Frame 4 (type=B, size=17699 bytes) pts 9000 key_frame 0 [DTS 2]
LOG: Frame 5 (type=B, size=6253 bytes) pts 10000 key_frame 0 [DTS 5]
LOG: Frame 6 (type=P, size=34992 bytes) pts 11000 key_frame 0 [DTS 1]

Теги

FFmpeglibavДекодирование видеоОбработка аудиоОбработка видеоПрограммированиеСинхронизация аудио и видео

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.