Библиотека сайта rus-linux.net
Фреймворк GStreamer. Руководство разработчика приложений. Таймеры и синхронизация в фреймворке GStreamer
Оригинал: GStreamer Application Development ManualАвторы: Wim Taymans, Steve Baker, Andy Wingo, Ronald S. Bultje, Stefan Kost
Дата публикации: 21 мая 2014 г.
Перевод: А.Панин
Дата перевода: 20 июня 2014 г.
Глава 14. Таймеры и синхронизация в фреймворке GStreamer
При проигрывании сложных мультимедийных потоков каждый сэмпл из аудиопотока и кадр из видеопотока должен воспроизводиться в определенной последовательности в определенное время. Для этой цели фреймворк GStreamer предоставляет механизм синхронизации.
- Для проигрывания мультимедийных потоков из источников, не относящихся к источникам реального времени, со скоростью доступа к мультимедийным данным, превышающей скорость воспроизведения. Это именно тот случай, когда осуществляется чтение мультимедийных данных из файла и их синхронизированное проигрывание. В данном случае множество потоков, таких, как аудиопотоки, видеопотоки и потоки данных субтитров подлежит синхронизации.
- Для захвата и синхронизированного мультиплексирования/смешивания мультимедийных потоков из нескольких источников реального времени. Это типичная ситуация при записи аудио- и видеопотока с помощью микрофона/камеры и мультиплексировании этих потоков для сохранения в файл.
- Для приема сетевых (медленных) мультимедийных потоков с буферизацией. Это типичная ситуация при приеме мультимедийных потоков посредством сети, когда вы осуществляете доступ к передаваемому с сервера вещания посредством протокола http потоку.
- Для захвата мультимедийного потока из источника реального времени и проигрывания его с помощью устройства вывода реального времени с настраиваемым временем задержки. Этот подход используется, например, при захвате видеопотока с камеры, наложении эффекта и демонстрации результата. Также этот подход используется при передаче потока с малым временем задержки по сети посредством протокола UDP.
- Для одновременного захвата мультимедийного потока в реальном времени и проигрывания предварительно записанного потока. Данный подход используется при записи аудиопотоков, когда вы проигрываете предварительно записанные звуки и записываете новые сэмплы, добиваясь идеальной синхронизации между новыми и предварительно записанными аудиоданными.
В следующих разделах мы увидим, что фреймворк GStreamer использует объект таймера типа GstClock
, метки времени буферов и события EVENT для синхронизации потоков в рамках конвейера.
14.1. Время таймера
В обычном компьютере существует множество устройств, которые могут использоваться в качестве источников времени, например, системный таймер, звуковые карты, счетчики производительности центрального процессора, ... По этой причине в рамках фреймворка GStreamer доступно множество реализаций класса таймера GstClock
. Время таймера не всегда отсчитывается от 0 или от какого-либо известного значения. Некоторые таймеры начинают отсчет с момента последней перезагрузки и.т.д...
GstClock
возвращает значение абсолютного времени (absolute-time) соответствующего таймера в случае использования функции gst_clock_get_time ()
. Абсолютное время таймера (или время таймера) монотонно возрастает. На основе абсолютного времени вычисляется затраченное время (running-time), которое является всего лишь разностью между текущим и предыдущим значением абсолютного времени, называемым базовым временем (base-time). Таким образом:
затраченное время = абсолютное время - базовое время
Объект конвейера фреймворка GStreamer типа GstPipeline
поддерживает существование объекта таймера типа GstClock
и производит расчет базового времени при переходе в состояние "проигрывается" (PLAYING). Конвейер передает ссылку на выбранный объект таймера типа GstClock
каждому своему элементу вместе с выбранным значением базового времени. При этом конвейер определяет базовое время таким образом, что затраченное время отражает все время, ушедшее на работу в состоянии "проигрывается" (PLAYING). В результате после перехода конвейера в состояние "пауза" (PAUSED) затраченное время не изменяется.
Благодаря тому, что все объекты конвейера используют один и тот же таймер, а также одно и то же значение базового времени, они имеют возможность рассчитывать затраченное время в соответствии с временем таймера конвейера.
14.2. Время буфера
Для расчета времени, затраченного на работу с буфером, нам потребуется метка времени буфера и событие SEGMENT, которое предшествует заполнению буфера. В первую очередь мы должны преобразовать событие SEGMENT в объект типа GstSegment
, после чего мы сможем использовать функцию gst_segment_to_running_time ()
для расчета времени, затраченного на работу с буфером.
Теперь вопрос синхронизация заключается в гарантии того, что проигрывание буфера, на работу с которым было затрачено определенное время, будет завершено в момент, когда значение таймера будет равно значению затраченного на работу с этим буфером времени. При выводе данных также следует учитывать значение заданного на уровне конвейера времени задержки и добавлять это значение к значению времени, затраченного на работу с буфером, перед осуществлением синхронизации с таймером конвейера.
Источники данных, не относящиеся к источникам реального времени, устанавливают метки времени буферов с учетом того, что затраченное время отсчитывается от значения 0. После изменения позиции в потоке с опустошением буферов отсчет затраченного времени также начнется с значения 0.
Источники данных реального времени должны устанавливать значения меток времени буферов, равные значениям затраченного времени конвейера в момент записи первого байта в буфер.
14.3. Время потока в буфере
Время потока в буфере, также известное как позиция в потоке, рассчитывается на основе метки времени буфера и данных предшествовавшего его заполнению события SEGMENT. Оно отражает время в рамках мультимедийного потока и является значением из диапазона от 0 до значения времени полной продолжительности мультимедийного потока.
- Сообщения о текущей позиции в потоке при использовании запроса POSITION.
- Вычисления позиции в потоке, используемой при работе с запросами и событиями перемещения в потоке.
- Вычисления позиции в потоке, используемой при синхронизации контролируемых значений.
Время потока никогда не используется для синхронизации потоков, так как эта операция осуществляется исключительно с использованием значения затраченного времени конвейера.
14.4. Обзор типов времени
В данном разделе приведен обзор различных шкал времени, используемых в рамках фреймворка GStreamer.
На рисунке ниже изображены различные значения времени в рамках конвейера при проигрывании потока длительностью 100 мс с повторным проигрыванием части потока с 50 мс до 100 мс.
Рисунок 14.1. Таймер конвейера фреймворка GStreamer и различные значения времени
Вы можете увидеть, что затраченное время буфера постоянно монотонно возрастает вместе с временем таймера. Буферы проигрываются в момент, когда время, затрачиваемое на работу с ними, становится равно разности абсолютного времени таймера конвейера (clock-time) и базового времени (base-time). Время потока представляет позицию в потоке и уменьшается при повторном проигрывании.
14.5. Источники таймеров
Источник таймера является элементом конвейера, который может предоставить объект таймера типа GstClock
. Объект таймера должен сообщать абсолютное время, которое монотонно возрастает при нахождении элемента в состоянии "проигрывается" (PLAYING). Он может останавливать отсчет времени тогда, когда элемент находится в состоянии "пауза" (PAUSED).
Источники таймеров существуют потому, что они позволяют проигрывать мультимедийные потоки с какой-либо скоростью и эта скорость не обязательно равна скорости системного таймера. Например, звуковая карта может проигрывать звук с частотой дискретизации 44,1 kHz, но это абсолютно не значит, что именно по прошествии 1 секунды по системному таймеру звуковая карта проиграет 44.100 сэмплов. Это соотношение корректно только в случае аппроксимации. На самом деле, звуковое устройство имеет внутренний таймер, отсчитывающий время на основе количества проигранных сэмплов, значения которого мы можем использовать.
В том случае, если элементу с внутренним таймером потребуется выполнить синхронизацию, он должен будет рассчитать время таймера конвейера, которое будет соответствовать времени его внутреннего таймера. Для этого расчета он должен будет связать время своего внутреннего таймера с временем конвейера.
В том случае, если значения таймера конвейера полностью совпадают с значениями внутреннего таймера элемента, элемент может пропустить этап связывания значений таймеров и использовать напрямую таймер конвейера для планирования операций с мультимедийным потоком. Подобная привязка к таймеру может позволить производить как более быстрое, таки и более точное планирование операций. Следовательно, в общем случае такие элементы с внутренними таймерами, как элементы для ввода аудиоданных или элементы для вывода аудиоданных с помощью звуковых устройств будут выступать в роли источника таймера конвейера.
В момент, когда конвейер переходит в состояние "проигрывается" (PLAYING), он обходит все элементы от элемента вывода данных до элемента ввода данных и запрашивает у каждого элемента информацию о возможности предоставления им таймера. Последний элемент, который сможет предоставить таймер, будет использоваться в качестве источника таймера конвейера. Алгоритм выбора элемента отдает предпочтение таймеру из элемента вывода аудиоданных в обычном конвейере для проигрывания мультимедийных потоков и таймеру из элемента ввода данных в обычном конвейере для захвата мультимедийных потоков.
Существуют некоторые сообщения, которые позволяют вам получить информацию о таймере и источниках таймеров используемого конвейера. Вы можете получить информацию о таймере, который был выбран для конвейера, отслеживая и обрабатывая сообщение NEW_CLOCK, передаваемое посредством шины сообщений. В момент, когда источник таймера будет удален из состава конвейера, будет отправлено сообщение CLOCK_POST и приложение должно будет перейти в состояние "пауза" (PAUSED), а затем снова в состояние "проигрывается" (PLAYING) для выбора нового таймера.
14.6. Задержка
Задержка является временем, которое необходимо сэмплу из буфера с меткой времени захвата X для достижения элемента вывода данных. Это время измеряется с помощью таймера конвейера. Для конвейеров, в которых единственными синхронизированными с таймером элементами являются элементы для вывода данных, задержка всегда равна 0, так как никакие другие элементы не задерживают обработку буфера.
Задержка возникает при работе с конвейерами для захвата данных по большей части из-за принципа работы источника данных реального времени. Рассмотрим аудиоустройство, которое начинает захват первого аудиосэмпла с меткой времени 0. В том случае, если устройство захватывает буферы с 44100 сэмплами за раз, используя частоту дискретизации 44100 Hz, оно соберет данные для буфера в первую секунду. Так как метка времени для буфера имеет значение 0, а время таймера на данный момент >= 1 секунде, элемент вывода данных отбросит буфер, так как посчитает его устаревшим. Без какой-либо компенсации задержки на уровне элемента вывода данных все буферы будут отброшены.
14.6.1. Компенсация задержки
Перед тем, как конвейер переходит в состояние "проигрывается" (PLAYING), он в дополнение к выбору таймера и расчету базового времени будет рассчитывать задержку конвейера. Эта задача выполняется с путем отправки запроса LATENCY всем элементам для вывода данных конвейера. После этого конвейер выберет максимальное значение задержки при работе с ним и использует событие LATENCY для соответствующей настройки элементов.
Все элементы для вывода данных будут задерживать проигрывание мультимедийного потока на время, соответствующее значению, переданному с помощью события LATENCY. Так как все элементы для вывода данных используют задержки на один и тот же промежуток времени, они будут синхронизированы друг с другом.
14.6.2. Динамические задержки
Добавление/удаление элементов конвейера или изменение свойств элемента может привести к изменению времени задержки конвейера. Элемент может запросить изменение значения времени задержки конвейера, отправив сообщение LATENCY посредством шины сообщений. После этого приложение сможет принять решение о том, стоит ли отправлять запрос для определения времени задержки с последующей рассылкой нового значения времени задержки или нет. Изменение времени задержки конвейера может привести к визуальным или звуковым искажениям и, следовательно, должно выполняться приложением только тогда, когда это разрешено.
Следующий раздел : Буферизация