Руководство по 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]