Наши партнеры

UnixForum





Библиотека сайта 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 предоставляет механизм синхронизации.

Фреймворк 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 и различные значения времени
Таймер конвейера фреймворка 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 посредством шины сообщений. После этого приложение сможет принять решение о том, стоит ли отправлять запрос для определения времени задержки с последующей рассылкой нового значения времени задержки или нет. Изменение времени задержки конвейера может привести к визуальным или звуковым искажениям и, следовательно, должно выполняться приложением только тогда, когда это разрешено.


Следующий раздел : Буферизация