18.2. パイプラインに対する手動でのデータの追加または削除

これまでに多くのユーザーが、独自のソースを使ってパイプラインにデータを挿入したいという希望を表明しています。一部には、パイプライン内の出力を取得し、実際の出力はアプリケーション内部で行いたいというユーザーもいます。どちらの方法もまったく推奨されない方法ですが、GStreamer ではこれらを行うためのハックを用意しています。ただし、これらの方法には一切サポートがありません。 もし期待どおり動作してなくても、自己責任で対処してください。また、これらの方法のいずれかを使うと、同期化やスレッドセーフをはじめ、それまでに当然可能であったはずのその他さまざまなことも、一切保証されなくなります。どんな場合でも、ふつうにプラグインを作成し、プラグインのスケジュールと管理をパイプラインに任せるのがよい方法です。この点についての詳細は、『Plugin Writer's Guide』を参照してください。また、アプリケーションに静的にプラグインを埋め込む方法について説明した次の項も参照してください。

注意

現在、アプリケーションがデータの挿入と取り出しをやり易くするための新しい API の開発が行われています。具体的には、gst-plugins-bad モジュールの GstAppSrc と GstAppSink がそれです。このマニュアルの執筆時点 (2007年10月) では、この API はまだかなり不安定で一般的な使用には耐えませんが、必要なら使えばうまく動作するかもしれません。

注意すべき点についてはすでに述べたので、先へ進みましょう。上で言及した目的に使用できる可能性のあるエレメントは 3 つあります。それぞれ、"fakesrc" (仮想ソース)、"fakesink" (仮想シンク)、"identity" (仮想フィルタ) と名付けられています。これらのエレメントのいずれにも、同じ方法が適用されます。ここでは、これらのエレメントを使って、パイプラインにデータを挿入したり (fakesrc を使用)、パイプラインからデータを取得したり (fakesink または identity を使用) する方法、そしてネゴシエーションを設定する方法について説明します。

注意深い読者は、identity の目的がプローブの目的とほとんど同じであることに気付いたかもしれません。実際、そのとおりです。プローブは同じ目的で使用できるほか、もっと多くのことを行うことができ、オーバーヘッドも少なく、ハンドラの動的削除や追加も可能です。しかしこれらの点を除けば、プローブと identity は同じ目的を持った要素で、実装のタイプが根本的に異なるだけです

18.2.1. データの挿入または取得

上に出した 3 つのエレメント (fakesrc, fakesink and identity) は、それぞれ "handoff" シグナルを持っており、このシグナルは _get ()-関数 (fakesrc の場合) または _chain ()-関数 (identity、fakesink の場合) から呼び出されます。シグナルハンドラでは、渡されたバッファに対してデータを設定したり (fakesrc)、取得したり (identity、fakesink) できます。fakesrc の場合には、"sizemax" プロパティを使って、渡されたバッファのサイズを設定する必要があることに注意してください。fakesrc と fakesink のどちらの場合も、目的の方法を動作させるには、"signal-handoffs" プロパティも設定する必要があります。

handoff 関数は、動作をブロックさせるものであってはいけません。さもないと、パイプラインの繰り返しはブロックされます。また、こうした関数の中では、同期化のようなことを実行する目的で、不適切なハックを行なわないようにしてください。そのようなやり方は正しい方法ではありませんし、どこか別のところで思わぬ問題が生じるおそれがあります。もしそうしたハックを行なうなら、そのプログラマは GStreamer の設計を根本的に誤解しているということになります。

18.2.2. 形式の強制

場合によっては、パイプラインの中でソースとして fakesrc を使う際に、特定の形式、たとえば動画サイズと形式、音声のビットサイズとチャンネル数などを設定したいことがあります。こうした処理は、パイプラインに対して特定の GstCaps を強制すれば実現でき、フィルタリングされたケイパビリティ (filtered caps) を使うことで可能になります。リンクに対してフィルタリングされたケイパビリティを設定するには、2 つのエレメントの間で "capsfilter" エレメントを使用し、GstCaps をこのエレメントの "caps" プロパティとして指定します。これで、指定されたケイパビリティに一致する種類だけが、ネゴシエーションを行なえるようになります。

18.2.3. サンプルアプリケーション

次に示すサンプルアプリケーションでは、ソースに fakesrc を使い、フィルタリングされたケイパビリティを使って形式を強制することにより、X の画面に (1 秒ごとに切り換わる) 白黒の動画を表示します。画像のデプスは実際に使用している X サーバーの設定に依存するため、色空間を変換するエレメントを使って、X サーバーへの出力が正しいビット深さを持つようにしています。渡されたバッファに対してタイムスタンプを設定すれば、固定フレームレートを上書きすることもできます。


#include <string.h> /* for memset () */
#include <gst/gst.h>

static void
cb_handoff (GstElement *fakesrc,
	    GstBuffer  *buffer,
	    GstPad     *pad,
	    gpointer    user_data)
{
  static gboolean white = FALSE;

  /* this makes the image black/white */
  memset (GST_BUFFER_DATA (buffer), white ? 0xff : 0x0,
	  GST_BUFFER_SIZE (buffer));
  white = !white;
}

gint
main (gint   argc,
      gchar *argv[])
{
  GstElement *pipeline, *fakesrc, *flt, *conv, *videosink;
  GMainLoop *loop;

  /* init GStreamer */
  gst_init (&argc, &argv);
  loop = g_main_loop_new (NULL, FALSE);

  /* setup pipeline */
  pipeline = gst_pipeline_new ("pipeline");
  fakesrc = gst_element_factory_make ("fakesrc", "source");
  flt = gst_element_factory_make ("capsfilter", "flt");
  conv = gst_element_factory_make ("ffmpegcolorspace", "conv");
  videosink = gst_element_factory_make ("xvimagesink", "videosink");

  /* setup */
  g_object_set (G_OBJECT (flt), "caps",
  		gst_caps_new_simple ("video/x-raw-rgb",
				     "width", G_TYPE_INT, 384,
				     "height", G_TYPE_INT, 288,
				     "framerate", GST_TYPE_FRACTION, 1, 1,
				     "bpp", G_TYPE_INT, 16,
				     "depth", G_TYPE_INT, 16,
				     "endianness", G_TYPE_INT, G_BYTE_ORDER,
				     NULL), NULL);
  gst_bin_add_many (GST_BIN (pipeline), fakesrc, flt, conv, videosink, NULL);
  gst_element_link_many (fakesrc, flt, conv, videosink, NULL);

  /* setup fake source */
  g_object_set (G_OBJECT (fakesrc),
		"signal-handoffs", TRUE,
		"sizemax", 384 * 288 * 2,
		"sizetype", 2, NULL);
  g_signal_connect (fakesrc, "handoff", G_CALLBACK (cb_handoff), NULL);

  /* play */
  gst_element_set_state (pipeline, GST_STATE_PLAYING);
  g_main_loop_run (loop);

  /* clean up */
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (GST_OBJECT (pipeline));

  return 0;
}