gstreamerのサンプルコードをmacで動かしてみた(gstreamer、c、c++)


はじめに

この記事は「完全に理解したTalk Advent Calendar 2020」11日目の投稿記事です。
gstreamerを使う必要があり、頑張ってたら完全理解してしまったのでその話をします。

OSはMacOS
言語はc/c++
cmakeでビルドします。

gstreamerとは

C言語で作られたライブラリで、
動画のストリーミングほか、メディアプレイヤーの再生などに使われます。
メインの処理以外は、プラグイン、ライブラリを読み込んで処理が行われています。
Wikipediaさんより

インストール

brewでインストールします。

$ brew update #必要であれば
$ brew install gstreamer
$ brew install gst-plugins-base
$ brew install gst-plugins-good
$ brew install gstreamermm #c++でバインドしたライブラリ

必要なものはこれらです。

$ brew install gst-plugins-ugly
$ brew install gst-plugins-bad

妙な名前でざわつくものもありますが、ugly、badは飛ばして大丈夫です。
公式に説明があるようです。。

パスを通す

$ brew link gstreamer
$ brew link gst-plugins-base
$ brew link gst-plugins-good
$ brew link gstreamermm #c++でバインドしたライブラリ

brew linkで /usr/local/lib/usr/local/includeにシンボリックリンクします。

cmakeを書く

gstreamerはcmakeのfindPackageは使えません。
ですが、pkg-configを使っていますのでcmakeでpkg_configを使ってリンクします。

cmake内で使われるpkg_configはパスが通っていない場合がありますので、
set(ENV{PKG_CONFIG_PATH} "/usr/local/lib/pkgconfig")
で一時的にパスを通します。
brewでインストールしリンクしたものは、/usr/local/lib/にあります。

cmake_minimum_required(VERSION 3.15)
project(testGstreamer)

set(CMAKE_CXX_STANDARD 14)

set(ENV{PKG_CONFIG_PATH} "/usr/local/lib/pkgconfig")
find_package(PkgConfig REQUIRED)

#pkg_check_modules(GSTREAMER_LIB REQUIRED IMPORTED_TARGET  gstreamer-1.0) cで使う場合はこっち
pkg_check_modules(GSTREAMERMM_LIB REQUIRED  gstreamermm-1.0)

add_executable(testGstreamer main.cpp)


target_include_directories(testGstreamer PUBLIC ${GSTREAMERMM_LIB_INCLUDE_DIRS})
target_link_libraries(testGstreamer ${GSTREAMERMM_LIB_LIBRARIES})
target_compile_options(testGstreamer PUBLIC ${GSTREAMERMM_CFLAGS_OTHER})

#target_include_directories(testGstreamer PUBLIC ${GSTREAMER_LIB_INCLUDE_DIRS}) cで使う場合はこっち
#target_link_libraries(testGstreamer ${GSTREAMER_LIB_LIBRARIES}) cで使う場合はこっち
#target_compile_options(testGstreamer PUBLIC ${GSTREAMER_CFLAGS_OTHER}) cで使う場合はこっち

exampleから引っ張ってきたコード

ここのexampleをビルドしました。


#include <gstreamermm.h>
#include <glibmm/main.h>
#include <glibmm/convert.h>
#include <iostream>
#include <stdlib.h>
#include <gstreamermm/playbin.h>

namespace
{

Glib::RefPtr<Glib::MainLoop> mainloop;

// This function is used to receive asynchronous messages in the main loop.
bool on_bus_message(const Glib::RefPtr<Gst::Bus>& /* bus */,
                    const Glib::RefPtr<Gst::Message>& message)
{
  switch(message->get_message_type()) {
  case Gst::MESSAGE_EOS:
    std::cout << std::endl << "End of stream" << std::endl;
    mainloop->quit();
    return false;
  case Gst::MESSAGE_ERROR:
  {
    Glib::RefPtr<Gst::MessageError> msgError =
        Glib::RefPtr<Gst::MessageError>::cast_static(message);

    if(msgError)
    {
      Glib::Error err;
      err = msgError->parse_error();
      std::cerr << "Error: " << err.what() << std::endl;
    }
    else
      std::cerr << "Error." << std::endl;

    mainloop->quit();
    return false;
  }
  default:
    break;
  }

  return true;
}

} // anonymous namespace

int main(int argc, char** argv)
{
  // Initialize gstreamermm:
  Gst::init(argc, argv);

  // Check input arguments:
  if(argc < 2)
  {
    std::cout << "Usage: " << argv[0] << " <media file or uri>" << std::endl;
    return EXIT_FAILURE;
  }

  // Create a playbin element.
#ifndef GSTREAMERMM_DISABLE_DEPRECATED
  Glib::RefPtr<Gst::PlayBin> playbin = Gst::PlayBin::create();
#else
  Glib::RefPtr<Gst::Element> playbin = Gst::ElementFactory::create_element("playbin");
#endif

  if(!playbin)
  {
    std::cerr << "The playbin2 element could not be created." << std::endl;
    return EXIT_FAILURE;
  }

  // Take the commandline argument and ensure that it is a uri:
  Glib::ustring uri;

  if(gst_uri_is_valid(argv[1]))
    uri = argv[1];
  else
    uri = Glib::filename_to_uri(argv[1]);

  // Set the playbyin2's uri property.
  playbin->set_property("uri", uri);

  // Create the main loop.
  mainloop = Glib::MainLoop::create();

  // Get the bus from the playbin, and add a bus watch to the default main
  // context with the default priority:
  Glib::RefPtr<Gst::Bus> bus = playbin->get_bus();
  bus->add_watch(sigc::ptr_fun(&on_bus_message));

  // Now set the playbin to the PLAYING state and start the main loop:
  std::cout << "Setting to PLAYING." << std::endl;
  playbin->set_state(Gst::STATE_PLAYING);
  std::cout << "Running." << std::endl;
  mainloop->run();

  // Clean up nicely:
  std::cout << "Returned. Setting state to NULL." << std::endl;
  playbin->set_state(Gst::STATE_NULL);

  return EXIT_SUCCESS;
}

実行

$ ./testGstreamer https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm
Setting to PLAYING.
Running.

End of stream
Returned. Setting state to NULL.

上のような画面が出てダウンロードしながら再生が行われます。
また、実行時、引数をローカルファイルにしても再生されますし、
mp3でも再生が行われます。

終わりに

今回は、gstreamerを使わないといけなくなったので、
まずは試しで使いました。

コマンドとしても用意されていますので、色々遊べると思います。

参考

公式
公式リファレンス