Python GStreamer チュートリアル

⑤ Src, sink, pad … oh my!

チュートリアル第5章。… Oh my! をどう訳していいかわからず原題そのままで。[DeepLの翻訳](https://www.deepl.com/translator#en/ja/Oh my!)では「すごい!」あたりかな。前節で苦労したのでホントは「オーマイガーッ!!」と叫びたいところ。


その名前が示すように、src はデータを「送信」するオブジェクトであり、sink はデータを「受信」するオブジェクトです。これらのオブジェクトはパッドで互いに接続します。パッドは src または sink のどちらかになります。ほとんどの要素は src と sink の両方のパッドを持っています。例えば、mad MP3 デコーダ要素は下の図のようになります。

Gstreamer エレメント

高レベル要素についてもっと知りたければ、おなじみの gst-inspect がお勧めです。

gst-inspect-1.0 mpg123audiodec

特に、継承図を見ると、mpg123audiodec は要素であることがわかります。

GObject
 +----GInitiallyUnowned
       +----GstObject
             +----GstElement
                   +----GstAudioDecoder
                         +----GstMpg123AudioDec

要素をリンクする方法はいろいろあります。pipeline-example.py では、Gst.Pipeline.add()と生成された要素の .link()メソッドを使用しました。また、parse_launch()関数を使って、完全に出来上がったパイプラインを作ることもできます。例にある多くの .add() の呼び出しは、次のように書き換えることができます。

mp3_pipeline = Gst.parse_launch("filesrc location=file.mp3 name=file-source ! \
                                 mpegaudioparse name=audio-parse ! \
                                 mpg123audiodec name=mp3-decoder ! \
                                 audioconvert name=converter ! audioresample name=resamler ! \
                                 autoaudiosink name=audio-output" )

この関数呼び出しで使用されるマイクロ言語は、gst-launchコマンドラインプログラムのものです。

.link() メソッドを使用してパッドを手動でリンクする場合は、src-pad を sink-pad にリンクすることを確認してください。例外が皆無とはいえませんが、ルールはありません。Gst.GhostPadは、それ自身と同じ種類のパッドにリンクされる必要があります。ゴーストパッドがどのように動作するかは、例2.2の追加ですでに示しました。Gst.Binは、Gst.GhostPadをビン内の要素にリンクしないと、他のオブジェクトにリンクできません。セクション③ の playbin-example-video.py の例は、次のようになります。

Gstreamer パッド

そして、上のゴーストパッドは「sink (流し)」というタイプで作成してください!

パッドの中には、いつでも使えるとは限らず、使用している時にしか作れないものもあります。このようなパッドは、「動的パッド」と呼ばれています。次の例では、動的に作成されたパッドを oggdemux で使用する方法を説明します。デマルチプレクサとデコーダの間のリンクは demuxer_callback() メソッドで作成され、“pad-added” シグナルを使用してデマルチプレクサにパッドが作成されるたびに呼び出されます。

# dynamic-ghostpad-example.py

import sys, os
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
gi.require_version("Gst", "1.0")
from gi.repository import Gst, GObject

class GTK_Main(object):

    def __init__(self):
        window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
        window.set_title("Vorbis-Player")
        window.set_default_size(500, 200)
        window.connect("destroy", Gtk.main_quit, "WM destroy")
        vbox = Gtk.VBox()
        window.add(vbox)
        self.entry = Gtk.Entry()
        vbox.pack_start(self.entry, False, False, 0)
        self.button = Gtk.Button("Start")
        vbox.add(self.button)
        self.button.connect("clicked", self.start_stop)
        window.show_all()

        self.player = Gst.Pipeline.new("player")
        source = Gst.ElementFactory.make("filesrc", "file-source")
        demuxer = Gst.ElementFactory.make("oggdemux", "demuxer")
        demuxer.connect("pad-added", self.demuxer_callback)
        self.audio_decoder = Gst.ElementFactory.make("vorbisdec", "vorbis-decoder")
        audioconv = Gst.ElementFactory.make("audioconvert", "converter")
        audiosink = Gst.ElementFactory.make("autoaudiosink", "audio-output")

        self.player.add(source)
        self.player.add(demuxer)
        self.player.add(self.audio_decoder)
        self.player.add(audioconv)
        self.player.add(audiosink)

        source.link(demuxer)
        self.audio_decoder.link(audioconv)
        audioconv.link(audiosink)

        bus = self.player.get_bus()
        bus.add_signal_watch()
        bus.connect("message", self.on_message)

    def start_stop(self, w):
        if self.button.get_label() == "Start":
            filepath = self.entry.get_text().strip()
            if os.path.isfile(filepath):
                filepath = os.path.realpath(filepath)
                self.button.set_label("Stop")
                self.player.get_by_name("file-source").set_property("location", filepath)
                self.player.set_state(Gst.State.PLAYING)
            else:
                self.player.set_state(Gst.State.NULL)
                self.button.set_label("Start")

    def on_message(self, bus, message):
        t = message.type
        if t == Gst.MessageType.EOS:
            self.player.set_state(Gst.State.NULL)
            self.button.set_label("Start")
        elif t == Gst.MessageType.ERROR:
            err, debug = message.parse_error()
            print("Error: %s" % err, debug)
            self.player.set_state(Gst.State.NULL)
            self.button.set_label("Start")

    def demuxer_callback(self, demuxer, pad):
        adec_pad = self.audio_decoder.get_static_pad("sink")
        pad.link(adec_pad)


GObject.threads_init()
Gst.init(None)
GTK_Main()
Gtk.main()

この4つの章を読んだ後は、休憩が必要ですね。Happy Hacking! 今後も興味深い章が続きますので、ご期待ください。


関連記事