RustでもGStreamer理解がしたい! その2 〜 映像・音声テスト信号を流す 〜

# 今回作ったもの

さて、前回はパイプラインにコマンド文字列を渡す `parse_launch()` だったためGStreamerを呼び出すメリットはあまりなさそうでした。
今回はエレメントをプログラムで作成してリンクする方法にて映像と音声のテスト信号を再生するところをやってみます。
これで基本エレメントを接続したりプロパティを設定したりできるようになります。ただし `queue` などを使う場合は `Pad` を動的に繋ぐ必要があり、まだまだGStreamerらしさは発揮できません。

<img width=640 title="スクリーンショット 2022-09-19 19.30.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/91710/54d0c5ae-03f5-d2a9-a30d-b4bbc0485fb7.png" />

### チュートリアルと違うところ

チュートリアルでは映像か音声か片方だけだったので、両方合わせたパイプラインにしました。
また、サイズやフレームレートを指定する方法もチュートリアル一覧にはなかったような気がするので追加してみました。

### ソースコード

https://github.com/alivelime/gstreamer-rs-streaming-tutorial

# パイプライン

`gst-launch-1.0` で書くとすると以下のようになります。
映像と音声は特に同期する必要もなく別々です。

```shell
$ gst-launch-1.0 videotestsrc pattern=smpte ! video/x-raw,width=720,height=480,framerate=30/1 ! audiovideosink \
  audiotestsrc ! audioconvert ! autoaudiosink
```

```plantuml
left to right direction
package gstreamer {
  [videotestsrc] as v_src
  [capsfilter] as v_caps
  [autovideosink] as v_sink
  [audiotestsrc] as a_src
  [audioconvert] as a_convert
  [autoaudiosink] as a_sink
}
[player]
[speaker]

v_src --> v_caps
v_caps --> v_sink
v_sink --> player

a_src --> a_convert
a_convert --> a_sink
a_sink --> speaker
```

# プログラムの中身

前回のプログラムに少し追加されるくらいです。

### エレメントを作成する

`gst::ElementFactory::make()` にてエレメントを作成します。ここは完全に動的リンクになりますのでパスが通っていないとエラーになります。

```rust:src/main.rs
fn tutorial_main() {
    gst::init().unwrap();

    let pipeline = gst::Pipeline::new(Some("test-pipeline"));
    let v_source = gst::ElementFactory::make("videotestsrc", Some("v_source")).expect("error videotestsrc");
    let v_sink = gst::ElementFactory::make("autovideosink", Some("v_sink")).expect("error autovideosink");
    let a_source = gst::ElementFactory::make("audiotestsrc", Some("a_source")).expect("error audiotestsrc");
    let a_convert = gst::ElementFactory::make("audioconvert", Some("a_convert")).expect("error audioconvert");
    let a_sink = gst::ElementFactory::make("autoaudiosink", Some("a_sink")).expect("error autoaudiosink");
    let capsfilter = gst::ElementFactory::make("capsfilter", None).expect("error capsfilter");

    ...
}
```

### エレメントをリンクする

エレメントを作成したらパイプラインに追加し、それから処理の繋がりごとにリンクします。
ただしここでリンクできるのは静的な `Pad` を持っているものだけで、`queue` や `demuxer` などの動的に生成されるエレメントについては処理をフックしてリンクする必要があります。

```rust:src/main.rs

    ...上の続き

    pipeline.add_many(&[&v_source, &capsfilter,  &v_sink]);
    gst::Element::link_many(&[&v_source, &capsfilter,  &v_sink]);
    pipeline.add_many(&[&a_source, &a_convert,  &a_sink]);
    gst::Element::link_many(&[&a_source, &a_convert, &a_sink]);

    // プロパティなどを設定する
    v_source.set_property_from_str("pattern", "ball");

     ... capsfilterの設定 (次を参照)

    // 以降は前回と同じ。
    pipeline.set_state(gst::State::Playing).expect("set state");

    let bus = pipeline.bus().unwrap();
    for msg in bus.iter_timed(gst::ClockTime::NONE) {
        ...
    }

    pipeline.set_state(gst::State::Null).expect("set to null");
}
```

### cpasfilterにて設定する

エレメントのリンクやプロパティの設定は上記の通りで特筆する点はなさそうですが、
パイプラインにてサイズやフレームレートを設定するには `cpasfilter` というエレメントにて設定するようです。

```shell:pipeline
# パイプラインによく出てくる
# こういうのとか
 ... ! video/x-raw,width=720,height=480 ...
# こういうやつ
 ... ! video/x-h264 ! h264parse ! ...

# 正確に書くとcapsfilterのプロパティということらしい
... ! capsfilter caps="video/x-raw,width=720,height=480" ...
```

そのためプログラム上では `capsfilter` エレメントを作成し、ビルダーを用いてプロパティを合成しています。

```rust:src/main.rs
    capsfilter.set_property(
        "caps",
        gst::Caps::builder("video/x-raw")
        .field("format", "I420")
        .field("width", 720)
        .field("height", 480)
        .field("framerate", gst::Fraction::new(30, 1))
        .build(),
        );
```

# 次回

さて、これでエレメントの作成とプロパティの設定はできるようになりましたが、まだまだ `queue` や `demuxer` などが使えません。
次回は `Pad` を動的に繋ぐ方法を書きたいと思います。