Python with GTK3 and Gst 1.0

GStreamer 1.0 を PyGI で試してみた。
Seekbar は面倒くさいので付けていない、Play/Pause ボタンのみ。
動画ファイルをドラッグアンドドロップすれば再生できるサンプル。

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

import gi
gi.require_version("Gtk", "3.0")
gi.require_version("Gst", "1.0")

"""
    GdkX11   @ get_xid()
    GstVideo @ xvimagesink
"""

from gi.repository import GObject, Gst, Gtk, Gdk, GdkX11, GstVideo

class Player(Gtk.Window):
    """
        Simple DnD Video Player
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", self.on_quit)
        # Status
        self.status = Gst.State.NULL
        # Video Area
        self.video_area = Gtk.DrawingArea()
        # Disable Double Buffered
        self.video_area.set_double_buffered(False)
        # Play/Pause Button
        self.button = Gtk.Button.new_with_label("Null")
        self.button.connect("clicked", self.on_button_clicked)
        # playbin
        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.on_message)
        bus.connect("sync-message::element", self.on_sync_message)
        # DnD
        dnd_list = Gtk.TargetEntry.new("text/uri-list", 0, 0)
        self.drag_dest_set(
                Gtk.DestDefaults.MOTION
                | Gtk.DestDefaults.HIGHLIGHT
                | Gtk.DestDefaults.DROP,
                [dnd_list],
                Gdk.DragAction.MOVE )
        self.drag_dest_add_uri_targets()
        self.connect("drag-data-received", self.on_drag_data_received)
        # pack
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0) 
        vbox.pack_start(self.video_area, True, True, 0)
        vbox.pack_start(self.button, False, False, 0)
        self.add(vbox)
        self.resize(640, 360)
        self.show_all()

    def on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
        uris = data.get_uris()
        self.player.set_state(Gst.State.NULL)
        self.player.props.uri = uris[0]
        self.player.set_state(Gst.State.PLAYING)

    def on_quit(self, widget, data=None):
        self.player.set_state(Gst.State.NULL)
        Gtk.main_quit()

    def on_sync_message(self, bus, message):
        if message.get_structure().get_name() == "prepare-window-handle":
            xid = self.video_area.props.window.get_xid()
            imagesink = message.src
            #imagesink.props.force_aspect_ratio = False
            imagesink.set_window_handle(xid)

    def on_message(self, bus, message):
        t = message.type
        if t == Gst.MessageType.EOS:
            self.player.set_state(Gst.State.NULL)
            self.status = Gst.State.NULL
            self.button.set_label("Null")
        elif t == Gst.MessageType.ERROR:
            self.messagebox(message.parse_error())
            self.emit("delete-event", None)
        elif t == Gst.MessageType.STATE_CHANGED:
            status = message.parse_state_changed()[1]
            if status == Gst.State.PLAYING: 
                if not self.status == Gst.State.PLAYING:
                    self.status = Gst.State.PLAYING
                    self.button.set_label("Pause")
            elif status == Gst.State.PAUSED: 
                if not self.status == Gst.State.PAUSED:
                    self.status = Gst.State.PAUSED
                    self.button.set_label("Play")

    def on_button_clicked(self, widget, data=None):
        if not self.player.props.uri == "":
            if self.status == Gst.State.PLAYING:
                self.player.set_state(Gst.State.PAUSED)
            elif self.status == Gst.State.PAUSED:
                self.player.set_state(Gst.State.PLAYING)

    def messagebox(self, text):
        dlg = Gtk.MessageDialog(
                self,
                Gtk.DialogFlags.MODAL,
                Gtk.MessageType.ERROR,
                Gtk.ButtonsType.OK,
                text)
        dlg.set_title("Error")  
        r = dlg.run()  
        dlg.destroy()
        return r

if __name__ == "__main__":
    GObject.threads_init()
    Gst.init(None)
    Player()
    Gtk.main()

GdkX11 は xid を得るのに必要、GstVideo は DrawingArea に貼り付けるのに必要。
つまり音楽プレイヤーを作るなら別にインポートする必要は無い。

以前リンクしたところは GstPipeline に playbin をセットしていた。
けど普通に playbin 自体を GstPipeline として使えるということみたい。

expose_event が無くなったおかげか xid を得るのに一手間がいらなくなった。
アスペクト比を保持させない方法も PyGtk と同じだった。

STATE_CHANGED メッセージで再生中に Gst.State.PLAYING が延々流れてくるのだが…
PyGtk ではこんなだったかな、覚えていないや。

というか PyGtk と Gst 0.10 で作るのと全然変わっていなかったという。

GStreamer のバージョンが上がったメリットは今の私には解らない。
多分そのうち気がつくだろう、多分。

しかし fedora 18 で検索しても皆 GStreamer なんて一言も触れていない。
Nautilus でサムネイルできなくて皆阿鼻叫喚してるかと思ったけど実際の話メインで使っている人がほとんどいないってことなのね。

しかし今日だけで五回もフリーズした、早く安定しないかなぁ。