ochalog

RubyとMediaWikiとIRCが好き。

gstreamermmでGStreamerのチュートリアル1

卒研で使うThe Imaging Source社の産業用カメラをLinuxで制御する1ためにマルチメディアフレームワークGStreamerを使うことになりそうなので、その練習をしている。

卒研ではOpenCVと組み合わせる予定なので、C++で書けると扱いやすい。GStreamerはC言語で書かれたライブラリだが、C++ラッパーのgstreamermmが用意されているので、これを使うことにした。

GStreamer公式の説明ページにはチュートリアルがいくつか用意されている。今回は最初のチュートリアル1を、gstreamermmを使って書いてみた。

開発環境

ライブラリのインストール

Homebrewを使って必要なライブラリをインストールした。

サンプル動画を再生するために、libvpxおよびlibvorbisとのリンクが必要。また brew link --force gettext が必要になる。これを行わないと、書いたプログラムのリンクの際に「libintlが見つからない」というエラーが出た。後に問題が発生したら brew unlink gettext を行う必要があるかもしれない。

brew install pkg-config gstreamer gst-plugins-good gettext
brew install gst-plugins-base --with-libogg --with-libvorbis
brew install gst-plugins-bad --with-libpng --with-libvpx
brew link --force gettext

ソースコード

CMakeLists.txt

CMake 簡易まとめ」を参考にした。

cmake_minimum_required(VERSION 3.0)
project(tutorial CXX)

find_package(PkgConfig)
pkg_check_modules(GSTREAMERMM REQUIRED gstreamermm-1.0)

set(CMAKE_CXX_FLAGS "-std=c++11 -Wall")
set(CMAKE_CXX_FLAGS_DEBUG "-g3 -O0 -pg")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -s -DNDEBUG -march=native")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g3 -Og -pg")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -s DNDEBUG -march=native")

add_executable(tutorial01 tutorial01.cpp)
target_include_directories(tutorial01 PUBLIC ${GSTREAMERMM_INCLUDE_DIRS})
target_link_libraries(tutorial01 ${GSTREAMERMM_LIBRARIES})
target_compile_options(tutorial01 PUBLIC ${GSTREAMERMM_CFLAGS_OTHER})

tutorial01.cpp

チュートリアルのコードをgstreamermmを使って書き直した。

#include <gstreamermm-1.0/gstreamermm.h>

int main(int argc, char* argv[]) {
  // GStreamerを初期化する
  Gst::init(argc, argv);

  // パイプラインを構築する
  Glib::RefPtr<Gst::Element> pipeline = Gst::Parse::launch(
    "playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm"
  );

  // 再生を開始する
  pipeline->set_state(Gst::STATE_PLAYING);

  {
    // エラーまたはEOSまで待つ
    Glib::RefPtr<Gst::Bus> bus = pipeline->get_bus();
    Glib::RefPtr<Gst::Message> msg = bus->pop(
      Gst::CLOCK_TIME_NONE, Gst::MESSAGE_ERROR | Gst::MESSAGE_EOS
    );
  }

  // リソースを解放する
  pipeline->set_state(Gst::STATE_NULL);

  return 0;
}

C言語で書かれたチュートリアルのコードは以下のとおり。

#include <gst/gst.h>

int main(int argc, char *argv[]) {
  GstElement *pipeline;
  GstBus *bus;
  GstMessage *msg;

  /* Initialize GStreamer */
  gst_init (&argc, &argv);

  /* Build the pipeline */
  pipeline = gst_parse_launch ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);

  /* Start playing */
  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  /* Wait until error or EOS */
  bus = gst_element_get_bus (pipeline);
  msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);

  /* Free resources */
  if (msg != NULL)
    gst_message_unref (msg);
  gst_object_unref (bus);
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (pipeline);
  return 0;
}

ビルド

CMakeの慣習に従い、buildディレクトリを作ってその中でビルドする。

cd /path/to/tutorial01
mkdir build
cd build
cmake ..
make

実行

ビルドしたら以下のコマンドで実行することができる。

./tutorial01

実行するとウィンドウが表示され、https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm の再生が始まる。最後まで再生されると終了する。

f:id:ochaochaocha3:20170808235748p:plain

C言語で書かれたものとの違い

生ポインタの代わりにGlib::RefPtrを使う

例えば GstElement * 型が返る関数のgstreamermm版では Glib::RefPtr<Gst::Element> が返る。

Glib::RefPtr(リファレンス)はglibmmが提供する参照カウンタ式のスマートポインタクラスで、C++11のstd::shared_ptrに似ている。デストラクタでカウントが1減り、0になったときにリソースが解放される。このスマートポインタのおかげで、gst_object_unref() に相当する ->unreference() を呼び出さなくてもよくなる。

上のコードではポインタを使用する範囲をブロックにした。ブロックを抜ける際にデストラクタが呼ばれ、C言語版とほぼ同じ場所で ->unreference() が呼ばれることになる。

一部の関数のインターフェースが異なる

多くの関数のgstreamermm版はC言語版からの規則的な変換で書けるが、一部インターフェースが異なるものがある。上の例だと、gst_bus_timed_pop_filtered() に対応する関数は Gst::Bus::pop(Gst::ClockTime timeout, Gst::MessageType message_type) と、オーバーロードを利用したものになっている。

一つ一つドキュメントから探していけば良いのだが、インターネットでは現在の最新版1.8.0のドキュメントのページが空になっているため、少し不便になっている。gstreamermmをHomebrewでインストールした場合は /usr/local/opt/gstreamermm/share/doc/gstreamermm-1.0/reference/html/index.html からDoxygenで生成されたドキュメントを見ることができた。未確認だが、Linuxではlibgstreamermm-1.0-docのようなパッケージをインストールすれば閲覧できそうだ。

感想

常にリソース管理に注意しなければならないC++なので、スマートポインタでリソースの解放し忘れが減ることが嬉しい。慣れていないのでまだドキュメントを何度も見ているが、もともとGStreamerがオブジェクト指向で書かれているので、対応するgstreamermmの関数を見つけるのは難しくなかった。個人的にはC++11以降の書き方が気に入っていて、できるだけC++で書きたいと思うので、今後も積極的にgstreamermmを使っていきたい。


  1. 制御するためのLinux用ライブラリがGitHubで公開されている:
    https://github.com/TheImagingSource/tiscamera