Paepoi » PyGObject Tips » GStreamer, ClutterGst で動画再生
GStreamer, ClutterGst で動画再生
# 最終更新日 2019.09.14
ClutterGst を使う方法の追記
古い手段も残すけど非推奨コードの書き換え
ClutterGst を使う方法の追記
古い手段も残すけど非推奨コードの書き換え
端末にて実験
GStreamer: open source multimedia framework
を利用した動画プレイヤーの作り方。
GNOME の人は Nautilus のサムネイルにも使われるのでお馴染みです。
Ubuntu の御仁はインストール時に入るでしょうけど、とにかく GStreamer デコーダーを入れてください。
デコーダーが入っている動画形式であれば Totem で再生可能かつ Nautilus でサムネイルできるはず。
その環境を構築した状態で以下のようなコマンドを打ち込んでみる。
動画ファイルの URI は自分の手持ちファイルに変更してください。
GStreamer は GNOME の様々な場面で利用されるため部品として提供されています。
つまり GStreamer で再生できる環境があるなら動画プレイヤーを作成する環境は揃っているということ。
を利用した動画プレイヤーの作り方。
GNOME の人は Nautilus のサムネイルにも使われるのでお馴染みです。
Ubuntu の御仁はインストール時に入るでしょうけど、とにかく GStreamer デコーダーを入れてください。
デコーダーが入っている動画形式であれば Totem で再生可能かつ Nautilus でサムネイルできるはず。
その環境を構築した状態で以下のようなコマンドを打ち込んでみる。
動画ファイルの URI は自分の手持ちファイルに変更してください。
$ gst-launch-1.0 playbin uri=file:///home/sasakima-nao/movie/katana.mp4DirectShow の IVideoWindow みたいなのが出てきて指定したファイルが再生できたはずです。
GStreamer は GNOME の様々な場面で利用されるため部品として提供されています。
つまり GStreamer で再生できる環境があるなら動画プレイヤーを作成する環境は揃っているということ。
ClutterGst
Clutter Gst 3.0.4 Reference Manual: Clutter Gst 3.0.4 Reference Manual
筆者が Y901x でやっている ClutterGst を使う方法。
多分ソースを見てもわからない人のほうが多いと思うので最小限の抜き出し。
細かい制御は Y901x のソースで、JavaScript だけど Python への変換は結構簡単。
筆者が Y901x でやっている ClutterGst を使う方法。
多分ソースを見てもわからない人のほうが多いと思うので最小限の抜き出し。
細かい制御は Y901x のソースで、JavaScript だけど Python への変換は結構簡単。
#!/usr/bin/env python3
import sys, re, gi
gi.require_version('Gtk', '3.0')
gi.require_version('Clutter', '1.0')
gi.require_version('GtkClutter', '1.0')
gi.require_version('ClutterGst', '3.0')
from gi.repository import Gtk, Clutter, GtkClutter, ClutterGst
class Win(Gtk.ApplicationWindow):
'''
Wayland, X.org 両方で動かすならコレが一番簡単
GtkClutter.Embed を使って Clutter を上に載せて使います
'''
def __init__(self, app):
Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
# var
self.src_width = 0
self.src_height = 0
#
self.player = ClutterGst.Playback()
self.player.connect('eos', self.on_eos)
self.player.connect('ready', self.on_ready)
self.content = ClutterGst.Content()
self.content.set_player(self.player)
self.actor = Clutter.Actor()
self.actor.set_background_color(Clutter.Color.new(0, 0, 0, 255))
self.actor.set_content(self.content)
embed = GtkClutter.Embed()
self.stage = embed.get_stage()
self.stage.add_child(self.actor)
self.stage.set_background_color(Clutter.Color.new(0, 0, 0, 255))
self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.vbox.connect('size-allocate', self.on_box_size)
self.vbox.pack_start(embed, True, True, 0)
self.add(self.vbox)
self.drag_dest_add_uri_targets()
self.show_all()
def do_drag_data_received(self, context, x, y, data, info, time):
uris = data.get_uris()
self.player.set_uri(uris[0])
def on_eos(self, player):
pass
def on_ready(self, player):
vsink = player.get_video_sink();
frame = vsink.get_frame()
self.src_width = frame.resolution.width
self.src_height = frame.resolution.height
allocation = self.vbox.get_allocation()
self.set_video_size(allocation)
player.set_playing(True)
def on_box_size(self, widget, allocation):
if self.src_width != 0:
self.set_video_size(allocation)
def set_video_size(self, allocation):
aw = allocation.width
ah = allocation.height
w = self.src_width
h = self.src_height
if aw * h > ah * w:
width = w * ah / h
height = ah
x = (aw - width) / 2
y = 0
else:
width = aw
height = h * aw / w
x = 0
y = (ah - height) / 2
self.actor.set_position(x, y)
self.actor.set_size(width, height)
class App(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self)
GtkClutter.init()
ClutterGst.init()
def do_startup(self):
Gtk.Application.do_startup(self)
Win(self)
def do_activate(self):
self.props.active_window.present()
app = App()
app.run(sys.argv)
古い手段
下記コードは Wayland では動きません。
C 言語の Wayland でのサンプルコードを見つけたので貼っておきます。
gst-wayland-gtk-demo.git - Unnamed repository; edit this file 'description' to name the repository.
以下は X.org 専用ですが参考にはなるのでしばらく残しておきます。
Novacut/GStreamer1.0 - Ubuntu Wiki
~jderose/+junk/gst-examples
Gst.ElementFactory.make() で playbin というパイプラインを作成。
DirectShow の IGraphBuilder のように面倒なことはコイツに「おまかせ」できるようだ。
get_bus() でパイプラインの gst.Bus オブジェクトを取得。
add_signal_watch() でメッセージを発行するように指定。
enable_sync_message_emission() はよく解らないんですけど…
映像と音声の同期メッセージを放出するように指示する関数っぽい。
そして GtkDrawingArea に貼り付ける。
一旦表示した所で xid を保存してソレを imagesink に割り当てます。
C 言語の Wayland でのサンプルコードを見つけたので貼っておきます。
gst-wayland-gtk-demo.git - Unnamed repository; edit this file 'description' to name the repository.
以下は X.org 専用ですが参考にはなるのでしばらく残しておきます。
Novacut/GStreamer1.0 - Ubuntu Wiki
~jderose/+junk/gst-examples
Gst.ElementFactory.make() で playbin というパイプラインを作成。
DirectShow の IGraphBuilder のように面倒なことはコイツに「おまかせ」できるようだ。
get_bus() でパイプラインの gst.Bus オブジェクトを取得。
add_signal_watch() でメッセージを発行するように指定。
enable_sync_message_emission() はよく解らないんですけど…
映像と音声の同期メッセージを放出するように指示する関数っぽい。
そして GtkDrawingArea に貼り付ける。
一旦表示した所で xid を保存してソレを imagesink に割り当てます。
#!/usr/bin/env python3
import sys, gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gst', '1.0')
gi.require_version('GstAudio', '1.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gtk, GObject, Gst, GstAudio, Gdk, GdkX11, GstVideo
class Win(Gtk.ApplicationWindow):
'''
動画ファイルをドロップすると再生する最小限のプレイヤー
X.org 状態でないと動かない
'''
def __init__(self, app):
'''
DnD で再生するだけの処理
'''
Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
# GStreamer を使う最小限の処理
self.player = Gst.ElementFactory.make('playbin', None)
bus = self.player.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect('message', self.gst_on_message)
bus.connect('sync-message::element', self.gst_on_sync_message)
# この DrawingArea に動画を貼り付ける
self.video_window = Gtk.DrawingArea()
self.add(self.video_window)
# DnD
self.drag_dest_add_uri_targets()
self.show_all()
# xid は show() の後に得る、Wayland は X11 ではないので xid が存在しない
self.xid = self.video_window.props.window.get_xid()
def set_uri(self, uri):
'''
playbin に uri をセット
一旦 NULL にしないと失敗する
'''
self.player.set_state(Gst.State.NULL)
self.player.props.uri = uri
self.player.set_state(Gst.State.PLAYING)
# W バッファリングを無効にする
#self.video_window.set_double_buffered(False) # 廃止
def do_delete_event(self, event):
'''
[閉じる] ボタンが押された
'''
self.player.set_state(Gst.State.NULL)
return False
def do_drag_data_received(self, drag_context, x, y, data, info, time):
'''
ファイルがドロップされた
'''
drop = data.get_uris()[0]
self.set_uri(drop)
def gst_on_message(self, bus, message):
'''
GStreamer から飛んでくるシグナルのハンドラ
'''
t = message.type
if t == Gst.MessageType.EOS:
self.player.set_state(Gst.State.NULL)
elif t == Gst.MessageType.ERROR:
self.player.set_state(Gst.State.NULL)
def gst_on_sync_message(self, bus, message):
'''
動画画面を GtkDrawingArea に貼り付ける
'''
if message.get_structure().get_name() == 'prepare-window-handle':
imagesink = message.src
imagesink.set_window_handle(self.xid)
class App(Gtk.Application):
def __init__(self):
Gtk.Application.__init__(self)
#GObject.threads_init() # 廃止
Gst.init()
def do_startup(self):
Gtk.Application.do_startup(self)
Win(self)
def do_activate(self):
self.props.active_window.present()
app = App()
app.run(sys.argv)
Copyright(C) sasakima-nao All rights reserved 2002 --- 2025.