search
LoginSignup
15

More than 5 years have passed since last update.

posted at

updated at

app plugin でより深いコードを書く

Day 1 のスライドの45枚目のネタです。

GStreamer には、app plugin という、プラグインを作成せずともエレメント内部の処理をユーザーが実装できる汎用的なエレメントがあります。
app plugin を使うことで、v4l2を直接叩いてカメラを制御したり、処理したデータを別のプログラムに渡すなどが行えるようになるので「Element を書くほどじゃないけど特殊なことをしたい」そんな時に便利です。

app plugin には appsrcappsink の2つがあります。
詳しくは inspect で確認してください。

$ gst-inspect-1.0 appsrc
$ gst-inspect-1.0 appsink

今回は例として、appsink が受け取ったフレームを数えるプログラムを作ります。

videotestsrc num-buffers=30 ! appsink

inspect で callback のインターフェースを確認

$ gst-inspect-1.0 appsink

[..]

Element Signals:
  "eos" :  void user_function (GstElement* object,
                               gpointer user_data);
  "new-preroll" :  GstFlowReturn user_function (GstElement* object,
                                                gpointer user_data);
  "new-sample" :  GstFlowReturn user_function (GstElement* object,
                                               gpointer user_data);

appsink がフレームを受け取った時の処理にはnew-sampleというコールバックを使います。

ここで抑えておくのは、戻り(GstFlowReturn)と引数(object, data)です。

ベースとなるコードを実装

pipeline
videotestsrc ! autovideosink

この pipeline を C で実装すると以下のようになります。

code
#incude <gst/gst.h>

int main(int argc, char *argv[])
{
    GString *launch_str;
    GstElement *pipeline;
    GstBus *bus;
    GstMessage *msg;

    gst_init(&argc, &argv);

    launch_str = g_string_new(NULL);
    g_string_printf(launch_str,
        "videotestsrc is-live=true num-buffers=30 ! autovideosink", NULL);

    pipeline = gst_parse_launch(launch_str->str, NULL);

    if (pipeline == NULL) {
        g_print("parse_launch fail.\n");
        return 1;
    }

    bus = gst_element_get_bus(pipeline);

    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    gint handle_msg;
    handle_msg = (GST_MESSAGE_EOS | GST_MESSAGE_ERROR);
    msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)handle_msg);

    GError *err;
    gchar *debug_info;
    if (msg != NULL)
        switch (GST_MESSAGE_TYPE(msg)) {
        case GST_MESSAGE_ERROR:
            gst_message_parse_error(msg, &err, &debug_info);
            g_printerr("Error received from element %s: %s\n", GST_OBJECT_NAME(msg->src), err->message);
            g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");
            g_clear_error(&err);
            g_free(debug_info);
            break;
        case GST_MESSAGE_EOS:
            g_print("End-Of-Stream reached.\n");
            break;
        default:
            /* We should not reach here because we only asked for ERRORs and EOS */
            g_printerr("Unexpected message received.\n");
            break;
        }
    gst_object_unref(bus);
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(pipeline);

    return 0;
}

コードに修正を加える

パイプラインに Appsink を追加

appsink を追加するほかに抑えておかなければならないのは、appsinkのプロパティにemit-signals=trueを設定すること。
これを行わなければ、コールバック関数を実装・登録しても実行されません。

@@ -11,7 +11,7 @@ int main(int argc, char *argv[])

        launch_str = g_string_new(NULL);
        g_string_printf(launch_str,
-               "videotestsrc is-live=true num-buffers=30 ! autovideosink", NULL);
+               "videotestsrc is-live=true num-buffers=30 ! appsink name=appsink emit-signals=true", NULL);

        pipeline = gst_parse_launch(launch_str->str, NULL);

パイプラインから Appsink を取得する

次にパイプラインから appsink を取得します。

@@ -4,6 +4,7 @@ int main(int argc, char *argv[])
 {
        GString *launch_str;
        GstElement *pipeline;
+       GstElement *appsink;
        GstBus *bus;
        GstMessage *msg;

@@ -20,6 +21,13 @@ int main(int argc, char *argv[])
                return 1;
        }

+       appsink = gst_bin_get_by_name(GST_BIN(pipeline), "appsink");
+       if (appsink == NULL)
+       {
+               g_print("appsink is NULL\n");
+       }
+       gst_object_unref(appsink);
+
        bus = gst_element_get_bus(pipeline);

        gst_element_set_state(pipeline, GST_STATE_PLAYING);

コールバックの実装と登録

最後にコールバック関数のcb_new_sample()を実装し、g_signal_connect()で登録します。

@@ -1,4 +1,19 @@
 #include <gst/gst.h>
+#include <gst/app/gstappsink.h>
+
+GstFlowReturn cb_new_sample(GstAppSink *sink, gpointer data)
+{
+       static int counter = 0;
+       GstSample *sample;
+       g_print("frame: %d\n", counter++);
+
+       sample = gst_app_sink_pull_sample(sink);
+       if (sample == NULL) {
+               return GST_FLOW_EOS;
+       } else {
+               return GST_FLOW_OK;
+       }
+}

 int main(int argc, char *argv[])
 {
@@ -26,6 +41,7 @@ int main(int argc, char *argv[])
        {
                g_print("appsink is NULL\n");
        }
+       g_signal_connect(appsink, "new-sample", G_CALLBACK(cb_new_sample), NULL);
        gst_object_unref(appsink);

        bus = gst_element_get_bus(pipeline);

ビルド・実行

VisualStudio で appsink (または appsrc) を使う場合は、プロパティシートを新たに追加する必要があります。追加するプロパティシートは gstreamer-app-1.0.propsです。

Linux の場合も同様に gstreamer-app-1.0.pc を使います。

$ gcc main.c `pkg-config --libs --cflags gstreamer-app-1.0`

実行すると以下のような表示が出ます。

$ a.out
frame: 0
frame: 1
frame: 2
frame: 3
frame: 4
frame: 5

:

frame: 27
frame: 28
frame: 29
End-Of-Stream reached.

もちろん、videotestsrcnum-buffers の値を変えると、その分表示が変わります。

以上が、appsink を使ったプログラミングの一例でした。これを使えば、mp4 のフレーム数を数えるなどもできますね。

使ってみてください :)

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
15