Знакомство с GStreamer: Источники данных

  • Tutorial
Здравствуй, хабраюзер, интересующийся фреймворком GStreamer! Сегодня мы поговорим о источниках медиа-данных (sources), и тем самым продолжим курс статей посвященный GStreamer.

Предыдущая статья: Знакомство с GStreamer: Введение.

Вступление



Источники данных — это класс плагинов GStreamer который позволяет читать медиаданные из различных источников, таких как файловая система или аудио-входы звуковой карты. Также, они позволяют получать медиапоток с различных серверов потокового вещания, такие как HTTP (ICECast, ShoutCast), RTSP, RTMP, TCP и UDP. А еще имеется возможность читать данные с DVB карт, CDDA-дисков (народе известных просто как «компакт-диски»), и еще много всего, при помощи различных плагинов, которых на данный момент около 30.
Примечание: как говорилось в прошлой статье, источники данных имеют только один pad с названием src, так как его можно подключить к другому элементу, но к нему подключить ничего нельзя.

В этой статье мы разберем некоторые (пожалуй, наиболее востребованные) источники данных, напишем немного кода на Python и узнаем много нового.

Поехали


0. gst-launch-1.0

Утилита gst-launch-1.0 позволяет запускать GStreamer pipeline без написания единой строчки кода. Я буду ее использовать для запуска небольших примеров, и что нам требуется знать сегодня — так это то, что запуск pipeline имеет примерно такой вид:
gst-launch-1.0 описание-pipeline

а описание pipeline, в свою очередь, делится на описание элементов вида
element1 property1=value1 property2=value2 ! element2 

Вроде в этой схеме все понятно, есть элемент типа element1 с свойствами property1 и property2 которые имеют значения value1 и value2 соответственно, и есть элемент типа element2. Символ «!» указывает на то, что выход element1 необходимо соединить с входом element2.
На этом возможности gst-launch не ограничиваются, просто в сегодняшней статье они нам не пригодятся и будут рассмотрены в дальнейшем.

1. filesrc

filesrc — на мой взгляд, это самый часто используемый источник мультимедийных данных. Как можно понять из его названия, он является элементом для чтения данных из файлов. В прошлой статье мы рассматривали схему примитивного плеера, в котором фигурировал filesrc как источник данных.
Элемент filesrc имеет несколько параметров. Рассмотрим некоторые из них.

location

Свойство location должно содержать путь к файлу, из которого необходимо производить чтение.
Пример: /foo/bar.mp3

blocksize

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

num-buffers

Количество блоков, после прочтения которых будет отправлено сообщение EOS.
Свойства blocksize и num-buffers можно не указывать. В этом случае будут использованы значения по умолчанию. А еще указанные свойства имеются у всех источников данных, и далее рассматриваться не будут.

Пример использования источника filesrc возьмем из предыдущей статьи:
gst-launch-1.0 filesrc location=/foo/bar.mp3 ! decodebin ! autoaudiosink

2. souphttpsrc

Элемент souphttpsrc предоставляет возможность чтения данных поверх HTTP (HTTPS) протокола. Чтение может производиться как из обычного файла, доступного через HTTP протокол, так и с ICECast/ShoutCast сервера. Также данный элемент позволяет производить чтение данных поверх протоколов ICY и ICYX. По данным протоколам я никакой информации найти не смог.
souphttpsrc имеет чуть больше параметров чем filesrc, рассмотрим наиболее важные.

location

Данный параметр аналогичен одноименному параметру элемента filesrc, за исключением того, что поддерживаются только схемы URI http, https, icy или icyx.

user-agent

Название говорит само за себя — данный параметр устанавливает, какой использовать User-Agent при отправке HTTP-запросов.

automatic-redirect

Устанавливает автоматический переход в случае, если сервер ответил, что ресурс был перемещен (статусы 301 и 302).

user-id и user-pw

Данные свойства устанавливают имя пользователя и пароль, на случай, если ресурс требует авторизации (HTTP Basic Authentication).

proxy, proxy-id и proxy-pw

Данные свойства устанавливают адрес прокси сервера, имя пользователя и пароль для авторизации (если необходимо). Поддерживаются HTTP и HTTPS прокси.

Использование источника souphttpsrc, ничем не отличается от использования источника filesrc:
gst-launch-1.0 souphttpsrc location=http://radio.localhost:1234 ! decodebin ! autoaudiosink

3. giosrc

В наборе плагинов GStreamer имеется интересный элемент giosrc. Это почти то же самое что и filesrc, только работает он через библиотеку GIO, и позволяет получать данные из различных источников с которыми может работать GIO. Таких источников 22, начиная от file, заканчивая mtp. Также, данный элемент поддерживает протоколы ftp, sftp, smb и webdav.
Данный элемент имеет параметры аналогичные элементу filesrc. Пример использования данного источника аналогичен использованию предыдущих:
gst-launch-1.0 giosrc location=ftp://ftp.localhost/foo/bar.mp3 ! decodebin ! autoaudiosink

4. rtspsrc

Как можно понять из названия, данный элемент позволяет читать данные с RTSP сервера. Подробно рассматривать свойства данного элемента не будем, скажу только то, что он имеет свойство location для указания адреса источника данных, proxy, proxy-id и proxy-pw для указания настроек прокси, и с десяток свойств специфичных для RTSP протокола, рассмотрение которого не входит в наши планы.
Но, у rtspsrc есть одна особенность, у него несколько pad-ов, и имеют они названия подходящие под шаблон source_%u. Это связано с тем, что сервер может транслировать несколько потоков одновременно, например, аудио и видео могут быть разбросаны по двум потокам.
Примечание:
Я не знаком c протоколом RTSP, поэтому я мог ошибиться в предположении выше.

Пример использования rtspsrc аналогичен предыдущим элементам:
gst-launch-1.0 rtspsrc location=rtsp://rtsp.localhost/movie.mp4 ! decodebin ! autoaudiosink

5. multifilesrc

Суть элемента multifilesrc такая же, как и у элемента filesrc — читать файлы с диска, но имеется одно отличие — multifilesrc читает файлы последовательно (1.mp3, 2.mp3...), в то время как filesrc может читать только по одному файлу. У данного элемента есть один недостаток — имена файлов должны различаться только числом, например, file-1.mp3, file-2.mp3 и так далее. Но такой недостаток можно списать в разряд фич, так как с помощью данного «недостатка» можно с легкостью слепить из изображений 0000.png-9999.png видеоролик из фотографий, ну или мелодию из семплов, или микс из треков.
Элемент multifilesrc имеет немного параметров, и как ни странно — они все полезные. Рассмотрим их подробнее.

location

Путь к файлу может (и должен) включать в себя управляющую последовательность %d, которая будет автоматически заменена с помощью функции sprintf() на число равное текущему индексу.
Пример: /foo/bar-%04d.mp3

index, start-index и stop-index

Данные свойства содержат в себе текущий индекс, стартовый индекс, и финальный индекс. Данные свойства содержат целочисленное число.

loop

Данное свойство может содержать булево значение, и в случае если оно равно true — чтение файлов будет зациклено. Грубо говоря, это аналог функции «Repeat All».

Пример использования элемента multifilesrc который читает файлы с именами начиная от bar-1.mp3 с включенной опцией loop:
gst-launch-1.0 multifilesrc location="/foo/bar-%d.mp3" loop=true start-index=1 ! decodebin ! autoaudiosink


6. fdsrc

На мой взгляд, самый бесполезный элемент. Читает данные из файлового дескриптора. Может применяться когда лень описывать filesrc для gst-launch.
Данный элемент имеет один параметр:

fd

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

Пример использования:
cat /foo/bar.mp3 | gst-launch-1.0 fdsrc ! decodebin ! autoaudiosink

7. fakesrc

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

8. alsasrc, pulsesrc, osssrc, jackaudiosrc, autoaudiosrc

Данные элементы предназначены для получения потока с микрофонного входа звуковой карты. Из названий следует, что они используются для работы с различными звуковыми подсистемами, такими, как Alsa, PulseAudio, OSS, Jack. Элемент autoaudiosrc является «оберткой» над остальными элементами, и упрощает разработку, забирая на свои плечи всю работу по выбору источника звука.
Свойств у данных элементов не очень много. Основным свойством является device, оно присутствует в первых трех элементах и должно содержать название девайса (hw:0, hw:1...). Остальные свойства используются специфичны для каждой звуковой подсистемы, и рассматривать мы их не будем.

9. v4l2src

Этот элемент предназначен для открытия видеопотока через интерфейс Video4Linux2. Интерфейс V4L2 поддерживает большинство веб-камер, ТВ-тюнеры, DVB-карточки и карточки видеозахвата.
Рассмотрим наиболее важные параметры:

device

Указывает имя устройства с которого нужно производить захват. Узнать список доступных устройств можно с помощью
ls -l /dev/v4l/by-id

В качестве значения этого параметра должен быть указан полный путь к устройству.
Пример: device=/dev/video0

hue, saturation, contrast. brightness

Данные параметры отвечают за цветовой баланс, насыщенность, контраст и яркость соответственно. Конечно же, если устройство видеозахвата не поддерживает данные параметры, то изображение будет таким, каким его отдает устройство. Значения этих свойств должны находиться в диапазоне от -2147483648 до 2147483647.

Пример использования элемента:
gst-launch-1.0 v4l2src ! xvimagesink

10. audiotestsrc

Этот элемент аналогичен элементу fakesrc, но отличается тем, что генерирует реальный аудиосигнал. Его можно использовать как для тестирования сложных pipeline запущенных посредством gst-launch, для тестирования работоспособности pipeline в написанном приложении, а также его можно использовать как эталонный генератор для измерения АЧХ и искажений которые вносят разнообразные фильтры.
Рассмотрим наиболее интересные параметры данного элемента.

wave

Данный параметр устанавливает форму сигнала. Доступно 13 значений: sine, square, saw, triangle, silence, white-noise, pink-noise, sine-table, ticks, gaussian-noise, red-noise, blue-noise, violet-noise.

volume

Не сложно догадаться, что этот параметр устанавливает уровень сигнала (громкость). Допустимые значения располагаются в диапазоне от 0 до 1.

freq

Это свойство устанавливает частоту сигнала, и значение может располагаться в диапазоне от 0 до 20000.

Пример использования элемента audiotestsrc:
gst-launch-1.0 audiotestsrc wave=saw freq=205 ! autoaudiosink

11. videotestsrc

Этот элемент, как и audiotestsrc, используется для генерации тестовых данных, а именно, видеопотока. Его можно использовать как для проверки работоспособности всего pipeline, или же для проверки на наличие искажений которые вносятся фильтрами.
Параметров у этого элемента очень много, рассмотрим мы только один, самый интересный на мой взгляд.

pattern

Данный параметр устанавливает шаблон видеосигнала. Допустимым значением может быть одно из 21. Полный список: smpte, snow, black, white, red, green, blue, checkers-1, checkers-2, checkers-4, checkers-8, circular, blink, smpte75, zone-plate, gamut, chroma-zone-plate, solid-color, ball, smpte100, bar.
На всех значениях останавливаться не буду, скажу только то, что значения smpte, smpte75 и smpte100 — это настроечные таблицы SMPTE которые применяются в настройках теле/видео-аппаратуры. Шаблоны checkers-* — это сетка NxN пикселей, где N равняется числу в имени шаблона. Остальные же значения устанавливают шаблон с монотонным изображением, например заливку цветом.

Пример использования:
gst-launch-1.0 videotestsrc pattern=smpte ! xvimagesink

Пояснения


Выше мы рассмотрели 15 основных источников данных, это где-то половина от всех имеющихся плагинов из категории source. Остальные плагины не были рассмотрены по двум причинам:
  • Они узкоспециализированные;
  • У меня отсутствуют необходимые «железки» для проверки того или иного плагина.

При рассмотрении плагинов были опущены многие параметры, так как в основном все они используются для «тонкой настройки», а у нас, как вы помните, идет знакомство с GStreamer. Или же данные параметры доступны только для чтения. Чтобы получить список всех доступных свойств, а так же допустимые для них значения, достаточно выполнить gst-inspect-1.0 <имя_элемента>, например:
gst-inspect-1.0 filesrc

и в секции «Element Properties» вы можете наблюдать все свойства (в том числе и унаследованные).

Примеры


Сегодня мы рассмотрим один простой пример использования GStreamer. В примере я буду использовать Python 2.7, GStreamer 1.0 и GObject Introspection.
Сразу оговорюсь, код может быть не идеальным, и может быть даже так называемым «говнокодом», но его единственное назначение показать, как использовать GStreamer, не более.

Простой плеер

Данный пример показывает, как проводится связывание элементов, как обрабатываются сообщения и изменяются свойства элементов.
Код плеера
#env python2
# coding=utf-8

import gi
gi.require_version("Gst", "1.0")
gi.require_version("Gtk", "3.0")
from gi.repository import Gst
from gi.repository import Gtk
from gi.repository import GObject

import os
import signal
import argparse

Gst.init("")
signal.signal(signal.SIGINT, signal.SIG_DFL)
GObject.threads_init()


def parse_args():
    parser = argparse.ArgumentParser(prog='example1.py')
    parser.add_argument('--volume', help='Указать громкость (0-100)', type=int, default=100)
    parser.add_argument('location')
    args = parser.parse_args()
    return args


class Player():

    def __init__(self, args):
        self.pipeline = self.create_pipeline(args)

        ## получаем шину по которой рассылаются сообщения
        ## и вешаем на нее обработчик
        message_bus = self.pipeline.get_bus()
        message_bus.add_signal_watch()
        message_bus.connect('message', self.message_handler)

        ## устанавливаем громкость
        self.pipeline.get_by_name('volume').set_property('volume', args.volume / 100.)

    def create_source(self, location):
        """create_source(str) -> Gst.Element"""
        if not args.location.startswith('http') and not os.path.exists(args.location):
            raise IOError("File %s doesn't exists" % args.location)

        if location.startswith('http'):
            source = Gst.ElementFactory.make('souphttpsrc', 'source')
        else:
            source = Gst.ElementFactory.make('filesrc', 'source')
        source.set_property('location', location)
        return source

    def create_pipeline(self, args):
        """create_pipeline() -> Gst.Pipeline"""

        pipeline = Gst.Pipeline()
        ## Создаем нужные элементы для плеера
        source = self.create_source(args.location)
        decodebin = Gst.ElementFactory.make('decodebin', 'decodebin')
        audioconvert = Gst.ElementFactory.make('audioconvert', 'audioconvert')
        volume = Gst.ElementFactory.make('volume', 'volume')
        audiosink = Gst.ElementFactory.make('autoaudiosink', 'autoaudiosink')

        ## decodebin имеет динамические pad'ы, которые так же динамически
        ## необходимо линковать
        def on_pad_added(decodebin, pad):
            pad.link(audioconvert.get_static_pad('sink'))
        decodebin.connect('pad-added', on_pad_added)

        ## добавляем все созданные элементы в pipeline
        [pipeline.add(k) for k in [source, decodebin, audioconvert, volume, audiosink]]

        ## линкуем элементы между собой по схеме:
        ## *src* -> (decodebin + audioconvert) -> volume -> autoaudiosink
        source.link(decodebin)
        audioconvert.link(volume)
        volume.link(audiosink)
        return pipeline

    def play(self):
        self.pipeline.set_state(Gst.State.PLAYING)

    def message_handler(self, bus, message):
        """Обработчик сообщений"""
        struct = message.get_structure()
        if message.type == Gst.MessageType.EOS:
            print('Воспроизведение окончено.')
            Gtk.main_quit()
        elif message.type == Gst.MessageType.TAG and message.parse_tag() and struct.has_field('taglist'):
            print('GStreamer обнаружил в потоке мета-теги')
            taglist = struct.get_value('taglist')
            for x in range(taglist.n_tags()):
                name = taglist.nth_tag_name(x)
                print('  %s: %s' % (name, taglist.get_string(name)[1]))
        else:
            pass


if __name__ == "__main__":
    args = parse_args()
    player = Player(args)
    player.play()
    Gtk.main()


Примеры запуска этого скрипта:
python2 ./example1.py /foo/bar.mp3
python2 ./example1.py --volume 50 /foo/bar.mp3
python2 ./example1.py http://myradio.localhost:5678/foo.mp3

В случае, если все пойдет как нужно — вы услышите нужный вам трек и увидите список ID3 тегов, если таковые имеются.
Пример доступен на GitHub.

Ну вот и все


Сегодня мы рассмотрели около половины имеющихся источников данных которые доступны «из коробки», рассмотрели примеры запуска этих элементов, а также рассмотрели пример простого плеера основанного на GStreamer. В следующих статьях мы будем рассматривать остальные классы плагинов, рассмотрим еще больше примеров использования GStreamer, и конечно же, напишем больше кода.
До встречи через неделю!

Литература


  1. GStreamer Application Development Manual
  2. GStreamer Core Plugins Reference Manual
  3. GStreamer Base Plugins Reference Manual
  4. GStreamer Good Plugins Reference Manual
  5. Video4Linux
  6. GIO Reference Manual
  7. The ID3v2/Shoutcast standart
  8. SHOUTCast


UPD 1: Добавлены элементы audiotestsrc, videotestsrc.

Предыдущая статья | Следующая статья
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 11

    0
    Спасибо за статью.
    Одна просьба. Не могли бы Вы делать хоть кратенький анонс следующей статьи?
    Можно было бы заранее «подбросить» вопросы.
      0
      Всегда пожалуйста!
      В следующей статье я подумываю рассмотреть элементы для вывода потока (sink).
      +1
      Есть ещё пара элементов очень полезных при разработке приложений: videotestsrc и audiotestsrc — бесконечные видео- и аудио-потоки.
        0
        Добавил их в статью. Спасибо что напомнили, а то совсем из головы вылетело.
        +1
        Хочется увидеть описание общего подхода при построение пайпов: enc/dec, mux/demux, pay/depay. А то даже для простого воспроизведение h264 оказывается h264parse нужен. Я поэтому частично гуглю готовые пайпы, т.к. общей логики не понял пока.

        Не подскажите, кстати как записать аудио и видео потоки в файл полученные с ip камеры через souphttp?

        Добавлю ссылку на различие между 0.1 и 1.0, мне была полезна т.к. в сети много примеров для 0.1
        gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/chapter-porting-1.0.html#section-porting-objects-1.0

        И есть еще ключ -e у gst-launch, который позволяет корректно завершать видео поток при завершение по ctrl-c
          0
          Все это будет, про кодирование/декодирование потоков хочу рассказать через одну-две статьи, пока что по плану у меня устройства вывода (sink) и контейнеры (bins). К сожалению все никак не могу время для публикации статьи, уже почти два месяца прошло…
          Для воспроизведения зачастую (мне так кажется, ибо это более разумно) используют decodebin в pipeline, но главной его особенностью является то, что аудио/видео потоки он может отдавать не в ожидаемом x-raw, а в том, в чем отдает его реальный декодер, а дальше уже искать нужный парсер/демуксер из имеющихся проверяя из возможности, о которых я как-нибудь попытаюсь рассказать, и если честно — сам еще не особо понял как они работают.

          По поводу записи потоков в файл: на счет аудио ничего не скажу, ибо все IP-камеры которые мне попадались отдавали видео в Motion JPEG, в котором аудио-дорожки само-собой нет. Для записи mjpg можно использовать например такую команду:
          gst-launch-1.0 souphttpsrc location="http://192.168.1.25:8080/video" ! decodebin use-buffering=true ! avimux ! filesink location=/tmp/1.avi
          

          В случае если камера отдает видео в каком-то ином формате с аудио-дорожкой, можно попробовать что-то вроде:
          gst-launch-1.0 souphttpsrc location="http://192.168.1.25:8080/video" ! decodebin use-buffering=true ! muxer. \
                lamemp3enc ! muxer. \
                avimux name=muxer ! filesink location=/tmp/1.avi
          

          (примерно так, в случае чего можно указать нужные энкодеры для потоков и муксеры для сбора в нужный контейнер).
            +2
            Мне казалось decodebin достаточно топорное решение, то есть его удобно использовать для разведки, чтобы получить список элементов, а затем собрать из тих нужный пайп, который гораздо гибче будет. Хотя, возможно, это излишне)

            Вчера все-таки собрал;
            gst-launch-1.0 souphttpsrc location=http://192.168.81.30/videostream.asf user-id=admin user-pw=1! asfdemux name=d d.! multiqueue! jpegdec! xvimagesink d.! multiqueue! adpcmdec! autoaudiosink

            Тема очень хорошая, найдите время)
              0
              всё еще актуально кстати :)
              ждем
                0
                Увы, я потерял интерес к gst, т.к. прекратил участие в разработке foobnix. Поэтому, скорее всего, продолжения не будет. И да, я очень сожалею об этом, но у меня нет ни сил, ни времени на продолжение этой серии статей.
                Извините. :(
            0
            Виктор, спасибо за подробные статьи из курса «начальный» :)
            Вы так уверенно отделили все входные параметры от остальных, да еще и исказали, что всего входных около тридцати. Скажите, вы это как сделали? Просто проштутдировав все маны? Пытаюсь сейчас найти какие-то упорядоченные списки и не нахожу ничего, кроме их мануала, на которые вы дали ссылки, но его струтура очень печальна, и для изучения не очень годится.
            Может, вы все еще помните..?)
              0

              Пожалуйста :)


              Как я это сделал… Да черт его знает как. Что-то понимал методом тыка, что-то выковыривал из материалов, на которые давал ссылки.
              Но в целом происходило так — сначала понял как должен создаваться pipeline, это не сложно, достаточно иметь логическое мышление, чтобы понять что за источником должен быть декодер, за кучей источников должна быть куча декодеров за которыми должен быть муксер, за всем этим должен находиться sink, который либо выведет звук в колонки, либо картинку в окно, или же должен быть энкодер и filesink для сохранения потока в файл.
              Ну а дальше уже разбирать каждый "блок" на "составляющие" — как соединять его с другими "блоками", какие у него есть аргументы, что нужно передавать в эти аргументы. Зачастую всю подобную информацию можно найти либо в официальной документации (которая да, не совсем хороша), либо на stackoverflow.

            Only users with full accounts can post comments. Log in, please.