試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

GStreamer 0.10でwavparseプラグインをプログラム中で用いる上でのメモ(前半)

GStreamerのプラグインを用いてWAVEファイルを再生したいときにwavparseというプラグインが使える(WAVEファイルから生の音声データが得られる)。例えばgst-launch-0.10を用いると

$ gst-launch-0.10 filesrc location=[WAVEファイルの場所] ! wavparse ! audioconvert ! audioresample ! autoaudiosink

のようにしてそのファイルが再生できる。
しかし、自作のプログラムで

  1. パイプラインオブジェクトを作成
  2. 各要素(プラグイン)を作成してパイプラインオブジェクトに追加
  3. 要素を順番通りに接続(リンク)する
  4. パイプラインの状態をSTATE_PLAYINGにする
  5. GLibのメインループを開始

としても、それを動かしてみると要素の接続(リンク)の段階でwavparseからその次のプラグインへの接続(だけ)が確実に失敗する。
ここではその件についてを扱うが、言語についてはPythonのバインディング(PyGST)を用いている。処理の要領はVala言語などの他の言語でも同様(C言語の場合はオブジェクトの参照カウンタ操作などが追加で必要になる)。

失敗例

この記事が書かれた時点ではGStreamerのPythonバインディングはバージョン2系のPython向けなので2系を用いることになるが、print()関数などの新しい書き方を用いている関係で、下のスクリプトはバージョン2.6系以上を対象とする。
[任意]ファイル名: playwave_ng.py

#! /usr/bin/python
# -*- coding: utf-8 -*-

from __future__ import print_function  # 2.6系以上でprint()関数を用いる
import sys


class PlayWaveNG:
  """
  GStreamerを用いてWAVEファイルを再生する
  """
  __retval = 0
  def __on_bus_message_error (self, bus, message):
    """
    GStreamerのバスにエラーメッセージが流れた
    """
    # エラーの解析処理
    (gerror, debug) = message.parse_error ()
    print (debug, file=sys.stderr)
    self.__retval = 1
    # メインループを抜ける
    self.__loop.quit ()
  def main (self):
    """
    メイン処理
    """
    # コマンド行引数を入力ファイルとする
    if len (sys.argv) < 2:
      print ('usage: {0} [WAVE file]'.format (sys.argv[0]))
      return 0

    # モジュールの読み込み
    try:
      import gst
    except:
      print ('Error: GStreamer Python binding is not installed.', file=sys.stderr)
      return 1
    try:
      import glib
    except:
      print ('Error: GLib Python binding is not installed.', file=sys.stderr)
      return 1

    # メインループ
    self.__loop = glib.MainLoop ()
    # ファイルの場所をGObjectプロパティlocationとして
    # その内容を開いて送り出すプラグイン
    filesrc = gst.element_factory_make ('filesrc', 'src')
    filesrc.props.location = sys.argv[1]
    # WAVEヘッダを解析して生の音声データを得る
    wavparse = gst.element_factory_make ('wavparse', 'parse')
    # 変換(これをしないと次のsinkにつながらない場合がある)
    audioconvert = gst.element_factory_make ('audioconvert', 'convert')
    audioresample = gst.element_factory_make ('audioresample', 'resample')
    # 自動でオーディオ出力先を探して渡してくれる
    autoaudiosink = gst.element_factory_make ('autoaudiosink', 'sink')
    # 処理の流れを通すパイプライン
    pl = gst.Pipeline ()
    # 内部メッセージを処理するバス
    bus = pl.get_bus ()
    # 下の2つのGObjectシグナルを接続するために必要
    bus.add_signal_watch ()
    # ストリーム終端になったらメインループを抜けるようにする
    bus.connect ('message::eos', lambda bus, message: self.__loop.quit ())
    # エラーメッセージが出たら表示/終了のためのハンドラが呼ばれるようにする
    bus.connect ('message::error', self.__on_bus_message_error)
    # パイプラインに要素を追加(順番は任意)
    pl.add (filesrc, wavparse, audioconvert, audioresample, autoaudiosink)

    # 要素を接続していく
    # Pythonではgst.LinkErrorが出るがValaでは戻り値で処理する
    try:
      filesrc.link (wavparse)
      # ここで確実に失敗する
      wavparse.link (audioconvert)
      audioconvert.link (audioresample)
      audioresample.link (autoaudiosink)
    except gst.LinkError as msg:
      print ('Error: {0}'.format (msg), file=sys.stderr)
      return 1

    # 準備完了
    # 再生状態にしてメインループ開始
    pl.set_state (gst.STATE_PLAYING)
    try:
      self.__loop.run ()
    except KeyboardInterrupt:
      # Ctrl+Cが押されたときはそこでループを抜けて停止・終了
      # これをしないとGStreamerのCRITICALメッセージが出る
      self.__loop.quit ()
    # 停止状態にして終了
    pl.set_state (gst.STATE_NULL)
    return self.__retval


if __name__ == '__main__':
  app = PlayWaveNG ()
  sys.exit (app.main ())

下は実行例。ファイルを指定してもリンクの部分で失敗している。

$ [playwave_ng.pyの場所] [WAVEファイルの場所]
Error: failed to link parse with convert

使用したバージョン:

  • Python 2.6.5
  • PyGObject 2.21.1
  • PyGST 0.10.18
  • GStreamerライブラリ 0.10.29