🎠

gstreamer  のエレメントを作る

2022/07/08に公開

Jetson Orin でgstreamerのエレメントを作ってみます。

準備

参考にしたページ
https://developer.ridgerun.com/wiki/index.php/Creating_a_New_GStreamer_Element_or_Application_Using_Templates

gst-plugins-bad/tools にあるgst-element-makerを使いたいのでその準備をします。
Jetson Orinのgstreamerは1.16.2なので、それに合わせます。

$ cd ~/work/src/gst/
$ git clone https://github.com/GStreamer/gst-plugins-bad.git
$ cd gst-plugins-bad
$ git checkout -b w1.16.2 1.16.2
$ sudo apt install autopoint automake
$ NOCONFIGURE=1 ./autogen.sh

$ sudo cp common/gst-indent /usr/local/bin/
$ sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-good1.0-dev libgstreamer-plugins-bad1.0-dev indent

gst-indentはgstreamerの流儀でのソースコードの整形をおこなってくれます。

$ gst-indent --help
usage: indent file [-o outfile ] [ options ]
       indent file1 file2 ... fileN [ options ]

myfakesink の作成

filesinkの構成を調べると、これはGstBaseSinkを継承していることがわかりました。これを真似します。

$ gst-inspect-1.0 filesink

 ...
 
GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstBaseSink
                         +----GstFileSink

basesinkを雛形にしてエレメントのソースコードを生成します。

$ mkdir myfakesink
$ cd myfakesink/
$  ~/work/src/gst/gst-plugins-bad/tools/gst-element-maker myfakesink basesink

自動で、gstmyfakesink.h, gstmyfakesink.cを生成して、そこから共有ライブラリをビルドするところまでやってくれました。
ビルドしたときにどのようなオプションを使ったのかを知るために、もう一度ログをとりながら実行します。

$ sh -x ~/work/src/gst/gst-plugins-bad/tools/gst-element-maker myfakesink basesink > log 2>&1

これをもとに以下のようなMakefileを作成しました。

Makefile
TARGET = libgstmyfakesink.so
OBJS = gstmyfakesink.o

CC = gcc
PACKAGES = gstreamer-1.0 gstreamer-base-1.0
CFLAGS = -Wall -Werror -fPIC $(shell pkg-config --cflags $(PACKAGES))
LDLIBS = $(shell pkg-config --libs $(PACKAGES))

$(TARGET): $(OBJS)
	$(CC) -shared -o $@ $< $(LDLIBS)

clean:
	rm -f $(TARGET) $(OBJS)

install: $(TARGET)
	cp $(TARGET) ${HOME}/.local/share/gstreamer-1.0/plugins/
%.o: %.c
	gst-indent $<
	$(CC) $(CFLAGS) -c -o $@ $<
$ make install
gst-indent gstmyfakesink.c
gcc -Wall -Werror -fPIC -pthread -I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/aarch64-linux-gnu/glib-2.0/include -c -o gstmyfakesink.o gstmyfakesink.c
gcc -shared -o libgstmyfakesink.so gstmyfakesink.o -lgstbase-1.0 -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0
cp libgstmyfakesink.so /home/koba/.local/share/gstreamer-1.0/plugins/

以下のスクリプトで動かしてみます。

test.sh
#!/bin/sh -eux

WIDTH=1280
HEIGHT=720
FRAMERATE=30

GST_DEBUG_NO_COLOR=1 GST_DEBUG=myfakesink:6 gst-launch-1.0 -v v4l2src device="/dev/video0" ! \
    "image/jpeg, width=$WIDTH, height=$HEIGHT, framerate=$FRAMERATE/1" ! \
    nvv4l2decoder mjpeg=1 ! 'video/x-raw(memory:NVMM)' ! \
    nvvidconv ! 'video/x-raw, format=(string)I420' ! \
    myfakesink

gstfilesink.cをまねして以下のように修正したらストリームが流れるようになりました。

@@ -90,8 +90,7 @@ static GstStaticPadTemplate gst_myfakesink_sink_template =
 GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("application/unknown")
-    );
+    GST_STATIC_CAPS_ANY);
 
 
 /* class initialization */
@@ -317,11 +316,20 @@ gst_myfakesink_unlock_stop (GstBaseSink * sink)
 static gboolean
 gst_myfakesink_query (GstBaseSink * sink, GstQuery * query)
 {
+  gboolean res;
   GstMyfakesink *myfakesink = GST_MYFAKESINK (sink);
 
-  GST_DEBUG_OBJECT (myfakesink, "query");
+  GST_DEBUG_OBJECT (myfakesink, "query: GST_QUERY_TYPE (query)=%d",
+      GST_QUERY_TYPE (query));
 
-  return TRUE;
+  switch (GST_QUERY_TYPE (query)) {
+    default:
+      res =
+          GST_BASE_SINK_CLASS (gst_myfakesink_parent_class)->query (sink,
+          query);
+      break;
+  }
+  return res;
 }
 
 /* notify subclass of event */

これで動作を確認しつつ、実装を増やしていきます。

余談

自作したエレメントをどこに置いたら認識してもらえるのか? 実験途中のものをいきなり  /lib/aarch64-linux-gnu/gstreamer-1.0/ に置くのは気が引けますね。間違って余計なものを消してしまう恐れもあるし。
結果として ${HOME}/.local/share/gstreamer-1.0/plugins/に置けばよいということがわかったのですが、それは strace -f -o st.log gst-inspect-1.0 myfakesink とやって、そのログでオープンしているディレクトリから見当をつけました。
そこから検索して、公式ドキュメントの記載を見つけました。
https://gstreamer.freedesktop.org/documentation/gstreamer/running.html?gi-language=c

余談2

makeの中でgst-indent を実行するのはあまりよくないかなと思って、いったんそれを削除。
そうすると、 .c から .oを作るとところはデフォルトのままでよいのでレシピを削除。
Makefileはこうなりました。

Makefile
TARGET = libgstmyfakesink.so
OBJS = gstmyfakesink.o

CC = gcc
PACKAGES = gstreamer-1.0 gstreamer-base-1.0
CFLAGS = -Wall -fPIC $(shell pkg-config --cflags $(PACKAGES))
LDLIBS = $(shell pkg-config --libs $(PACKAGES))

$(TARGET): $(OBJS)
	$(CC) -shared -o $@ $< $(LDLIBS)

clean:
	rm -f $(TARGET) $(OBJS)

install: $(TARGET)
	cp $(TARGET) ${HOME}/.local/share/gstreamer-1.0/plugins/

makeのデフォルトのレシピは以下のようにすることで確認できます。

$ make -p |less

デフォルトは以下のようになっていることがわかります。

...

# default
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

...

%.o: %.c
#  recipe to execute (built-in):
        $(COMPILE.c) $(OUTPUT_OPTION) $<

余談3

Makefileにヘッダファイルに依存関係を書いていませんでした。こちらの記事で追加しました。
https://zenn.dev/tetsu_koba/articles/433fa58dfc8d54

参考

https://zenn.dev/tetsu_koba/articles/bf939ff3621cc0

Discussion