#android #video-streaming #gstreamer #gstreamer-1.0
#Android #потоковое видео #gstreamer #gstreamer-1.0
Вопрос:
У меня возникла некоторая проблема при реализации плагина gstreamer для воспроизведения RTP-видео на Android. У меня есть следующий код (который работает правильно):
full_pipeline_description = g_strdup_printf("playbin3 uri=%s", uri);
gub_log_pipeline(pipeline, "Using pipeline: %s", full_pipeline_description);
pipeline->pipeline = gst_parse_launch(full_pipeline_description, amp;err);
g_free(full_pipeline_description);
if (err) {
gub_log_pipeline(pipeline, "Failed to create pipeline: %s", err->message);
return;
}
vsink = gst_parse_bin_from_description(gub_get_video_branch_description(), TRUE, NULL);
gub_log_pipeline(pipeline, "Using video sink: %s", gub_get_video_branch_description());
g_object_set(pipeline->pipeline, "video-sink", vsink, NULL);
g_object_set(pipeline->pipeline, "flags", 0x0003, NULL);
bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline->pipeline));
gst_bus_add_signal_watch(bus);
gst_object_unref(bus);
g_signal_connect(bus, "message", G_CALLBACK(message_received), pipeline);
if (vsink) {
// Plant a pad probe to answer context queries
GstElement *sink;
sink = gst_bin_get_by_name(GST_BIN(vsink), "sink");
if (sink) {
GstPad *pad = gst_element_get_static_pad(sink, "sink");
if (pad) {
gulong id = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, pad_probe, pipeline, NULL);
gst_object_unref(pad);
}
gst_object_unref(sink);
}
}
А использование того же кода с другим конвейером (на основе udpsrc вместо playbin3) — нет. Конвейер, который я использую в этом случае, является:
порт udpsrc = 53512! приложение / x-rtp, медиа = видео, тактовая частота= 90000, имя кодировки = H264, полезная нагрузка = 96 ! rtph264depay! decodebin3! загружайте! glcolorconvert! видео / x-raw (память: GLMemory), формат = RGBA, текстура-цель= 2D ! синхронизация fakesink = 0 qos = 1 имя = приемник
Код выглядит следующим образом:
full_pipeline_description = g_strdup_printf("%s", pipeline_cmd);
gub_log_pipeline(pipeline, "Using pipeline: %s", full_pipeline_description);
pipeline->pipeline = gst_parse_launch(full_pipeline_description, amp;err);
g_free(full_pipeline_description);
if (err) {
gub_log_pipeline(pipeline, "Failed to create pipeline: %s", err->message);
return;
}
vsink = gst_parse_bin_from_description(gub_get_video_branch_description(), TRUE, NULL);
gub_log_pipeline(pipeline, "Using video sink: %s", gub_get_video_branch_description());
g_object_set(pipeline->pipeline, "sink", vsink, NULL);
g_object_set(pipeline->pipeline, "flags", 0x0003, NULL);
bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline->pipeline));
gst_bus_add_signal_watch(bus);
gst_object_unref(bus);
g_signal_connect(bus, "message", G_CALLBACK(message_received), pipeline);
// Plant a pad probe to answer context queries
GstElement *sink;
sink = gst_bin_get_by_name(GST_BIN(vsink), "sink");
if (sink) {
GstPad *pad = gst_element_get_static_pad(sink, "sink");
if (pad) {
gulong id = gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, pad_probe, pipeline, NULL);
gst_object_unref(pad);
}
gst_object_unref(sink);
}
По сути, в этом случае я просто вижу закрывающееся окно (с разными цветами). Единственное отличие в выполнении заключается в том, что pad_probe вызывается при использовании playbin3, но не при использовании udpsrc. Это единственное отличие, которое я вижу, добавляя некоторые журналы. Я хотел бы понять, почему этот обратный вызов не вызывается при использовании udpsrc и если я что-то упускаю или использую это неправильно.
Я сталкиваюсь с одинаковыми проблемами при использовании версий gstreamer 1.14.4 и 1.16.2. Любые подсказки более чем приветствуются.
Ответ №1:
g_object_set(pipeline->pipeline, "sink", vsink, NULL);
на самом деле ничего не делает; у GstPipeline нет свойства «sink» (в отличие от playbin). Обычно он выдает предупреждение журнала, в котором говорится именно об этом.
Чтобы добавить свой приемник в конвейер, вам нужно сделать это так, как вы обычно делаете в приложении GStreamer: вы находите исходные панели, к которым он должен быть подключен, или вы ждете, пока правильный исходный блок, с которым его нужно связать, не появится в сигнале «добавленный блок» (это происходит, например, в decodebin
).
Ответ №2:
Наконец, после некоторых исследований и на основе этой темы Gstreamer devel я нашел основную причину проблемы. В принципе, как я и подозревал, обратный вызов для pad probe не был вызван при использовании udpsrc, он работал только при использовании playbin3. Как следствие, не был предоставлен графический контекст, и видео воспроизводилось некорректно. Чтобы решить эту проблему, мне пришлось добавить логику для обработки сообщений на шине, чтобы правильно отвечать на запрос GST_MESSAGE_NEED_CONTEXT. Для этого сначала вам нужно подключить обратный вызов для обработки сообщений шины следующим образом:
g_signal_connect (шина, «сообщение», G_CALLBACK (message_received), конвейер);
Затем в функции message_received я добавил следующий код.
static void message_received(GstBus *bus, GstMessage *message, GUBPipeline *pipeline) {
switch (GST_MESSAGE_TYPE(message)) {
...
case GST_MESSAGE_NEED_CONTEXT:
{
const gchar *context_type;
GstContext *context = NULL;
gst_message_parse_context_type (message, amp;context_type);
context = gub_provide_graphic_context(pipeline->graphic_context, context_type);
if (context)
{
gst_element_set_context (GST_ELEMENT (message->src), context);
gst_context_unref (context);
}
break;
}
...
}
С этими изменениями теперь я могу правильно получать и воспроизводить видео. Видеопоток RTP моделируется с помощью инструмента ffmpeg testsrc следующим образом:
ffmpeg -f lavfi -i testsrc -vf scale=1280:960 -vcodec libx264 -profile:v baseline -pix_fmt yuv420p -f rtp rtp://YOUR_IP:PORT