Руководство по FFmpeg libav. Синхронизация аудио и видео
Продолжение серии статей об основах работы с FFmpeg libav.
Прежде чем мы перейдем к написанию кода для примера перекодирования, давайте поговорим о времени или о том, как видеоплеер определяет правильное время для воспроизведения кадра.
В предыдущем примере мы сохранили несколько кадров, которые можно увидеть ниже:
|
|
|
|
|
|
Когда мы разрабатываем видеоплеер, нам нужно воспроизводить каждый кадр в заданном темпе, иначе было бы трудно комфортно смотреть видео из-за того, что оно воспроизводится очень быстро или очень медленно.
Поэтому нам нужно ввести некоторую логику для плавного воспроизведения каждого кадра. В этом отношении каждый кадр имеет временную метку представления (воспроизведения) (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:
- fps =
AVStream->avg_frame_rate - tbr =
AVStream->r_frame_rate - tbn =
AVStream->time_base
Просто из любопытства, кадры, которые мы сохранили, были отправлены в порядке 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]
