チュートリアル第7章。Capabilities(キャップ)の巻。
Capabilities、Gst.Caps は、Gst.PadTemplate に渡すことができる情報を格納するコンテナです。パイプラインの状態を再生または一時停止のいずれかに設定すると、エレメント内のパッドはストリームに使用するキャップをネゴシエートします。これで以下のパイプラインは完璧に動作します。
gst-launch-1.0 videotestsrc ! video/x-raw, width=320, height=240 ! xvimagesink
しかし、xvimagesink を ximagesink に交換しようとすると、うまくいかないことに気づくでしょう。これは、ximagesink がvideo/x-rawを扱えないため、扱える要素をパイプライン内で ximagesinkの前に置く必要があるからです。
gst-launch-1.0 videotestsrc ! video/x-raw, width=320, height=240 ! videoconvert ! ximagesink
また、ximagesinkはハードウェアのスケーリングをサポートしていないため、ソフトウェアでスケーリングを行いたい場合は、ビデオスケール要素を追加する必要があります。
gst-launch-1.0 videotestsrc ! video/x-raw, width=320, height=240 ! videoscale ! videoconvert ! ximagesink
上記の例をコードに入れるには、capsfilter 要素の中にキャップを入れる必要があります。
# capabilities-example.py
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:
def __init__(self):
window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
window.set_title("Videotestsrc-Player")
window.set_default_size(300, -1)
window.connect("destroy", Gtk.main_quit, "WM destroy")
vbox = Gtk.VBox()
window.add(vbox)
self.button = Gtk.Button("Start")
self.button.connect("clicked", self.start_stop)
vbox.add(self.button)
window.show_all()
self.player = Gst.Pipeline.new("player")
source = Gst.ElementFactory.make("videotestsrc", "video-source")
sink = Gst.ElementFactory.make("xvimagesink", "video-output")
caps = Gst.Caps.from_string("video/x-raw, width=320, height=240")
filter = Gst.ElementFactory.make("capsfilter", "filter")
filter.set_property("caps", caps)
self.player.add(source)
self.player.add(filter)
self.player.add(sink)
source.link(filter)
filter.link(sink)
def start_stop(self, w):
if self.button.get_label() == "Start":
self.button.set_label("Stop")
self.player.set_state(Gst.State.PLAYING)
else:
self.player.set_state(Gst.State.NULL)
self.button.set_label("Start")
GObject.threads_init()
Gst.init(None)
GTK_Main()
Gtk.main()
よく聞かれる質問でファイルの解像度を調べる方法が問われることがありますが、その方法の一つに、一時停止状態のデコードビン要素のキャップをチェックする方法があります。
※ このコードを実行してもビデオの解像度情報は得られなかった。caps構造から数字を取り出すところがまずいっぽい。
width = caps.get_structure(0).get_int('width')
height = caps.get_structure(0).get_int('height')
# capabilities-resolution-example.py
import 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:
def __init__(self):
window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
window.set_title("Resolutionchecker")
window.set_default_size(300, -1)
window.connect("destroy", Gtk.main_quit, "WM destroy")
vbox = Gtk.VBox()
window.add(vbox)
self.entry = Gtk.Entry()
self.entry.set_text("Sintel - Wikipedia.webm")
vbox.pack_start(self.entry, False, True, 0)
self.button = Gtk.Button("Check")
self.button.connect("clicked", self.start_stop)
vbox.add(self.button)
window.show_all()
self.player = Gst.Pipeline.new("player")
source = Gst.ElementFactory.make("filesrc", "file-source")
decoder = Gst.ElementFactory.make("decodebin", "decoder")
decoder.connect("pad-added", self.decoder_callback)
self.fakea = Gst.ElementFactory.make("fakesink", "fakea")
self.fakev = Gst.ElementFactory.make("fakesink", "fakev")
self.player.add(source)
self.player.add(decoder)
self.player.add(self.fakea)
self.player.add(self.fakev)
source.link(decoder)
bus = self.player.get_bus()
bus.add_signal_watch()
bus.connect("message", self.on_message)
def start_stop(self, w):
filepath = self.entry.get_text().strip()
if os.path.isfile(filepath):
filepath = os.path.realpath(filepath)
self.player.set_state(Gst.State.NULL)
self.player.get_by_name("file-source").set_property("location", filepath)
self.player.set_state(Gst.State.PAUSED)
def on_message(self, bus, message):
typ = message.type
if typ == Gst.MessageType.STATE_CHANGED:
if message.parse_state_changed()[1] == Gst.State.PAUSED:
decoder = self.player.get_by_name("decoder")
for pad in decoder.srcpads:
caps = pad.query_caps(None)
structure_name = caps.to_string()
width = caps.get_structure(0).get_int('width')
height = caps.get_structure(0).get_int('height')
print(structure_name[0:5], width, height )
print("..................................")
# width, heightの取得がうまくいかないので、以下のif文もTrueにならない。
if structure_name.startswith("video") and len(str(width)) < 6:
print("Width:%d, Height:%d" %(width, height))
self.player.set_state(Gst.State.NULL)
break
elif typ == Gst.MessageType.ERROR:
err, debug = message.parse_error()
print("エラーです!: %s" % err, debug)
self.player.set_state(Gst.State.NULL)
def decoder_callback(self, decoder, pad):
caps = pad.query_caps(None)
structure_name = caps.to_string()
if structure_name.startswith("video"):
fv_pad = self.fakev.get_static_pad("sink")
pad.link(fv_pad)
elif structure_name.startswith("audio"):
fa_pad = self.fakea.get_static_pad("sink")
pad.link(fa_pad)
GObject.threads_init()
Gst.init(None)
GTK_Main()
Gtk.main()
次の例では、3章2節のプレイビンを使用して、そのビデオシンクを、いくつかの要素で満たされた自作のビンに切り替えます。さて、あなたがテレビ局を運営していて、画面の右上にロゴを入れたいとしましょう。そのためには textoverlay (テキストオーバーレイ)を使用できますが、ソースの解像度がどのようなものであってもフォントが画面上でまったく同じサイズになるようにするには、すべてのフォントが画面に応じてスケーリングされるように幅を指定しなければなりません。
※ このコードを実行してもビデオは再生されなかった。message.typeの型が違うような?
typ = message.type
if typ == Gst.MessageType.EOS:
# capabilities-playbin-example.py
import 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:
def __init__(self):
window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
window.set_title("Video-Player")
window.set_default_size(500, 400)
window.connect("destroy", Gtk.main_quit, "WM destroy")
vbox = Gtk.VBox()
window.add(vbox)
hbox = Gtk.HBox()
vbox.pack_start(hbox, False, False, 0)
self.entry = Gtk.Entry()
self.entry.set_text("Sintel - Wikipedia.webm")
hbox.add(self.entry)
self.button = Gtk.Button("Start")
hbox.pack_start(self.button, False, False, 0)
self.button.connect("clicked", self.start_stop)
self.movie_window = Gtk.DrawingArea()
vbox.add(self.movie_window)
window.show_all()
# PlayBinの設定
self.player = Gst.ElementFactory.make("playbin", "player")
self.bin = Gst.Bin.new("my-bin")
videoscale = Gst.ElementFactory.make("videoscale")
videoscale.set_property("method", 1)
pad = videoscale.get_static_pad("sink")
ghostpad = Gst.GhostPad.new("sink", pad)
self.bin.add_pad(ghostpad)
caps = Gst.Caps.from_string("video/x-raw, width=720")
myfilter = Gst.ElementFactory.make("capsfilter", "filter")
myfilter.set_property("caps", caps)
textoverlay = Gst.ElementFactory.make('textoverlay')
textoverlay.set_property("text", "GNUTV")
textoverlay.set_property("font-desc", "normal 14")
conv = Gst.ElementFactory.make ("videoconvert", "conv")
videosink = Gst.ElementFactory.make("autovideosink")
self.bin.add(videoscale)
self.bin.add(myfilter)
self.bin.add(textoverlay)
self.bin.add(conv)
self.bin.add(videosink)
videoscale.link(myfilter)
myfilter.link(textoverlay)
textoverlay.link(conv)
conv.link(videosink)
self.player.set_property("video-sink", self.bin)
bus = self.player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect("message", self.on_message)
bus.connect("sync-message::element", self.on_sync_message)
def start_stop(self, w):
if self.button.get_label() == "Start":
filepath = self.entry.get_text().strip()
if os.path.exists(filepath):
filepath = os.path.realpath(filepath)
print(filepath)
self.button.set_label("Stop")
self.player.set_property("uri", "file:///" + 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):
print("on_messageのメッセージだよ", message.type)
# message.typeをプリントすると<flags GST_MESSAGE_STATE...>
# if文で期待しているものとは型が違うっぽい
typ = message.type
if typ == Gst.MessageType.EOS:
self.player.set_state(Gst.State.NULL)
self.button.set_label("Start")
elif typ == Gst.MessageType.ERROR:
self.player.set_state(Gst.State.NULL)
self.button.set_label("Start")
err, debug = message.parse_error()
print("Error: %s" % err, debug)
def on_sync_message(self, bus, message):
if message.structure is None:
return
message_name = message.structure.get_name()
print(message_name)
if message_name == "prepare-xwindow-id":
imagesink = message.src
imagesink.set_property("force-aspect-ratio", True)
imagesink.set_xwindow_id(self.movie_window.window.xid)
GObject.threads_init()
Gst.init(None)
GTK_Main()
Gtk.main()
capsを利用して解像度情報を得るコード、画面にテキストをかぶせるコード、どちらも動かなかった。怪しいところを抜き出したけれども、どう解決していいかわからない。
それよりも、細かいところにこだわるのを止めて、早くこのチュートリアルを終わらせたいです。
で、残りの8章・9章を見たところ、
- 8章:ビデオミキサー サンプルコードは動かないらしい
- 9章:ウェブカメラ・ビューワ
ということで、文字起こしツールにはあまり関係のない機能なので、GStreamer の勉強はここで終わりにします。