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

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

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

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

  1. 解決策
  2. 修正した例

解決策

色々と調べたところ、wavparseというプラグインはその次に渡す要素(プラグイン)と接続する部分(GStreamerにおける用語で「パッド」と呼ばれるところ)が最初は存在せず、動的にその状態を変えるようで、wavparseに至るまでの要素を接続してからパイプラインの状態をSTATE_PLAYINGにした後、あるタイミングでパッドが現れて次の要素への接続ができるようになるようだ。
その「あるタイミング」で次の要素への接続(リンク)処理をするためには、wavparseの要素の「pad-added」というGObjectシグナルとハンドラを関連付け、その中で次の要素への接続(リンク)の記述を行えばよいことが分かった。このハンドラは

  • GStreamerの要素
  • 新しいパッド
  • 追加のユーザデータ(任意)

を引数として受け取り、最初の引数のオブジェクトに対してlink()を呼ぶようにもできる。

修正した例

[任意]ファイル名: playwave.py

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

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


class PlayWave:
  """
  GStreamerを用いてWAVEファイルを再生する
  """
  __retval = 0
  def __on_wavparse_pad_added (self, gstelement, new_pad, user_data):
    """
    wavparseプラグインのパッドが現れてから次の要素に接続できる状態になった
    user_dataはaudioconvertの要素を渡している
    """
    try:
      gstelement.link (user_data)
    except gst.LinkError as msg:
      print ('Error: failed to link (wavparse -> audioconvert): {0}'.format (msg), file=sys.stderr)
      self.__retval = 1
      self.__loop.quit ()
  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とaudioconvertをここでリンクしようとすると
      # 確実に失敗する
      # wavparseから次の要素にリンクする部分の「パッド」は
      # この時点では存在せず、
      # 後で「pad-added」シグナルの発生とともに現れる
      # (パッドは要素の中の一部分で他要素との接続を行う部分を指す)
#     wavparse.link (audioconvert)
      # この先はこの段階で接続しておくことができる
      audioconvert.link (audioresample)
      audioresample.link (autoaudiosink)
    except gst.LinkError as msg:
      print ('Error: {0}'.format (msg), file=sys.stderr)
      return 1

    # 次の要素にリンクするためのパッドが出たら
    # 接続を行うためのハンドラが呼ばれるようにする
    wavparse.connect ('pad-added', self.__on_wavparse_pad_added, audioconvert)

    # 準備完了
    # 再生状態にしてメインループ開始
    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 = PlayWave ()
  sys.exit (app.main ())

下は実行例。WAVEファイルが再生され、再生が終わったらスクリプトは終了する。

$ [playwave.pyの場所] [WAVEファイルの場所]

使用したバージョン:

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