第 10章最初のアプリケーション

これまでに学んだことのまとめとなるこの章では、ライブラリの初期化をはじめ、エレメントの作成、エレメントをまとめてパイプラインを構築する方法、パイプラインの実行 (再生) など、簡単な GStreamer アプリケーションのすべての側面について説明します。説明に従って手順を実行すれば、簡単な Ogg/Vorbis オーディオプレーヤーを作成することができます。

10.1. Hello world

ここでは、最初の簡単なアプリケーションとして、シンプルな Ogg/Vorbis コマンドラインオーディオプレーヤーを作成します。作成にあたって使用するのは、標準的な GStreamer コンポーネントだけです。プレーヤーでは、コマンドラインで指定されたファイルを読み取ります。ではさっそく始めましょう。

第4章では、アプリケーションでは最初に gst_init () を呼び出して GStreamer の初期化を行う必要があることを学びました。アプリケーションでは、gst/gst.h をインクルードして、すべての関数と名前が適切に定義されるようにする必要があります。それには、#include <gst/gst.h> を記述します。

次に、gst_element_factory_make () を使って、必要となるさまざまなエレメントを作成します。これから作成する Ogg/Vorbis オーディオプレーヤーの場合、ディスクからファイルを読み取るソースエレメントが必要になります。GStreamer"filesrc" という名前でこのエレメントを取り込みます。次に、ファイルを解析して、これを未加工の音声にデコードする処理を行う必要があります。GStreamer にはそのためのエレメントが 2 つ用意されています。1 つは、Ogg ストリームを解析してエレメンタリストリーム (動画、音声) に変換するエレメントで、"oggdemux" という名前が付いています。もう 1 つは、Vorbis 音声デコーダで、便宜上 "vorbisdec" と呼ばれています。"oggdemux" は、エレメンタリストリームごとに動的パッドを作成します。このため、項8.1.1 で学んだように、Ogg デマルチプレクサエレメントと Vorbis デコーダエレメントをリンクするために、"oggdemux" エレメントに対して "pad-added" イベントハンドラを設定する必要があります。最後に、音声出力エレメントも必要です。作成するアプリケーションでは、"autoaudiosink" を使用します。これで、使用するオーディオデバイスが自動的に検出されます。

最後に、すべてのエレメントをコンテナエレメント GstPipeline に追加し、このパイプラインを曲全体の再生が済むまで繰り返し実行します。エレメントをコンテナビンに追加する方法については、第6章ですでに学びました。また、エレメントの状態についても、項5.6 で学びました。あとは、パイプラインバスにメッセージハンドラをアタッチして、エラーを取得したり、ストリームの最後を検出したりできるようにします。

では、最初のオーディオプレーヤーのコードを以下にまとめて示します。


#include <gst/gst.h>
#include <glib.h>


static gboolean
bus_call (GstBus     *bus,
          GstMessage *msg,
          gpointer    data)
{
  GMainLoop *loop = (GMainLoop *) data;

  switch (GST_MESSAGE_TYPE (msg)) {

    case GST_MESSAGE_EOS:
      g_print ("End of stream\n");
      g_main_loop_quit (loop);
      break;

    case GST_MESSAGE_ERROR: {
      gchar  *debug;
      GError *error;

      gst_message_parse_error (msg, &error, &debug);
      g_free (debug);

      g_printerr ("Error: %s\n", error->message);
      g_error_free (error);

      g_main_loop_quit (loop);
      break;
    }
    default:
      break;
  }

  return TRUE;
}


static void
on_pad_added (GstElement *element,
              GstPad     *pad,
              gpointer    data)
{
  GstPad *sinkpad;
  GstElement *decoder = (GstElement *) data;

  /* We can now link this pad with the vorbis-decoder sink pad */
  g_print ("Dynamic pad created, linking demuxer/decoder\n");

  sinkpad = gst_element_get_static_pad (decoder, "sink");

  gst_pad_link (pad, sinkpad);

  gst_object_unref (sinkpad);
}



int
main (int   argc,
      char *argv[])
{
  GMainLoop *loop;

  GstElement *pipeline, *source, *demuxer, *decoder, *conv, *sink;
  GstBus *bus;

  /* Initialisation */
  gst_init (&argc, &argv);

  loop = g_main_loop_new (NULL, FALSE);


  /* Check input arguments */
  if (argc != 2) {
    g_printerr ("Usage: %s <Ogg/Vorbis filename>\n", argv[0]);
    return -1;
  }


  /* Create gstreamer elements */
  pipeline = gst_pipeline_new ("audio-player");
  source   = gst_element_factory_make ("filesrc",       "file-source");
  demuxer  = gst_element_factory_make ("oggdemux",      "ogg-demuxer");
  decoder  = gst_element_factory_make ("vorbisdec",     "vorbis-decoder");
  conv     = gst_element_factory_make ("audioconvert",  "converter");
  sink     = gst_element_factory_make ("autoaudiosink", "audio-output");

  if (!pipeline || !source || !demuxer || !decoder || !conv || !sink) {
    g_printerr ("One element could not be created. Exiting.\n");
    return -1;
  }

  /* Set up the pipeline */

  /* we set the input filename to the source element */
  g_object_set (G_OBJECT (source), "location", argv[1], NULL);

  /* we add a message handler */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_bus_add_watch (bus, bus_call, loop);
  gst_object_unref (bus);

  /* we add all elements into the pipeline */
  /* file-source | ogg-demuxer | vorbis-decoder | converter | alsa-output */
  gst_bin_add_many (GST_BIN (pipeline),
                    source, demuxer, decoder, conv, sink, NULL);

  /* we link the elements together */
  /* file-source -> ogg-demuxer ~> vorbis-decoder -> converter -> alsa-output */
  gst_element_link (source, demuxer);
  gst_element_link_many (decoder, conv, sink, NULL);
  g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), decoder);

  /* note that the demuxer will be linked to the decoder dynamically.
     The reason is that Ogg may contain various streams (for example
     audio and video). The source pad(s) will be created at run time,
     by the demuxer when it detects the amount and nature of streams.
     Therefore we connect a callback function which will be executed
     when the "pad-added" is emitted.*/


  /* Set the pipeline to "playing" state*/
  g_print ("Now playing: %s\n", argv[1]);
  gst_element_set_state (pipeline, GST_STATE_PLAYING);


  /* Iterate */
  g_print ("Running...\n");
  g_main_loop_run (loop);


  /* Out of the main loop, clean up nicely */
  g_print ("Returned, stopping playback\n");
  gst_element_set_state (pipeline, GST_STATE_NULL);

  g_print ("Deleting pipeline\n");
  gst_object_unref (GST_OBJECT (pipeline));

  return 0;
}

    

これでパイプラインが完成しました。次の図は、作成したパイプラインを図示したものです

図 10-1. "hello world" パイプライン