A pandemia COVID-19 tornou-se um catalisador para novos serviços úteis. Por exemplo, o Zoom se tornou tão bem-sucedido que ultrapassou a IBM em valor neste mês. Fomos inspirados por este exemplo e decidimos ir ainda mais longe: e se as conferências online forem implementadas em decodificadores e Smart TVs para nos comunicarmos não apenas no trabalho, mas para organizar encontros remotos no sofá com amigos? Mas então vocês podem gritar juntos no futebol, assistir a um filme ou praticar esportes sob a supervisão de um treinador.
- , - Linux/Android RDK. « Zoom» Smart TV. GStreamer. , .
- . , desktop-, , , embedded- .
, -:
. STB- ARM-, , / . , — .
. Android, — RDK, — Linux . . desktop-. .
. Ethernet wifi. / — .
. .
.
. Zoom - :
/
/
:
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).
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. — , .
Essa ideia de um serviço de videoconferência via Smart TV pode ser desenvolvida ainda mais, tanto em termos de soluções de engenharia quanto de cenários de sua utilização. Então, compartilhe seus pensamentos nos comentários.