GSTREAMERのPLAYBACK

こんにちは。Ianです。

今、C++を使用してH3上でGSTREAMERのPLAYBACK機能を開発していますが、以下の問題に遭遇しています:

1. filesrc location=%s ! queue ! decodebin use-dmabuf=true ! appsink name=VideoOutputを再生パイプラインとして使用していますが、appsink側で受信されるデータが予想されていたDMAバッファではなく、CPUデータであることがわかりました。
2. 再生中に、g_main_loopと異なるスレッドで以下の指令
gst_element_seek_simple(instance_->pipeline, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH), time * GST_SECOND);
実行すると、必ずデッドロックが発生しています。

関連する情報または解決策をご存知の方はいますでしょうか?お願いします。

Parents
  • こんにちは、Ianです。

    状況を更新します

    https://github.com/renesas-rcar/gst-omx(TAG:1.16.3)をコンパイルして、正常にGStreamerに読み込まれていることを確認しでいました。

    以下はサンプルコード:

    #include <thread>
    #include <vector>
    #include "gst/gst.h"
    #include "gst/video/video.h"
    #include "gst/gl/gl.h"
    #include "gst/gl/gstgldisplay.h"
    #include "gst/app/gstappsink.h"
    #include "gst/allocators/gstdmabuf.h"

    #define SEEK_FLAG (GstSeekFlags)(GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH)
    static gboolean stream_bus_callback(GstBus * bus, GstMessage * message, gpointer data);
    static GstPadProbeReturn appsink_query_cb(GstPad *pad G_GNUC_UNUSED, GstPadProbeInfo *info, gpointer user_data G_GNUC_UNUSED);

    struct Video {
      Video(const char *path);
      ~Video();

      void ProcessStream(const char *path);

      GMainLoop *mainloop;
      GstElement *videosink;
      GstElement *pipeline;
      std::thread video_loop;
    };

    static gboolean stream_bus_callback(GstBus * bus, GstMessage * message, gpointer data)
    {
      Video *instance = reinterpret_cast<Video*>(data);
      auto msgType = GST_MESSAGE_TYPE (message);
      switch ( msgType ) {
        case GST_MESSAGE_ERROR:{
          GError *err;
          gchar *debug;
          gst_message_parse_error (message, &err, &debug);
          g_print ("Error: %s\n", err->message);
          g_error_free (err);
          g_free (debug);

          g_main_loop_quit(instance->mainloop);
          break;
          }

        default: break;
     }

      return TRUE;
    }

    static GstPadProbeReturn appsink_query_cb(GstPad *pad G_GNUC_UNUSED, GstPadProbeInfo *info, gpointer user_data G_GNUC_UNUSED)
    {
     GstQuery *query = reinterpret_cast<GstQuery*>(info->data);
      if (GST_QUERY_TYPE (query) != GST_QUERY_ALLOCATION) return GST_PAD_PROBE_OK;
      gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);
      return GST_PAD_PROBE_HANDLED;
    }

    Video::Video(const char *path)
      : mainloop(nullptr)
      , videosink(nullptr)
      , pipeline(nullptr)
      , video_loop(std::thread(&Video::ProcessStream, this, path))
    {}

    Video::~Video()
    {
      if( mainloop ) g_main_loop_quit(mainloop);
      if( video_loop.joinable() ) video_loop.join();
    }

    void Video::ProcessStream(const char *path)
    {
     GError *err = nullptr;
     char buff[256];
      snprintf(buff, 256, "filesrc location=\"%s\" ! tee name=t ", path);

      gchar *pipelineString = g_strdup_printf("%s "
        "t. ! queue ! decodebin use-dmabuf=true ! appsink name=VideoOutput "
        , buff);

      GstElement *pipe = gst_parse_launch(pipelineString, &err);
      g_free(pipelineString);
      if( err ) {
        printf("Something goes wrong with pipeline initialization: %s\n", err->message);
        g_clear_error (&err);
        return;
      }

     mainloop = g_main_loop_new(NULL, FALSE);
      videosink = gst_bin_get_by_name (GST_BIN(pipe), "VideoOutput");
      GstPad *pad = gst_element_get_static_pad(videosink, "sink");
      gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, appsink_query_cb, NULL, NULL);
      gst_object_unref(pad);
      GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE(pipe));
      guint busId = gst_bus_add_watch (bus, stream_bus_callback, this);
      gst_object_unref (bus);

      gst_element_set_state(GST_ELEMENT(pipe), GST_STATE_PAUSED);
      gst_element_seek_simple(pipe, GST_FORMAT_TIME, SEEK_FLAG, 0.0f);
      gst_element_set_state(pipe, GST_STATE_PLAYING);
      pipeline = pipe;
      g_main_loop_run(mainloop);

      gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL);
      gst_object_unref(GST_OBJECT(pipeline));
      g_source_remove (busId);
      g_main_loop_unref(mainloop);
      mainloop = nullptr;
      pipeline = nullptr;
      if( videosink ) gst_object_unref(videosink);
      videosink = nullptr;
    }

    int main(int argc, char* args[])
    {
      gst_init(nullptr, nullptr);
      Video inst(args[1]);
      while ( true ) {
        if ( !inst.videosink ) {
          printf("not ready\n");
          continue;
        }
        GstSample *sample = gst_app_sink_try_pull_sample(reinterpret_cast<GstAppSink*>(inst.videosink), 10);
        if ( sample ) {
        GstBuffer *buffer = gst_sample_get_buffer(sample);
        int memCount = gst_buffer_n_memory(buffer);
        std::vector<GstMemory*> memoryVec(memCount, nullptr);
        for ( int i=0 ; i<memCount ; ++i ) memoryVec[i] = gst_buffer_peek_memory(buffer, i);
        if ( !gst_is_dmabuf_memory(memoryVec[0]) ) {
          printf("do normal cpu shader convert\n");
          // to do : convert cpu memory to rgb
        } else {
         printf("use dma video\n");
         // eglCreateImageKHR
         // glActiveTexture
         // glBindTexture
         // glEGLImageTargetTexture2DOES

         // draw texture : got black

         // eglDestroyImageKHR
        }
        gst_sample_unref(sample);
       }
     }

      return 0;
    }

    1.の状況の説明:

    • 実行時に、omx/gstomxvideodec.cuse_dmabuftrueであることを確認しました。

    • gst_is_dmabuf_memorytrueであることを確認しました

    • テスト時にはGstVideoMetaのデータが出力され、その解像度とストライドはビデオデータに一致しており、gst_dmabuf_memory_get_fdはそれぞれ97および100を取得しており、正常に見える。

    • main.cppでテクスチャを描画する際、"got black"の部分が異常な部分であり、描画されるのは完全に黒い画像です。

    • 同じパイプラインが他のプラットフォームで正常に動作することを確認しているため、EGLイメージをGLテクスチャに変換する部分は正常だと思います。

    上記の状況に基づいて、appsinkが受信したdmabufがこの方法をサポートしていないか、または取得したdmabufIDが実際にはデコーダーの出力するdmabufではないかを確認したいと思います。

    2.の状況の説明:

    • avdec_h264を使用してソフトデコードする場合、seekはデッドロックを引き起こしません。

    • omxh264decスレッドのデッドロックが以下のスタックで発生していることを確認しました: gst_omx_port_acquire_buffer:2219 → gstomx.c:487

    この状況は、未処理のメッセージがあるかどうかを確認する必要がありそうです。確認していただけると助かります。

    よろしくおねがいします。

Reply
  • こんにちは、Ianです。

    状況を更新します

    https://github.com/renesas-rcar/gst-omx(TAG:1.16.3)をコンパイルして、正常にGStreamerに読み込まれていることを確認しでいました。

    以下はサンプルコード:

    #include <thread>
    #include <vector>
    #include "gst/gst.h"
    #include "gst/video/video.h"
    #include "gst/gl/gl.h"
    #include "gst/gl/gstgldisplay.h"
    #include "gst/app/gstappsink.h"
    #include "gst/allocators/gstdmabuf.h"

    #define SEEK_FLAG (GstSeekFlags)(GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH)
    static gboolean stream_bus_callback(GstBus * bus, GstMessage * message, gpointer data);
    static GstPadProbeReturn appsink_query_cb(GstPad *pad G_GNUC_UNUSED, GstPadProbeInfo *info, gpointer user_data G_GNUC_UNUSED);

    struct Video {
      Video(const char *path);
      ~Video();

      void ProcessStream(const char *path);

      GMainLoop *mainloop;
      GstElement *videosink;
      GstElement *pipeline;
      std::thread video_loop;
    };

    static gboolean stream_bus_callback(GstBus * bus, GstMessage * message, gpointer data)
    {
      Video *instance = reinterpret_cast<Video*>(data);
      auto msgType = GST_MESSAGE_TYPE (message);
      switch ( msgType ) {
        case GST_MESSAGE_ERROR:{
          GError *err;
          gchar *debug;
          gst_message_parse_error (message, &err, &debug);
          g_print ("Error: %s\n", err->message);
          g_error_free (err);
          g_free (debug);

          g_main_loop_quit(instance->mainloop);
          break;
          }

        default: break;
     }

      return TRUE;
    }

    static GstPadProbeReturn appsink_query_cb(GstPad *pad G_GNUC_UNUSED, GstPadProbeInfo *info, gpointer user_data G_GNUC_UNUSED)
    {
     GstQuery *query = reinterpret_cast<GstQuery*>(info->data);
      if (GST_QUERY_TYPE (query) != GST_QUERY_ALLOCATION) return GST_PAD_PROBE_OK;
      gst_query_add_allocation_meta(query, GST_VIDEO_META_API_TYPE, NULL);
      return GST_PAD_PROBE_HANDLED;
    }

    Video::Video(const char *path)
      : mainloop(nullptr)
      , videosink(nullptr)
      , pipeline(nullptr)
      , video_loop(std::thread(&Video::ProcessStream, this, path))
    {}

    Video::~Video()
    {
      if( mainloop ) g_main_loop_quit(mainloop);
      if( video_loop.joinable() ) video_loop.join();
    }

    void Video::ProcessStream(const char *path)
    {
     GError *err = nullptr;
     char buff[256];
      snprintf(buff, 256, "filesrc location=\"%s\" ! tee name=t ", path);

      gchar *pipelineString = g_strdup_printf("%s "
        "t. ! queue ! decodebin use-dmabuf=true ! appsink name=VideoOutput "
        , buff);

      GstElement *pipe = gst_parse_launch(pipelineString, &err);
      g_free(pipelineString);
      if( err ) {
        printf("Something goes wrong with pipeline initialization: %s\n", err->message);
        g_clear_error (&err);
        return;
      }

     mainloop = g_main_loop_new(NULL, FALSE);
      videosink = gst_bin_get_by_name (GST_BIN(pipe), "VideoOutput");
      GstPad *pad = gst_element_get_static_pad(videosink, "sink");
      gst_pad_add_probe(pad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, appsink_query_cb, NULL, NULL);
      gst_object_unref(pad);
      GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE(pipe));
      guint busId = gst_bus_add_watch (bus, stream_bus_callback, this);
      gst_object_unref (bus);

      gst_element_set_state(GST_ELEMENT(pipe), GST_STATE_PAUSED);
      gst_element_seek_simple(pipe, GST_FORMAT_TIME, SEEK_FLAG, 0.0f);
      gst_element_set_state(pipe, GST_STATE_PLAYING);
      pipeline = pipe;
      g_main_loop_run(mainloop);

      gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_NULL);
      gst_object_unref(GST_OBJECT(pipeline));
      g_source_remove (busId);
      g_main_loop_unref(mainloop);
      mainloop = nullptr;
      pipeline = nullptr;
      if( videosink ) gst_object_unref(videosink);
      videosink = nullptr;
    }

    int main(int argc, char* args[])
    {
      gst_init(nullptr, nullptr);
      Video inst(args[1]);
      while ( true ) {
        if ( !inst.videosink ) {
          printf("not ready\n");
          continue;
        }
        GstSample *sample = gst_app_sink_try_pull_sample(reinterpret_cast<GstAppSink*>(inst.videosink), 10);
        if ( sample ) {
        GstBuffer *buffer = gst_sample_get_buffer(sample);
        int memCount = gst_buffer_n_memory(buffer);
        std::vector<GstMemory*> memoryVec(memCount, nullptr);
        for ( int i=0 ; i<memCount ; ++i ) memoryVec[i] = gst_buffer_peek_memory(buffer, i);
        if ( !gst_is_dmabuf_memory(memoryVec[0]) ) {
          printf("do normal cpu shader convert\n");
          // to do : convert cpu memory to rgb
        } else {
         printf("use dma video\n");
         // eglCreateImageKHR
         // glActiveTexture
         // glBindTexture
         // glEGLImageTargetTexture2DOES

         // draw texture : got black

         // eglDestroyImageKHR
        }
        gst_sample_unref(sample);
       }
     }

      return 0;
    }

    1.の状況の説明:

    • 実行時に、omx/gstomxvideodec.cuse_dmabuftrueであることを確認しました。

    • gst_is_dmabuf_memorytrueであることを確認しました

    • テスト時にはGstVideoMetaのデータが出力され、その解像度とストライドはビデオデータに一致しており、gst_dmabuf_memory_get_fdはそれぞれ97および100を取得しており、正常に見える。

    • main.cppでテクスチャを描画する際、"got black"の部分が異常な部分であり、描画されるのは完全に黒い画像です。

    • 同じパイプラインが他のプラットフォームで正常に動作することを確認しているため、EGLイメージをGLテクスチャに変換する部分は正常だと思います。

    上記の状況に基づいて、appsinkが受信したdmabufがこの方法をサポートしていないか、または取得したdmabufIDが実際にはデコーダーの出力するdmabufではないかを確認したいと思います。

    2.の状況の説明:

    • avdec_h264を使用してソフトデコードする場合、seekはデッドロックを引き起こしません。

    • omxh264decスレッドのデッドロックが以下のスタックで発生していることを確認しました: gst_omx_port_acquire_buffer:2219 → gstomx.c:487

    この状況は、未処理のメッセージがあるかどうかを確認する必要がありそうです。確認していただけると助かります。

    よろしくおねがいします。

Children
No Data