作って学ぶGStreamerエレメント


Debian GNU/Linux (amd64, stretch)にて確認。

この文書では、以下のGStreamerエレメントを作る

* guint32をインクリメントして出力し続けるnumbersrc
* numbersrcを受けて数字を２倍して出力するtwicefilter
* 結果を出力するnumbersink

[チュートリアル](https://gstreamer.freedesktop.org/documentation/plugin-development/basics/index.html)等を参照しながら手探りで実行した結果なので間違いあるかも。

# 前準備

## パッケージインストール

```bash
$ sudo apt install build-essential libgstreamer1.0-dev indent
```

エレメント雛形作成スクリプト(gst-element-maker)がGNUindentを使用するため一緒にインストールしている。

## ツールのコピー

Debianだと残念ながらgst-plugins-badに入っているはずの`gst-element-maker`が無いため、リポジトリからコピーする。

```bash
$ git clone https://anongit.freedesktop.org/git/gstreamer/gst-plugins-bad.git
$ cd gst-plugsins-bad/tools
$ cp -r gst-element-maker element-templates <パスが通ったディレクトリ>
```

あと`gst-element-maker`から呼ばれる`gst-indent`もコピーする。

```bash
$ git clone https://anongit.freedesktop.org/git/gstreamer/gstreamer.git
$ cd gstreamer/tools
$ cp gst-indent <パスが通ったディレクトリ>
```

# エレメント作成

## ソースとシンクを作る

basesrcを継承してnumbersrc, basesinkを継承してnumbersinkを作成する。

```bash
$ gst-element-maker numbersrc basesrc
$ gst-element-maker numbersink basesink
```

gstnumbersrc.c, gstnumbersrc.h, gstnumbersink.c, gstnumbersink.hの４ファイルが作成され、ビルドまでしてgstnumbersrc.so, gstnumbersink.soが作成される。

Makefileを追加しビルド出来るようにしたものが以下
https://github.com/maueki/learning-gstreamer/tree/tag_add_makefile

だが実行してみるとエラーがでてしまう。

```base
$ GST_PLUGIN_PATH=. gst-launch-1.0 numbersrc ! numbersink

(gst-launch-1.0:36307): GStreamer-CRITICAL **: gst_mini_object_ref: assertion 'mini_object != NULL' failed
(以下エラーいろいろ)
WARNING: erroneous pipeline: numbersrc0 を numbersink0 へリンクできません
```

原因は`gst-element-maker`で作成されたファイルではGstBaseSrcやGstBaseSinkのvmethodをオーバーライドしているが、NULLを返してしまったりとあまりよろしくない実装をしてしまっているためである。

ソース側は`fill()`、シンク側は`render()`がオーバーライドされていれば最低限動作するため、それ以外のオーバーロードを無効にしたものが以下
https://github.com/maueki/learning-gstreamer/tree/tag_1st_run

ビルドして実行してみると

```bash
$ GST_PLUGIN_PATH=. gst-launch-1.0 numbersrc ! numbersink                             
パイプラインを一時停止 (PAUSED) にしています...     
Pipeline is PREROLLING ...                          
Pipeline is PREROLLED ...                           
パイプラインを再生中 (PLAYING) にしています...      
New clock: GstSystemClock  
```

無事動作した。

## ソースからシンクへデータを受け渡す

ソースの`gst_numbersrc_fill()`を修正しデータを送ってみる

```c:gstnumbersrc.c
static GstFlowReturn
gst_numbersrc_fill (GstBaseSrc * src, guint64 offset, guint size,
    GstBuffer * buf)
{
  static guint32 i = 0;
  GstNumbersrc *numbersrc = GST_NUMBERSRC (src);
  GstMapInfo info;

  GST_DEBUG_OBJECT (numbersrc, "fill");

  if (size < sizeof(i)) {
      GST_ERROR_OBJECT(numbersrc, "gst_numbersrc_fill: size too short");
      return GST_FLOW_ERROR;
  }

  if (!gst_buffer_map (buf, &info, GST_MAP_WRITE))
    return GST_FLOW_ERROR;

  guint8 *data = info.data;
  memcpy(data, &i, sizeof(i));
  i++;

  gst_buffer_unmap (buf, &info);
  gst_buffer_resize(buf, 0, sizeof(i));

  return GST_FLOW_OK;
}
```

`gst_numbersrc_fill()`が呼び出されるたびにインクリメントされる変数のメモリ内容を送るだけのものだ。

シンク側は送られてきたバッファを`gst_util_dump_mem()`でダンプする。

```c:gstnumbersink.c
static GstFlowReturn
gst_numbersink_render (GstBaseSink * sink, GstBuffer * buffer)
{
  GstNumbersink *numbersink = GST_NUMBERSINK (sink);

  GST_DEBUG_OBJECT (numbersink, "render");

  GstMapInfo info;

  if (gst_buffer_map (buffer, &info, GST_MAP_READ)) {
      gst_util_dump_mem (info.data, info.size);
      gst_buffer_unmap (buffer, &info);
  }

  return GST_FLOW_OK;
}
```

実行してみる

```bash
$ GST_PLUGIN_PATH=. gst-launch-1.0 numbersrc ! numbersink
(snip)
00000000 (0x7ff294004490): 14 35 05 00                                      .5..                                                                                                                                   
00000000 (0x7ff294007090): 15 35 05 00                                      .5..                                                                                                                                   
00000000 (0x7ff294004490): 16 35 05 00                                      .5..                                                                                                                                   
00000000 (0x7ff294007090): 17 35 05 00                                      .5..                                                                                                                                   
00000000 (0x7ff294004490): 18 35 05 00                                      .5..                                                                                                                                   
00000000 (0x7ff294007090): 19 35 05 00                                      .5.. 
```

インクリメントされたメモリ内容がダンプされているのが見て取れる。

## トランスフォームエレメントの作成

numbersrcを受けて２倍にして出力するtwicefilterを作ってみる

```bash
$ gst-element-maker twicefilter basetransform
```

ここではベースクラスとしてソース、シンクが1対1で対応するbasetransformを使用した。
例によって自動生成された実装はあまりよろしくないため、base_transform_class.transformへの登録以外はコメントアウトし、`gst_twicefilter_transform()`を以下のように実装した

```c:gsttwicefilter.c
/* transform */
static GstFlowReturn
gst_twicefilter_transform (GstBaseTransform * trans, GstBuffer * inbuf,
    GstBuffer * outbuf)
{
  GstTwicefilter *twicefilter = GST_TWICEFILTER (trans);

  GST_DEBUG_OBJECT (twicefilter, "transform");

  GstMapInfo iinfo;
  if (!gst_buffer_map (inbuf, &iinfo ,GST_MAP_READ))
      return GST_FLOW_ERROR;

  guint32 i=0;
  memcpy(&i, iinfo.data, sizeof(i));
  gst_buffer_unmap(inbuf, &iinfo);

  i*=2;

  GstMapInfo oinfo;
  if (!gst_buffer_map(outbuf, &oinfo, GST_MAP_WRITE))
      return GST_FLOW_ERROR;

  memcpy(oinfo.data, &i, sizeof(i));

  gst_buffer_unmap(outbuf, &oinfo);
  gst_buffer_resize(outbuf, 0, sizeof(i));

  return GST_FLOW_OK;
}
```

かなりテキトウな実装だけどご勘弁。`inbuf`から数字を取り出し２倍して`outbuf`へ格納しているだけ。

ビルドしたら`gst-launch-1.0`で動作を確認してみる

```bash
$ GST_PLUGIN_PATH=. gst-launch-1.0 numbersrc ! twicefilter ! numbersink
(snip)
00000000 (0x55870716e6f0): 90 a7 01 00                                      ....                         
00000000 (0x55870716e790): 92 a7 01 00                                      ....                         
00000000 (0x7fd3140060a0): 94 a7 01 00                                      ....                         
00000000 (0x7fd314006140): 96 a7 01 00                                      ....                         
00000000 (0x7fd3140061e0): 98 a7 01 00                                      ....                         
00000000 (0x7fd314006280): 9a a7 01 00                                      .... 
```

数字が２つ飛びになっており、たしかに２倍されていることがわかる。