はじめに
この記事は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
このように普通の動画が再生されれば成功です。(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という要素ひとつだけからなるパイプラインを構築しました。
playbin
はsource
としても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