Help us understand the problem. What is going on with this article?

[ストリーミング技術]RustでGStreamerチュートリアル 1: Hello World

はじめに

この記事はGStreamerの公式チュートリアルをRustで行い、さらに日本語に雑に翻訳したものです。

特に今回はGStreamer-1:Hello Worldで扱われているものを対象とします。

また、Rustに書き直されたサンプルとしてgstreamer-rsのtutorialsフォルダ以下のソースを大きく参考にしますが、必ずしも一致しません。特にMac上でのメインループの記述のために本家のgstreamer-rsではOSごとの分岐を行う処理を含めていますが、自分の実行環境上で必要がなかったため含めていません。

このチュートリアル用に解説したコードはすべて以下に含まれています。
https://github.com/kyasbal-1994/qiita-gstreamer-rust-tutorial

単に自分の学習目的と日々の継続的なアウトプットの目標として記事を書くだけですが、昨今の動画配信技術の需要の高まりの一方で直接動画技術を扱うための入門として参考にしていただければ幸いです。

Rust環境の構築

本家のチュートリアルではCの環境ですので特殊な環境ではありませんが、チュートリアルのrustのプロジェクトは相対パスで各ライブラリをリンクしているため現実的なユースケースにおけるプロジェクトの構造と異なります。そのため、チュートリアルをこなすために自分でプロジェクトを作成します。

$ cargo init

生成されたCargo.tomlに対して、チュートリアルをこなすのに最低限の依存ライブラリを追加します。

[dependencies]
gstreamer = "0.15.7"
glib = "0.9.3"

さらに、gstreamer自身をインストールしておきます。

$ apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
      gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
      gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \
      gstreamer1.0-libav libgstrtspserver-1.0-dev libges-1.0-dev

Tutorial 1の目標は単純な動画プレイヤーのエレメントであるplaybinを用いて動画を再生することにあります。Rustによる構築の前に、gstreamerに付随する簡易的なパイプラインのテストに利用するコマンドであるgst-launch-1.0を用いて実験をしてみましょう。

gst-launch-1.0 playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm

gst-launch.gif

このように普通の動画が再生されれば成功です。(VMWare上の仮想マシンで上手く動かなかったので(webm形式だけ)、そもそも動かすのに手こずりました)

Tutorial 1 : Hello World

(この辺から本家チュートリアルの翻訳のRust版への改変になります)

このチュートリアルでは、単に動画を表示して"Hello World"を行います。

実際に用いるコードは以下のようなものです。

extern crate gstreamer as gst;
use gst::prelude::*;


fn main(){
        // Initialize GStreamer
        gst::init().unwrap();

        // Build the pipeline
        let uri =
            "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
        let pipeline = gst::parse_launch(&format!("playbin uri={}", uri)).unwrap();

        // Start playing
        pipeline
            .set_state(gst::State::Playing)
            .expect("Unable to set the pipeline to the `Playing` state");

        // Wait until error or EOS
        let bus = pipeline.get_bus().unwrap();
        let msg_types = [gst::MessageType::Error,gst::MessageType::Eos];
        bus.timed_pop_filtered(gst::CLOCK_TIME_NONE, &msg_types);

        // Shutdown pipeline
        pipeline
            .set_state(gst::State::Null)
            .expect("Unable to set the pipeline to the `Null` state");

}

Walkthrough

gstreamer::init()

gst::init().unwrap();

どうやら、GStreamerを用いるためには最初にgstreamer::init()を呼んでおかなければならないようです。

パイプラインの作成

let uri = "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
let pipeline = gst::parse_launch(&format!("playbin uri={}", uri)).unwrap();

GStreamerではマルチメディアをパイプラインを構築することによって扱います。
このパイプライン上でsourceと呼ばれる要素から、sinkと呼ばれる要素へいくつかのタスクを行う要素を通っていくことによってメディアデータを操作します。
こうしてつなぎ合わされた要素たちをパイプラインと呼んでいます。

プログラムで一つ一つの要素を作成しつなぎ合わせることによってパイプラインを構築することも可能ですが、特に生成するパイプラインが簡単な場合には、手軽な方法として文字列から作成することができます。

この例では、playbinという要素しかないパイプラインを生成しています。playbinはパラメータとしてuriを取り、ここに文字列として再生したい動画ファイルのパスを配置しました。

playbin

先程のパイプラインの作成で我々はplaybinという要素ひとつだけからなるパイプラインを構築しました。

playbinsourceとしてもsinkとしても振る舞うことのでき、それ自身がパイプライン全体になりうることができる特殊な要素です。内部的に、渡されたメディアデータを再生するのに必要な要素を作成し、互いに接続してくれるため内部について気にする必要がありません。

自分でパイプラインを構築する場合と異なり、微細なパイプラインの調整を行うことはできませんが、十分に幅広いアプリケーションで利用できるだけのカスタマイズ可能な項目が存在します。

このチュートリアルでは、再生したい動画ファイルのURIを指定しました。ぜひ違うファイルで試してみてください。それがhttp://でも、file://でもplaybinは対応した適切なsource要素を作成しつなげてくれます。

State

pipeline.set_state(gst::State::Playing).expect("Unable to set the pipeline to the `Playing` state");

この行では、GStreamerの興味深いコンセプトの一つであるステートを操作しています。すべてのGStreamerの要素は対応したステートを常に持っています。
これは、通常のビデオプレイヤーから想像するような、ただ単に再生/停止だけを意味するもの以上のものがありますが、ここではpipelineのステートがPlayingにならない限り再生されないという程度の説明にとどめます。

また、プログラムの最後ではパイプラインのステートをNoneにしています。

pipeline.set_state(gst::State::Null).expect("Unable to set the pipeline to the `Null` state");

終了時にはこのようにしてパイプラインの後処理をしてあげる必要があります。パイプラインにNullステートをセットすることによって、確保された様々なリソースが開放されていることを保証します。

Bus

let bus = pipeline.get_bus().unwrap();
let msg_types = [gst::MessageType::Error,gst::MessageType::Eos];
bus.timed_pop_filtered(gst::CLOCK_TIME_NONE, &msg_types);

これらの行ではパイプラインからバスを作成しています。バスについてはチュートリアルその2で深く取り扱うので簡単な説明にとどめますが、ここではErrorもしくはEos(End Of Stream)の状態になるまで、タイムアウトなく(gst::CLOCK_TIME_NONE)ループを行うことを示しています。

実際、このループにタイムアウトをもたせたい場合には以下のようにすると1秒間再生して終了するようなメッセージループを記述することができます。

let bus = pipeline.get_bus().unwrap();
let msg_types = [gst::MessageType::Error,gst::MessageType::Eos];
bus.timed_pop_filtered(gst::ClockTime::from_mseconds(1000), &msg_types);

Conclusion

  • gst::init()を用いて初期化する
  • 文字列のパイプラインを作成するのにgst::parse_launchを用いる。
  • 簡単に再生するパイプラインを作成するにはplaybin要素を用いる
  • パイプラインを動き始めるようにするには.set_state(gst::State::Playing)を用いる。
  • timed_pop_filteredを行っている間はGStreamerがパイプラインの中身を処理している

次回: https://qiita.com/kyasbal_1994/items/a7f2ab0b05f47d96ded3

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away