Плагин Gstreamer не может корректно воспроизводить видео на Android при использовании udpscr

#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