[IPTV, Видеоконференцсвязь, Разработка под Linux, Разработка под Android, Производство и разработка электроники] Как разработать аналог Zoom для ТВ-приставок на RDK и Linux. Разбираемся с фреймворком GStreamer
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Сценарии: как использовать приложение для видеоконференций на SmartTV и ТВ-приставкахПандемия COVID-19 стала катализатором для новых полезных сервисов. Например, Zoom стал настолько успешным, что по стоимости обогнал в этом месяце IBM. Нас вдохновил этот пример, и мы решили пойти еще дальше: а что если онлайн-конференции реализовать на приставках и Smart TV, чтобы общаться не только по работе, но устраивать удаленные посиделки на диване с друзьями? Но ведь тогда можно на футболе вместе покричать, и кино посмотреть или спортом заняться под контролем тренера. Почему-то у операторов цифрового ТВ такой услуги не оказалось, хотя с инженерной точки зрения все эти функции вполне можно реализовать на ТВ-приставках на базе Linux/Android и RDK. Мы это проверили на практике и вот теперь делимся с читателями Хабра своим рецептом создания «аналога Zoom» и видеоконференций через Smart TV. Разберем архитектуру решения и кодирование видеопотока с использованием GStreamer. Информацию для работы с этим фреймворком мы собирали по крупицам, но оно того стоило.Разбираемся с архитектуройПри разработке архитектуры приложений для ТВ-приставок важно учитывать потенциальные ограничения программной и аппаратной части этих устройств. Те, кто создает desktop-программы, как правило, не сталкиваются с этими сложностями, а для embedded-разработчиков это обычное дело.Итак, с чем мы столкнемся на ТВ-приставках:
- Ограниченность ресурсов процессоров и самих приставок. Чаще всего в STB-устройствах используются разнообразные ARM-процессоры, они несут в себя ряд ограничений и дополнительных задач, в частности необходимость использования аппаратного кодирования/декодирование видеопотока. Всегда нужно учитывать, что быстродействие — одно из узких мест.
- Разность архитектур в приставках разных производителей. Некоторые базируются на Android, другие — на RDK, третьи — на дистрибутивах на базе Linux со своими ограничениями и нюансами. Поэтому на старте разработки лучше выбрать наиболее общие и кроссплатформенные решения в разных модулях программы. Уже не говоря о поддержке desktop-версии. А уже потом переходить к частным случаям.
- Сетевые ограничения. Многие приставки работают как по Ethernet так и по wifi. Сжатие и передача видео/аудиопотока — еще одно узкое место в приложениях такого рода.
- Безопасность передачи потока и др. вопросы безопасности данных.
- Поддержка камер и микрофонов на встроенных платформах.
Теперь можно разложить по полочкам и саму архитектуру. Аналог Zoom для ТВ-приставок будет состоять из нескольких больших компонентов и модулей:
- Захват видеопотока
- Захват аудиопотока
- Сетевой модуль
- Модуль кодирования видео/аудиопотока
- Модуль декодирования видео/аудиопотока
- Вывод видеоконференции на экран
- Вывод звука
- Цветовые преобразования
- Несколько других второстепенных компонентов
В упрощенном виде один из вариантов архитектуры можно изобразить так:
Архитектура приложения для видеоконференций через Smart TVВ рамках статьи мы сфокусируемся на кодировании декодирования видеопотока и на возможной реализации с использованием фреймворка GStreamer, т.к. это один из ключевых моментов в разработке приложений для видеоконференций.Кодирование/декодирование потока 1) Преимущества GStreamerКак мы уже отметили, одним из узких мест является передача видеопотока. Предположим у вас есть камера, отдающие кадры на скорости 30 кадров с секунду на небольшом разрешении 640x480. Итого, в RGB24 получается:640 х 480 х 3 х 30 = 27 648 000 байт в секунду, т.е. более 26 Мбайт в секунду, что по понятным причинам совершенно неприемлемо в рамках сетевой передачи.Один из вариантов решения — реализовать кодирование какой-нибудь библиотекой. И тут, как можно уже было догадаться из названия статьи, выбор пал на фреймворк GStreamer. Почему именно эта библиотека? Вот несколько ее преимуществ на фоне остальных решений:
- Хорошая кроссплатформенность и отличная поддержка Linux и Android.
- На RDK Gstreamer является стандартом кодирования/декодирования и включен в дистрибутив по-умолчанию.
- Поддержках широкого набора модулей, фильтров и кодеков. Тот же FFmpeg, который можно использовать для тех же целей, де-факто является лишь одним из модулей GStreamer’а.
- Простота построения конвейера (pipeline). Выстроить цепочку кодирования/декодирования не составляет большого труда, а сам подход конвейера позволяет легко заменять кодеки, фильтры и прочее без лишнего переписывания кода.
- API для работы С/C++ идет комплекте.
- Поддержка аппаратных кодеров/декодеров, в частности OpenMAX API — что крайне важно при работе с ТВ-приставками.
2) Знакомимся с GStreamer разбираемся с конвейерами Прежде чем приступать к рассмотрению кода, давайте разберемся с тем, что можно делать пока и без него. В состав GStreamer входят полезные утилиты для работы, в частности:gst-inspect-1.0 позволит посмотреть список доступных кодеков и модулей, так что можно сразу увидеть, что будет работать, и выбрать набор фильтров и кодеков.gst-launch-1.0 позволяет запустить любой конвейер (pipeline).В GStreamer используется схема декодирования, по которой поток последовательно проходит через разные компоненты, начиная с source, заканчивая sink-выводом. При этом source может быть любым — файл, устройство, для вывода (sink) также можно указать нужное — файл, экран, сетевые выводы и протоколы (наподобие RTP).Подробнее про использование утилит и принцип построения конвейера можно почитать в другой статье на Хабре. Классический пример проигрывания mp4-файла:gst-launch-1.0 filesrc location=file.mp4 ! qtdemux ! h264parse ! avdec_h264 ! videoconvert ! autovideosinkНа входе принимается mp4-файл, который проходит через демуксер mp4 — qtdemux, парсер h264, затем через декодер, конвертер и, наконец, идет вывод на экран.Можно заменить autovideosink на filesink с параметром файла и вывести декодированный поток прямиком в файл.3) Пишем приложение на GStreamer C/C++ API. Попробуем декодироватьТеперь, когда мы разобрались немного с использованием gst-launch-1.0, проделаем все тоже самое, только уже в рамках приложения. Принцип останется тем же: мы встраиваем декодирующий конвейер (pipeline), однако уже с использованием библиотеки GStreamer и glib-событий.О простейшем примере построения filesrc в filesink с кодом хорошо рассказано в другой хабрастатье — «GStreamer: элементы и контейнеры». Мы же рассмотрим чуть более реалистичный и сложный живой пример H264-декодирования.Инициализация GStreamer-приложения происходит один раз при помощиgstinit (NULL, NULL);Если вы хотите сразу видеть происходящее в деталях, можно выстроить уровень логирования перед инициализацией
gst_debug_set_active(TRUE);
gst_debug_set_default_threshold(GST_LEVEL_LOG);
Учтите: сколько бы у вас не было конвейеров в приложении, инициализировать gstinit достаточно один раз.Создадим новый event-loop, в котором будут обрабатываться события:
GMainLoop *loop;
loop = g_main_loop_new (NULL, FALSE);
И теперь можно начать выстраивать наш конвейер:Объявим необходимые элементы, в частности сам конвейер как тип GstElement:
GstElement *pipeline, *source, *demuxer, *parser, *decoder, *conv, *sink;
pipeline = gst_pipeline_new ("video-decoder");
source = gst_element_factory_make ("filesrc", "file-source");
demuxer = gst_element_factory_make ("qtdemux", "h264-demuxer");
parser = gst_element_factory_make ("h264parse", "h264-parser");
decoder = gst_element_factory_make ("avdec_h264", "h264-decoder");
conv = gst_element_factory_make ("videoconvert", "converter");
sink = gst_element_factory_make ("appsink", "video-output");
Каждый элемент конвейера создается через gst_element_factory_make, где первым параметром идет тип, а вторым — его условное название для GStreamer, на которое он впоследствии будет опираться, например при выдаче ошибок.Также не мешало бы проверить, что все компоненты найдены, в противном случае gst_element_factory_make, возвращает NULL.
if (!pipeline || !source || !demuxer || !parser || !decoder || !conv || !sink) {
// не инициализирован один элемент - остановка
return;
}
Установим тот самый параметр location через gob_ject_set: gob_ject_set (G_OBJECT (source), "location", argv[1], NULL);Остальные параметры в других элементах можно выставлять аналогичным образом.Теперь необходим обработчик сообщений GStreamer, создадим соответствующий bus_call:
GstBus *bus;
guint bus_watch_id;
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
bus_watch_id = gst_bus_add_watch (bus, bus_call, loop);
gst_object_unref (bus);
gst_object_unref и другие подобные вызовы нужны для очищения выделенных объектов.Затем объявим сам обработчик сообщений:
static gboolean
bus_call (GstBus *bus,
GstMessage *msg,
gpointer data)
{
GMainLoop *loop = (GMainLoop *) data;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_EOS:
LOGI ("End of stream\n");
g_main_loop_quit (loop);
break;
case GST_MESSAGE_ERROR: {
gchar *debug;
GError *error;
gst_message_parse_error (msg, &error, &debug);
g_free (debug);
LOGE ("Error: %s\n", error->message);
g_error_free (error);
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
А теперь самое важное: собираем и добавляем все созданные элементы в единый конвейер, тот самый что выстраивали через gst-launch. Очередность добавления, естественно, важна:
gst_bin_add_many (GST_BIN (pipeline), source, demuxer, parser, decoder, conv, sink, NULL);
gst_element_link_many (source, demuxer, parser, decoder, conv, sink, NULL);
Стоит также заметить, что такой подход линковки элементов прекрасно работает для потоковых выходов, но в случае именно проигрывания (autovideosink) необходима дополнительная синхронизация и динамическая линковка демуксера и парсера:
gst_element_link (source, demuxer);
gst_element_link_many (parser, decoder, conv, sink, NULL);
g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), parser);
static void
on_pad_added (GstElement *element,
GstPad *pad,
gpointer data)
{
GstPad *sinkpad;
GstElement *decoder = (GstElement *) data;
/* We can now link this pad with the sink pad */
g_print ("Dynamic pad created, linking demuxer/decoder\n");
sinkpad = gst_element_get_static_pad (decoder, "sink");
gst_pad_link (pad, sinkpad);
gst_object_unref (sinkpad);
}
Динамическое соединение дает возможность определения типа и количества потоков в отличии от статического, и будет работать в некоторых случаях когда это требуется.Ну и, наконец, переводим состояние конвейера в проигрывание:gst_element_set_state (pipeline, GST_STATE_PLAYING);И запускаем event-loop:g_main_loop_run (loop);После окончания этой процедуры все нужно почистить:
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
g_source_remove (bus_watch_id);
g_main_loop_unref (loop);
4) Выбираем энкодеры и декодеры. ФоллбэкиСтоит еще рассказать о полезных, но не особо упомянутых вещах в документации — о том, как можно легко организовать фоллбэк декодеров или энкодеров.Поможет нам в этом функция gst_element_factory_find, которая проверяет, есть ли у нас кодек в factory элементов:
if(gst_element_factory_find("omxh264dec"))
decoder = gst_element_factory_make ("omxh264dec", "h264-decoder");
else
decoder = gst_element_factory_make ("avdec_h264", "h264-decoder");
В данном примере мы приоритезировали выбор аппаратного декорера OMX на платформе RDK, и в случае его отсутствия будем выбирать программную реализацию.Другой крайне полезной, но еще более редко используемой функцией является проверка того, что же мы на самом деле инициализировали в GstElement (какой кодек):
gst_plugin_feature_get_name(gst_element_get_factory(encoder))
Можно проделать это таким незамысловатым способом и вернуть название инициализированного кодека.5) Цветовые модели видеоНе стоит оставлять без внимания и цветовые модели, раз уж мы заговорили о кодировании видео с камер. И тут чаще подразумевается YUV, нежели RGB.Камеры просто обожают цветовую модель YUYV. Но это то, с чем любит работать GStreamer, для него более привычна модель I420. На выходе, если речь не идет о выводе в gl-фрейм, у нас также будут выходить I420-кадры. Будьте готовы подставить необходимые фильтры и выполнить преобразования. Некоторые энкодеры умеют работать и с другими цветовыми моделями, но чаще это исключения из правил.Еще стоит заметить что у GStreamer’а есть свой модуль получения видеопотока с камеры, и его можно использовать для построения конвейера, но об этом расскажем как-нибудь в другой раз. Разбираемся с буферами и берем данные на лету 1) Буфер вводаПришло время разобраться с потоками данных. До этого момента мы просто кодировали то, что есть в файле, через filesrc и выводили все в тот же filesink или на экран.Теперь будем работать с буферами и входами и выходами appsrc / appsink. Почему-то этому вопросу почти не уделиливнимания в официальной документации. Как же организовать постоянный поток данных в созданных конвейерах, а точнее подать буфер на выход и получить кодированный или декодированный буфер выхода? Допустим мы получили картинку с камеры, и нам нужно ее закодировать. Мы уже определились с тем, что нам потребуется фрейм в I420. Допустим он у нас есть, что дальше? Как пропустить картинку через весь поток конвейера?Вначале установим обработчик события need-data, который будет запускаться при необходимости подачи данных в конвейер и начнем подавать входной буфер:
g_signal_connect (source, "need-data", G_CALLBACK (encoder_cb_need_data), NULL);
Сам обработчик имеет следующий вид:
encoder_cb_need_data (GstElement *appsrc,
guint unused_size,
gpointer user_data)
{
GstBuffer *buffer;
GstFlowReturn ret;
GstMapInfo map;
int size;
uint8_t* image;
// get image
buffer = gst_buffer_new_allocate (NULL, size, NULL);
gst_buffer_map (buffer, &map, GST_MAP_WRITE);
memcpy((guchar *)map.data, image, gst_buffer_get_size( buffer ) );
gst_buffer_unmap(buffer, &map);
g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret);
gst_buffer_unref(buffer);
}
image — это, условно говоря, псевдокод буфера картинки в I420.Далее мы создаем через gst_buffer_new_allocate буфер необходимого размера, который будет соответствовать размеру буфера картинки.При помощи gst_buffer_map устанавливаем буфер в режим записи и, используя memcpy, копируем нашу картинку в созданный буфер. И, наконец, сигнализируем GStream’у о том, что буфер готов.Ремарка: очень важно после записи использовать gst_buffer_unmap, а также очищать буфер после использования при помощи gst_buffer_unref. Иначе будет утечка памяти. В скудном количестве доступных примеров никто особо насчет вопроса освобождения памяти не заморачивался, а он, понятное дело, важный.Теперь, когда мы закончили с обработчиком, нужно проделать еще одну вещь: настроить caps на поступление ожидаемого формата.Делается это перед установкой обработчика сигнала need-data:
g_object_set (G_OBJECT (source),
"stream-type", 0,
"format", GST_FORMAT_TIME, NULL);
g_object_set (G_OBJECT (source), "caps",
gst_caps_new_simple ("video/x-raw",
"format", G_TYPE_STRING, "I420",
"width", G_TYPE_INT, 640,
"height", G_TYPE_INT, 480,
"framerate", GST_TYPE_FRACTION, 30, 1,
NULL),
NULL);
Как и все параметры GstElement, установка параметров осуществляется через g_object_set.В данном случае мы задали тип потока, и его caps — формат данных. Указав, что на выход appsrc будет поступать данные I420 c разрешением 640x480 и частотой 30 кадров в секунду.Частота в нашем случае, да в целом, не играет роли. На практике мы не замечали, чтобы GStreamer как-то ограничивал вызовы need-data по частоте.Готово, теперь наши кадры поступают в энкодер. 2) Буфер выводаА теперь разберемся, как получить кодированный поток на выходе.Подключаем обработчик к sink pad:
GstPad *pad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, encoder_cb_have_data, NULL, NULL);
gst_object_unref (pad);
Похожим образом мы подключились к другому событию sink pad — GST_PAD_PROBE_TYPE_BUFFER, — которое будет вызываться по мере поступления буфера данных в sink pad.
static GstPadProbeReturn
encoder_cb_have_data (GstPad * pad,
GstPadProbeInfo * info,
gpointer user_data) {
GstBuffer *buf = gst_pad_probe_info_get_buffer (info);
GstMemory *bufMem = gst_buffer_get_memory(buf, 0);
GstMapInfo bufInfo;
gst_memory_map(bufMem, &bufInfo, GST_MAP_READ);
// bufInfo.data, bufInfo.size
gst_memory_unmap(bufMem, &bufInfo);
return GST_PAD_PROBE_OK;
}
Колбэк имеет похожую структуру. Теперь нужно достучаться до памяти буфера. Вначале получаем GstBuffer, затем указатель его памяти, используя gst_buffer_get_memory по индексу 0 (как правило задействован только он). И, наконец, используя gst_memory_map, получаем адрес буфера данных bufInfo.data и его размер bufInfo.size.Фактически мы добились цели — получили буфер с кодированными данными и его размер.Итак, мы рассмотрели ключевые и самые интересные компоненты из приложения для Smart TV — аналога Zoom для ТВ-приставок: архитектуру, модули кодирования / декодирования через GStreamer, буферы ввода / вывода и используемые цветовые преобразования.Для операторов цифрового ТВ такая программная платформа может стать новым абонентским сервисом. Для нас — инженеров — новым интересным embedded-проектом для реализации на разных приставках на базе RDK, Linux и Android. А для всех остальных — возможностью приятно проводить время за совместным просмотром фильмов и спортивных матчей, занятий спортом и посиделок с близкими в период карантина или удаленной работы.Эту идею с сервисом видеоконференций по Smart TV можно развивать и дальше, как с точки зрения инженерных решений, так и по сценариям их использования. Так что делитесь мыслями в комментариях.
===========
Источник:
habr.com
===========
Похожие новости:
- [Видеокарты, Компьютерное железо, Производство и разработка электроники] Nvidia выпустила версию драйвера Game Ready 456.55 WHQL, который занижает частоты RTX 3080
- [Звук, Носимая электроника, Периферия, Производство и разработка электроники] Личные пристрастия: Sennheiser HD 560S или о том, какие наушники можно купить за $ 200
- [Информационная безопасность, IT-инфраструктура, Исследования и прогнозы в IT, IT-компании] Зона доступа: 30 способов, которые позволят разблокировать любой смартфон. Часть 2
- Релиз дистрибутива для запуска игр Ubuntu GamePack 20.04
- [Разработка под Android] Бесшовные A/B-обновления в Android: как они устроены
- [IT-компании, Копирайт, Разработка мобильных приложений, Разработка под Android] Со следующего года все приложения Google Play будут платить комиссию 30 %, включая Netflix и Spotify
- Проект DSL (DOS Subsystem for Linux) для запуска Linux-приложений из окружения MS-DOS
- [GitHub, Open source, Разработка под Linux, Старое железо] Разработчик опубликовал DOS Subsystem for Linux
- [Производство и разработка электроники, Химия] Исследователи наконец создали металлические провода из углерода (перевод)
- [Flutter, Конференции, Разработка мобильных приложений, Разработка под Android, Разработка под iOS] Нативная разработка vs кроссплатформенная – обсуждаем 30 сентября с владельцами приложений
Теги для поиска: #_iptv, #_videokonferentssvjaz (Видеоконференцсвязь), #_razrabotka_pod_linux (Разработка под Linux), #_razrabotka_pod_android (Разработка под Android), #_proizvodstvo_i_razrabotka_elektroniki (Производство и разработка электроники), #_tvpristavki (тв-приставки), #_tsifrovoe_tv (цифровое тв), #_konferentssvjaz (конференцсвязь), #_zoom, #_gstreamer, #_rdk, #_linux, #_promwad, #_android, #_cpp, #_iptv, #_videokonferentssvjaz (
Видеоконференцсвязь
), #_razrabotka_pod_linux (
Разработка под Linux
), #_razrabotka_pod_android (
Разработка под Android
), #_proizvodstvo_i_razrabotka_elektroniki (
Производство и разработка электроники
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 02:52
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Сценарии: как использовать приложение для видеоконференций на SmartTV и ТВ-приставкахПандемия COVID-19 стала катализатором для новых полезных сервисов. Например, Zoom стал настолько успешным, что по стоимости обогнал в этом месяце IBM. Нас вдохновил этот пример, и мы решили пойти еще дальше: а что если онлайн-конференции реализовать на приставках и Smart TV, чтобы общаться не только по работе, но устраивать удаленные посиделки на диване с друзьями? Но ведь тогда можно на футболе вместе покричать, и кино посмотреть или спортом заняться под контролем тренера. Почему-то у операторов цифрового ТВ такой услуги не оказалось, хотя с инженерной точки зрения все эти функции вполне можно реализовать на ТВ-приставках на базе Linux/Android и RDK. Мы это проверили на практике и вот теперь делимся с читателями Хабра своим рецептом создания «аналога Zoom» и видеоконференций через Smart TV. Разберем архитектуру решения и кодирование видеопотока с использованием GStreamer. Информацию для работы с этим фреймворком мы собирали по крупицам, но оно того стоило.Разбираемся с архитектуройПри разработке архитектуры приложений для ТВ-приставок важно учитывать потенциальные ограничения программной и аппаратной части этих устройств. Те, кто создает desktop-программы, как правило, не сталкиваются с этими сложностями, а для embedded-разработчиков это обычное дело.Итак, с чем мы столкнемся на ТВ-приставках:
Архитектура приложения для видеоконференций через Smart TVВ рамках статьи мы сфокусируемся на кодировании декодирования видеопотока и на возможной реализации с использованием фреймворка GStreamer, т.к. это один из ключевых моментов в разработке приложений для видеоконференций.Кодирование/декодирование потока 1) Преимущества GStreamerКак мы уже отметили, одним из узких мест является передача видеопотока. Предположим у вас есть камера, отдающие кадры на скорости 30 кадров с секунду на небольшом разрешении 640x480. Итого, в RGB24 получается:640 х 480 х 3 х 30 = 27 648 000 байт в секунду, т.е. более 26 Мбайт в секунду, что по понятным причинам совершенно неприемлемо в рамках сетевой передачи.Один из вариантов решения — реализовать кодирование какой-нибудь библиотекой. И тут, как можно уже было догадаться из названия статьи, выбор пал на фреймворк GStreamer. Почему именно эта библиотека? Вот несколько ее преимуществ на фоне остальных решений:
gst_debug_set_active(TRUE);
gst_debug_set_default_threshold(GST_LEVEL_LOG); GMainLoop *loop;
loop = g_main_loop_new (NULL, FALSE); GstElement *pipeline, *source, *demuxer, *parser, *decoder, *conv, *sink;
pipeline = gst_pipeline_new ("video-decoder"); source = gst_element_factory_make ("filesrc", "file-source"); demuxer = gst_element_factory_make ("qtdemux", "h264-demuxer"); parser = gst_element_factory_make ("h264parse", "h264-parser"); decoder = gst_element_factory_make ("avdec_h264", "h264-decoder"); conv = gst_element_factory_make ("videoconvert", "converter"); sink = gst_element_factory_make ("appsink", "video-output"); if (!pipeline || !source || !demuxer || !parser || !decoder || !conv || !sink) {
// не инициализирован один элемент - остановка return; } GstBus *bus;
guint bus_watch_id; bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); bus_watch_id = gst_bus_add_watch (bus, bus_call, loop); gst_object_unref (bus); static gboolean
bus_call (GstBus *bus, GstMessage *msg, gpointer data) { GMainLoop *loop = (GMainLoop *) data; switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_EOS: LOGI ("End of stream\n"); g_main_loop_quit (loop); break; case GST_MESSAGE_ERROR: { gchar *debug; GError *error; gst_message_parse_error (msg, &error, &debug); g_free (debug); LOGE ("Error: %s\n", error->message); g_error_free (error); g_main_loop_quit (loop); break; } default: break; } return TRUE; } gst_bin_add_many (GST_BIN (pipeline), source, demuxer, parser, decoder, conv, sink, NULL);
gst_element_link_many (source, demuxer, parser, decoder, conv, sink, NULL); gst_element_link (source, demuxer);
gst_element_link_many (parser, decoder, conv, sink, NULL); g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), parser); static void on_pad_added (GstElement *element, GstPad *pad, gpointer data) { GstPad *sinkpad; GstElement *decoder = (GstElement *) data; /* We can now link this pad with the sink pad */ g_print ("Dynamic pad created, linking demuxer/decoder\n"); sinkpad = gst_element_get_static_pad (decoder, "sink"); gst_pad_link (pad, sinkpad); gst_object_unref (sinkpad); } gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline)); g_source_remove (bus_watch_id); g_main_loop_unref (loop); if(gst_element_factory_find("omxh264dec"))
decoder = gst_element_factory_make ("omxh264dec", "h264-decoder"); else decoder = gst_element_factory_make ("avdec_h264", "h264-decoder"); gst_plugin_feature_get_name(gst_element_get_factory(encoder))
g_signal_connect (source, "need-data", G_CALLBACK (encoder_cb_need_data), NULL);
encoder_cb_need_data (GstElement *appsrc,
guint unused_size, gpointer user_data) { GstBuffer *buffer; GstFlowReturn ret; GstMapInfo map; int size; uint8_t* image; // get image buffer = gst_buffer_new_allocate (NULL, size, NULL); gst_buffer_map (buffer, &map, GST_MAP_WRITE); memcpy((guchar *)map.data, image, gst_buffer_get_size( buffer ) ); gst_buffer_unmap(buffer, &map); g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret); gst_buffer_unref(buffer); } g_object_set (G_OBJECT (source),
"stream-type", 0, "format", GST_FORMAT_TIME, NULL); g_object_set (G_OBJECT (source), "caps", gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, "I420", "width", G_TYPE_INT, 640, "height", G_TYPE_INT, 480, "framerate", GST_TYPE_FRACTION, 30, 1, NULL), NULL); GstPad *pad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, encoder_cb_have_data, NULL, NULL); gst_object_unref (pad); static GstPadProbeReturn
encoder_cb_have_data (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) { GstBuffer *buf = gst_pad_probe_info_get_buffer (info); GstMemory *bufMem = gst_buffer_get_memory(buf, 0); GstMapInfo bufInfo; gst_memory_map(bufMem, &bufInfo, GST_MAP_READ); // bufInfo.data, bufInfo.size gst_memory_unmap(bufMem, &bufInfo); return GST_PAD_PROBE_OK; } =========== Источник: habr.com =========== Похожие новости:
Видеоконференцсвязь ), #_razrabotka_pod_linux ( Разработка под Linux ), #_razrabotka_pod_android ( Разработка под Android ), #_proizvodstvo_i_razrabotka_elektroniki ( Производство и разработка электроники ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 02:52
Часовой пояс: UTC + 5