Gstreamer勉強メモ

Gstreamerは、マルチメディアに対し様々な処理を行うことができるライブラリ・ツール・フレームワーク。
コマンドとして実行することもできるし、各種プログラミング言語でライブラリも用意されている。

# 基本的な使い方
メディアに対して1つの処理を行う「エレメント」と呼ばれる機能を、パイプ（Linuxコマンド等で使われるパイプと同様）で繋ぎ、パイプラインを構築する。
パイプラインにメディアを流し、先頭のエレメントから順に処理を行っていくイメージだ。

例えば、PCに接続されたカメラなどのデバイスから映像を取得するエレメントである`v4l2src`と、
動画をH264形式にエンコードするエレメントである`x264enc`を連結させ、実行するとH264形式でエンコードしたカメラ映像を取得することができる。

パイプラインをコマンドで実行する場合は、`gst-launch-1.0`コマンドを使用する。
上記の処理をLinuxコマンドで実行する場合は、下記のようになる。
パイプの記号はLinuxコマンドでは`|`だが、Gstreamerではエクスクラメーションマークを使用する。

```bash
gst-launch-1.0 v4l2src ! x264enc
```

ただし、実は上記例では動かない。エラーが出力されるはずだ。

エレメントには一つ前のエレメントからメディアを受け取る口と、処理した後のメディアを送り出す口が存在する。
受け取る口をSink Padと呼び、送り出す口をSource Padと呼ぶ。
それぞれのPadには、こういった形式のメディアを送り出しますよ、こういった形式のメディアを受け取りますよ、という決まりがあり、どういった形式を受け取るかはエレメントによって異なる。

上記例での`v4l2src`のSource Padから送り出したメディアが、`x264enc`のSink Padで受け付けているメディアの形式に合っていなければパイプラインはエラーとなってしまう。
各エレメントがどういった形式のメディアを送り出すのか、受付するのかは`gst-inspect-1.0`コマンドで調べることができる。

```bash
gst-inspect-1.0 v4l2src
```

上記コマンドを実行すると、`v4l2src`エレメントの説明が表示される。
`Pad Templates`という項目に、各Padがどういった形式のメディアを扱うかが記載されている。

```
Pad Templates:
  SRC template: 'src'
    Availability: Always
    Capabilities:
      image/jpeg
      video/mpeg
            mpegversion: 4
           systemstream: false
      video/mpeg
            mpegversion: 2
      ...（省略）

```

`Capabilities`という項目の中にある通り、メディアの形式のことは「ケイパビリティ」と呼ぶ。
ケイパビリティが複数記述されているのは、デバイスによってケイパビリティが異なるためだ。


デバイスがどういったケイパビリティのメディアを送り出すのかを調べるには、`gst-device-monitor-1.0`コマンドを使用する。

```bash
gst-device-monitor-1.0
```

実行すると、下記のようにデバイスの情報のほか、Gstreamerで何のエレメントを使えばメディアを取り出せるかの情報も表示される。

```
Device found:

        name  : vi-output, ****** 2-0070
        class : Video/Source
        caps  : video/x-raw, format=(string)UYVY, width=(int)1280, height=(int)720, framerate=(fraction)30/1;
        properties:
                udev-probed = true
        ...（省略）
```

`caps`という項目がケイパビリティである。
このデバイスは、formatがUYVYで解像度が1280x720、fpsが30の、生の映像データ（video/x-raw）を出力することがわかる。

ここで`x264enc`のSink Padを調べると、下記のように表示される。

```
  SINK template: 'sink'
    Availability: Always
    Capabilities:
      video/x-raw
              framerate: [ 0/1, 2147483647/1 ]
                  width: [ 16, 2147483647 ]
                 height: [ 16, 2147483647 ]
                 format: { (string)Y444, (string)Y42B, (string)I420, (string)YV12, (string)NV12, (string)Y444_10LE, (string)I422_10LE, (string)I420_10LE }

```

fpsは0から2147483647、フレーム幅は16から2147483647、フレーム高さは16から2147483647、formatは上記に表示されているものを受け付ける、と言う意味だが、formatの候補の中にUYVYが存在しない。
そのため、下記パイプラインではエラーになってしまうというわけだ。

```bash
gst-launch-1.0 v4l2src ! x264enc
```

このようにエレメント間でformatがマッチしない場合は、`videoconvert`というエレメントを間に挟むことで解決できる。
`videoconvert`は受け取ったメディアのformatを、次のエレメントが受け付けられるformatに自動で変換してくれる。

```bash
gst-launch-1.0 v4l2src ! videoconvert ! x264enc
```

上記でパイプライン自体は問題ないはずだが、実際に実行するとまだエラーが表示されるはずだ。
`x264enc`のSource Padから送り出されるメディアを最終的にどうするかが指定されていないためだ。
Source Padに対応するSink Padが指定されていないと、エラーとなってしまう。

最終的にメディアをどう出力するかもエレメントで指定する。
例えば、メディアをファイルに出力する場合は`filesink`というエレメントを使用する。
`filesink`にはSource Padは存在しないため、パイプラインの最後に置けばエラーにはならない。
この他にも画面に映像を表示するエレメントなど、様々な出力用エレメントがある。

```bash
gst-launch-1.0 v4l2src ! videoconvert ! x264enc ! filesink location=/home/test/output.mp4
```

上記コマンドを実行することで、`/home/test/output.mp4`に映像データが蓄積されていく。
`v4l2src`はカメラデバイスで撮影された映像を流し続けるため、処理が自動では終わらない。
十分に蓄積できたところでCtrl+Cで中断する必要がある。
ただ、おそらくファイルを再生することはできないはずだ。

`x264enc`のSource Padを見ると、下記のようになっている。

```
  SRC template: 'src'
    Availability: Always
    Capabilities:
      video/x-h264
              framerate: [ 0/1, 2147483647/1 ]
                  width: [ 1, 2147483647 ]
                 height: [ 1, 2147483647 ]
          stream-format: { (string)avc, (string)byte-stream }
              alignment: au
                profile: { (string)high-4:4:4, (string)high-4:2:2, (string)high-10, (string)high, (string)main, (string)baseline, (string)constrained-baseline, (string)high-4:4:4-intra, (string)high-4:2:2-intra, (string)high-10-intra }
```

stream-formatが、avcかbyte-streamの2択になっている。
avcはサイズが決まっている動画ファイル等に向いている形式であるため、カメラ撮った映像をファイルに流した形式のファイルだとサイズが決まらず、再生できない。
リアルタイムで映像が流れるような形式であれば、byte-streamの方が向いている。
byte-streamで`filesink`にメディアを流したい場合は、`x264enc`の後に
`video/x-h264,stream-format=byte-stream`というエレメントを挿入する。

```bash
gst-launch-1.0 v4l2src ! videoconvert ! x264enc ! video/x-h264,stream-format=byte-stream ! filesink location=/home/nvidia/my_workspace/output_test.mp4
```

この書き方は、実はエレメント名が省略されている。省略せずに書くと
`capsfilter caps=video/x-h264,stream-format=byte-stream`
となる。
capsfilterは、一つ前のエレメントから送り出されるメディアに複数のケイパビリティがある場合に、特定のケイパビリティを指定するエレメントである。
上記の例ではstream-formatにbyte-streamを指定しているが、「video/x-h264」の部分（コンテンツタイプと呼ぶ）は選択肢がない場合でも必須となる。

上記のコマンドを実行すればカメラで撮影した映像を再生できるファイルが作成されるはずだ。