Day 1 のスライドの45枚目のネタです。
GStreamer には、app plugin という、プラグインを作成せずともエレメント内部の処理をユーザーが実装できる汎用的なエレメントがあります。
app plugin を使うことで、v4l2を直接叩いてカメラを制御したり、処理したデータを別のプログラムに渡すなどが行えるようになるので「Element を書くほどじゃないけど特殊なことをしたい」そんな時に便利です。
app plugin には appsrc
と appsink
の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)です。
ベースとなるコードを実装
videotestsrc ! autovideosink
この pipeline を C で実装すると以下のようになります。
#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.
もちろん、videotestsrc
の num-buffers
の値を変えると、その分表示が変わります。
以上が、appsink を使ったプログラミングの一例でした。これを使えば、mp4 のフレーム数を数えるなどもできますね。
使ってみてください :)