GStreamer 0.10で音声を複数の出力先へ分岐させる(音を鳴らすと同時にファイルへ書き出すなど)
GStreamerの仕組みの中で、ある要素(プラグイン)から出力された音声データを
- サウンドカードへ出力する(音を鳴らす)
- ファイルに書き出す
といった複数の出力先へ渡したいということがある。
こうしたときに役に立つのがteeというプラグインで、コマンドのteeのように、受け取ったデータをそのまま2つに分岐させる機能を持つ。これを用いると上のような要求は簡単に満たせる。
ただし、teeと同時にqueueというプラグインの要素を2つ用意し、teeからそれぞれのqueueへリンク(接続)する形にしないと
http://gstreamer.freedesktop.org/wiki/FAQ#MypipelinewithmultiplesinksneverreachesthePAUSEDstate.2CwhatamIdoingwrong.3F
に書かれているようにPAUSEDの状態にできないとのことなので、PAUSEDの状態にするプログラムを書く場合には注意する。
処理の流れを図にすると
src > [途中の処理を行う要素...] > tee > [途中の処理を行う要素...] > sink1 (出力先1) (入力) +--> [途中の処理を行う要素...] > sink2 (出力先2)
のようにすることになる。teeからの接続はteeプラグインの要素のメンバ関数link()をそれぞれのqueueプラグインの要素に対して呼び出す形で行う。
tee = gst.element_factory_make ('tee', 'tee') queue1 = gst.element_factory_make ('queue', 'queue1') queue2 = gst.element_factory_make ('queue', 'queue2') ... [teeの1つ前の要素].link (tee) tee.link (queue1) tee.link (queue2) queue1.link ( ... ) queue2.link ( ... )
下は例で、FLACファイルの場所と出力WAVEファイルの場所を引数に指定して実行すると、それを再生しながらデコード結果をWAVEファイルに保存する。「GStreamer 0.10でwavparseプラグインをプログラム中で用いる上でのメモ(前半)」「GStreamer 0.10でwavparseプラグインをプログラム中で用いる上でのメモ(後半)」と同様、バージョン2.6系以上のPythonを対象とする。
[任意]ファイル名: gstteetest.py
#! /usr/bin/python # -*- coding: utf-8 -*- from __future__ import print_function # 2.6系以上でprint()関数を用いる import sys class GstTeeTest: """ GStreamerのteeプラグインを用いたテスト FLACファイルの再生と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) < 3: print ('usage: {0} [FLAC file] [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] # FLACヘッダを解析(中身はFLACエンコードされている状態) flacparse = gst.element_factory_make ('flacparse', 'parse') # FLACのデコード(ここで生の音声データになる) flacdec = gst.element_factory_make ('flacdec', 'decode') # 分岐 # 注意:必ず分岐した次の要素はqueueプラグインになるようにする # そうしないと不具合が起こることがある # http://gstreamer.freedesktop.org/wiki/FAQ#MypipelinewithmultiplesinksneverreachesthePAUSEDstate.2CwhatamIdoingwrong.3F tee = gst.element_factory_make ('tee', 'tee') queue_audio = gst.element_factory_make ('queue', 'queue_audio') queue_file = gst.element_factory_make ('queue', 'queue_file') # 変換(これをしないと次のsinkにつながらない場合がある) audioconvert = gst.element_factory_make ('audioconvert', 'convert') audioresample = gst.element_factory_make ('audioresample', 'resample') # 自動でオーディオ出力先を探して渡してくれる autoaudiosink = gst.element_factory_make ('autoaudiosink', 'audiosink') # WAVEファイルにエンコード wavenc = gst.element_factory_make ('wavenc', 'encode') # ファイルへの書き出し filesink = gst.element_factory_make ('filesink', 'filesink') filesink.props.location = sys.argv[2] # 処理の流れを通すパイプライン 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, flacparse, flacdec, tee, queue_audio, audioconvert, audioresample, autoaudiosink, queue_file, wavenc, filesink) # 要素を接続していく # Pythonではgst.LinkErrorが出るがValaでは戻り値で処理する # # filesrc > flacparse > flacdec # > tee > queue_audio > audioconvert > audioresample > autoaudiosink (サウンドカード出力) # +--> queue_file > wavenc > filesink (ファイル書き出し) try: filesrc.link (flacparse) flacparse.link (flacdec) # 分岐したいところでteeに入れる flacdec.link (tee) # teeからそれぞれのqueueプラグインにリンクする tee.link (queue_audio) tee.link (queue_file) # 音を鳴らすためのルート queue_audio.link (audioconvert) audioconvert.link (audioresample) audioresample.link (autoaudiosink) # 終点 # WAVEファイルに書き出すためのルート queue_file.link (wavenc) wavenc.link (filesink) # 終点 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 = GstTeeTest () sys.exit (app.main ())
下は実行例。
$ [gstteetest.pyの場所] [FLACファイルの場所] [WAVEファイルの場所]
関連記事:
参考URL:
使用したバージョン:
- Python 2.6.5
- PyGObject 2.21.1
- PyGST 0.10.18
- GStreamerライブラリ 0.10.29