Перейти к основному содержимому

07-06-02 Минимальный пример Python RTSP — окно

Теперь, когда вы познакомились с gst-launch-1.0 и научились собирать пайплайны из командной строки, настало время перейти к следующему уровню — управлению GStreamer через код. В реальной разработке медиаприложений (например, систем видеонаблюдения, мостов протоколов или плагинов для OBS) редко используются только команды в терминале. Гораздо чаще GStreamer интегрируется в приложения на C, Python или других языках.

В этом разделе мы разберём минимальный, но рабочий пример на Python, который делает то же самое, что и команда gst-launch: подключается к RTSP-камере и отображает видео в окне. Но теперь — не как строка в терминале, а как полноценная программа, которую можно модифицировать, расширять и встраивать в другие системы.


Инициализация GStreamer в Python

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

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

Gst.init(None)

Что происходит здесь:

  • gi — это Python-обёртка для GObject Introspection, механизм, позволяющий использовать C-библиотеки (включая GStreamer) из Python.
  • gi.require_version("Gst", "1.0") — указывает, что мы хотим использовать версию GStreamer 1.0.
  • Gst.init(None) — инициализирует GStreamer. Параметр None означает, что мы не передаём аргументы командной строки (в отличие от C-версии).

💡 Аналог в C:
В C-коде это выглядело бы как gst_init(&argc, &argv);.
В Python мы просто вызываем Gst.init(), и движок готов к работе.


Создание пайплайна из строки

Один из самых простых способов начать — использовать строковое описание пайплайна, как в gst-launch, но уже внутри Python-кода. Это даёт плавный переход от CLI к программированию.

pipeline_str = """
rtspsrc location=rtsp://user:pass@ip/stream latency=0 !
decodebin !
videoconvert !
autovideosink
"""

Разбор строки:

Этот пайплайн:

  1. rtspsrc — подключается к RTSP-потоку по указанному URL.
  2. decodebin — автоматически выбирает подходящий декодер (например, H.264 → RAW).
  3. videoconvert — преобразует формат видео под нужды следующего элемента (например, из I420 в RGB).
  4. autovideosink — выводит видео в окно (через X11, Wayland и т.п.).

🔍 Важно: строка должна быть корректной с точки зрения синтаксиса GStreamer.
Пробелы, точки с запятой и отступы — всё это имеет значение.

Теперь передаём строку в GStreamer:

pipeline = Gst.parse_launch(pipeline_str)

Что делает Gst.parse_launch()?

Эта функция парсит строку и строит из неё объект пайплайна — точно так же, как это делает gst-launch-1.0. Внутри создаются и соединяются все элементы, как если бы мы делали это вручную.

Плюс подхода:
Вы можете сначала отладить пайплайн в терминале, а потом просто скопировать его в Python.
Это отличный способ начать, не погружаясь сразу в сложности API.


Запуск пайплайна: управление состояниями

После создания пайплайн находится в состоянии NULL — ничего не делает. Чтобы начать воспроизведение, нужно перевести его в состояние PLAYING.

pipeline.set_state(Gst.State.PLAYING)

Состояния пайплайна (напоминание из лекции 07-02-04):

СостояниеОписание
NULLИзначальное состояние, элементы не инициализированы.
READYЭлементы готовы, но не работают.
PAUSEDПайплайн "заморожен" — буферы могут заполняться, но вывод не идёт.
PLAYINGПайплайн активно обрабатывает данные.

⚠️ Важно: переход между состояниями не мгновенный.
GStreamer может тратить время на установку соединения, получение SPS/PPS, синхронизацию.
Поэтому сразу после set_state(PLAYING) пайплайн может ещё не работать — это нормально.


Обработка событий: шина (bus) и сообщения

Теперь пайплайн работает, но у нас нет способа узнать, что происходит. А вдруг ошибка? Или поток закончился? Чтобы отслеживать такие события, используется шина (bus) — центральная очередь сообщений.

bus = pipeline.get_bus()

bus — это как «почтовый ящик» пайплайна. Все элементы могут отправлять туда сообщения: об ошибках, завершении, изменениях состояния и т.д.

Чтение сообщений с таймаутом

Мы будем периодически проверять шину на наличие важных событий:

while True:
msg = bus.timed_pop_filtered(
10 * Gst.MSECOND,
Gst.MessageType.ERROR | Gst.MessageType.EOS
)
if msg:
if msg.type == Gst.MessageType.ERROR:
err, debug = msg.parse_error()
print("ERROR:", err, debug)
break
elif msg.type == Gst.MessageType.EOS:
print("End of stream")
break

Как это работает:

  • timed_pop_filtered(timeout, types) — ждёт сообщение не дольше заданного времени (здесь — 10 мс), но только определённых типов.
  • Мы интересуемся двумя типами:
    • ERROR — произошла ошибка (например, камера недоступна).
    • EOS (End of Stream) — поток завершён (например, камера отключилась).
  • Если пришло сообщение — обрабатываем его и выходим из цикла.

🔄 Почему цикл while True?
Это простая замена полноценного event loop. В реальных приложениях (например, с GUI) используется GObject.MainLoop, но здесь мы хотим сохранить пример минимальным.


Корректное завершение: освобождение ресурсов

После завершения работы (по ошибке или по EOS) необходимо остановить пайплайн и освободить ресурсы.

pipeline.set_state(Gst.State.NULL)

Почему NULL?

Перевод в состояние NULL:

  • Останавливает все элементы.
  • Освобождает память, закрывает сетевые соединения, уничтожает окна.
  • Это обязательный шаг при завершении работы с GStreamer.

Что будет, если не вызвать set_state(NULL)?
Программа может "зависнуть" или оставить открытые соединения.
В некоторых случаях окно не закроется, или камера останется занятой.


Полный код — с комментариями

Вот как выглядит весь пример целиком, готовый к запуску:

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

# Инициализация GStreamer
Gst.init(None)

# Описание пайплайна — как в gst-launch
pipeline_str = """
rtspsrc location=rtsp://user:pass@192.168.1.100:554/stream latency=0 !
decodebin !
videoconvert !
autovideosink
"""

# Создание пайплайна из строки
pipeline = Gst.parse_launch(pipeline_str)

# Запуск пайплайна
pipeline.set_state(Gst.State.PLAYING)

# Получение шины для отслеживания событий
bus = pipeline.get_bus()

print("Пайплайн запущен. Для выхода — ожидание ошибки или EOS...")

# Цикл обработки сообщений
try:
while True:
# Ждём сообщение до 10 мс
msg = bus.timed_pop_filtered(
10 * Gst.MSECOND,
Gst.MessageType.ERROR | Gst.MessageType.EOS
)
if msg:
if msg.type == Gst.MessageType.ERROR:
err, debug = msg.parse_error()
print("❌ ОШИБКА:", err, "\nДетали:", debug)
break
elif msg.type == Gst.MessageType.EOS:
print("⏹️ Поток завершён (EOS)")
break
except KeyboardInterrupt:
print("\nПрервано пользователем")

# Остановка пайплайна
pipeline.set_state(Gst.State.NULL)
print("Пайплайн остановлен")

Как использовать этот код на практике

  1. Замените URL на реальный адрес вашей RTSP-камеры.
  2. Убедитесь, что GStreamer установлен и Python-биндинги работают:
    python3 -c "import gi; gi.require_version('Gst', '1.0'); from gi.repository import Gst; print(Gst.version())"
  3. Запустите скрипт:
    python3 rtsp_player.py

Если всё настроено верно — вы увидите окно с видео с камеры.


Почему это важно: от строки к приложению

Сравните два подхода:

Характеристикаgst-launch (CLI)Python-код
Простота старта✅ Очень просто🟡 Нужно знать Python
Возможность обработки ошибок❌ Только вывод в терминал✅ Можно реагировать, переподключаться
Управление параметрами "на лету"❌ Только перезапуск✅ Можно менять latency, location и т.д.
Интеграция с GUI / веб-интерфейсом❌ Нет✅ Легко встраивается
Автоматизация❌ Через скрипты оболочки✅ Полноценная логика

🎯 Вывод: gst-launch — отличный инструмент для отладки и тестирования.
Но реальные приложения строятся на коде, где вы контролируете каждый аспект: подключение, ошибки, переключение камер, обработку кадров.


Дальнейшие шаги

После этого примера вы можете:

  • Добавить обработку аудио (через decodebinaudioconvertautoaudiosink).
  • Внедрить переподключение при обрыве (по ERROR или EOS).
  • Использовать appsink, чтобы получить кадры в OpenCV (следующий слайд).
  • Добавить GUI (например, с GTK или PyQt), чтобы управлять пайплайном кнопками.

Но главное — вы теперь понимаете: gst-launch — это не магия.
Это просто удобная обёртка над теми же API, которые вы только что использовали в Python.