【问题标题】:How to show a video in a Gtk dialog?如何在 Gtk 对话框中显示视频?
【发布时间】:2016-11-12 13:15:22
【问题描述】:

我想在对话框中显示视频。

因此,我创建了一个 Gst.DrawingArea 和一个 Gst.Pipeline,其中包含一个 Gst.Element“playbin”。

我创建了一个小例子,它会打开一个带有按钮的窗口。 如果单击该按钮,则会打开对话框并播放视频。 但我只听到视频的音频。

在另一个只有一个窗口和一个 DrawingArea 的示例中,它可以正常工作。

甚至可以在 Gtk.Dialog 中显示视频吗?

我正在使用 Python 3.5.2。

编辑:

#!/usr/bin/python3

from os import path

import gi
gi.require_version('Gst', '1.0')
gi.require_version('Gtk', '3.0')
gi.require_version('GdkX11', '3.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gst, Gtk, GLib, GdkX11, GstVideo

from gi.repository import GObject


GObject.threads_init()
Gst.init(None)
filename = "/path/to/movie.avi"
uri = 'file://' + filename


class DialogExample(Gtk.Dialog):

def __init__(self, parent):
    Gtk.Dialog.__init__(self, "My Dialog", parent, 0,
        (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
         Gtk.STOCK_OK, Gtk.ResponseType.OK))

    self.drawingarea = Gtk.DrawingArea.new()

    self.drawingarea.connect('draw', self.on_draw)
    #self.drawingarea.connect('realize', self.on_realize)
    self.drawingarea.connect('unrealize', self.on_unrealize)
    self.drawingarea.set_size_request(800, 600)
    self.set_default_size(1000, 800)

    self.btn_Play = Gtk.Button.new_with_label("Play")
    self.btn_Play.connect('clicked', self.on_click)

    box = self.get_content_area()
    box.add(self.drawingarea)
    box.add(self.btn_Play)

    # Create GStreamer pipeline
    self.pipeline = Gst.Pipeline()

    # Create bus to get events from GStreamer pipeline
    self.bus = self.pipeline.get_bus()
    self.bus.add_signal_watch()
    self.bus.connect('message::eos', self.on_eos)
    self.bus.connect('message::error', self.on_error)

    # This is needed to make the video output in our DrawingArea:
    self.bus.enable_sync_message_emission()
    self.bus.connect('sync-message::element', self.on_sync_message)

    # Create GStreamer elements
    self.playbin = Gst.ElementFactory.make('playbin', None)

    # Add playbin to the pipeline
    self.pipeline.add(self.playbin)

    # Set properties
    self.playbin.set_property('uri', uri)

    self.show_all()


def on_click(self, button):
    if self.playbin.get_state(0).state == Gst.State.PAUSED:
        self.pipeline.set_state(Gst.State.PLAYING)
        button.set_label("Stop")
    else:
        self.pipeline.set_state(Gst.State.PAUSED)
        button.set_label("Play")

def on_realize(self, widget, data=None):
    print("on_relaize")

    window = widget.get_window()
    self.xid = window.get_xid()
    self.playbin.set_window_handle(self.xid)


def on_draw(self, widget, cr):
    print("ondraw", self.playbin.get_state(0).state)
    if self.playbin.get_state(0).state < Gst.State.PAUSED:
        allocation = widget.get_allocation()

        cr.set_source_rgb(0, 0, 0)
        cr.rectangle(0, 0, allocation.width, allocation.height)
        cr.fill()

    self.on_realize(widget)


def on_unrealize(self, widget, data=None):
    # to prevent racing conditions when closing the window while playing
    self.playbin.set_state(Gst.State.NULL)
    self.pipeline.set_state(Gst.State.NULL)

def on_sync_message(self, bus, msg):
    if msg.get_structure().get_name() == 'prepare-window-handle':
        print('prepare-window-handle')

        print('on_sync', self.xid)

        print(msg)
        print(msg.src)


def on_eos(self, bus, msg):
    print('on_eos(): seeking to start of video')
    self.pipeline.seek_simple(
        Gst.Format.TIME,
        Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT,
        0
    )

def on_error(self, bus, msg):
    print('on_error():', msg.parse_error())



class DialogWindow(Gtk.Window):

def __init__(self):
    Gtk.Window.__init__(self, title="Dialog Example")

    self.set_border_width(6)

    button = Gtk.Button("Open dialog")
    button.connect("clicked", self.on_button_clicked)

    self.add(button)



def on_button_clicked(self, widget):
    dialog = DialogExample(self)


    response = dialog.run()

    if response == Gtk.ResponseType.OK:
        print("The OK button was clicked")
    elif response == Gtk.ResponseType.CANCEL:
        print("The Cancel button was clicked")

    dialog.destroy()


win = DialogWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()

编辑 2: UI after I pressed the play button

【问题讨论】:

    标签: python python-3.5 gtk3 gstreamer-1.0


    【解决方案1】:

    也许您的 drawingarea 没有调用 on_realize 方法... 我有同样的错误并决定强制它,在我的代码中保持这种方式:

    def on_realize(self, widget):
        print("on realize")
        window = widget.get_window()
        window_handle = window.get_xid()
        self.player.set_window_handle(window_handle)
    
    def on_draw(self, widget, cr):
        print("ondraw",self.player.get_state(0).state)
        if self.player.get_state(0).state < Gst.State.PAUSED:
            allocation = widget.get_allocation()
    
            cr.set_source_rgb(0, 0, 0)
            cr.rectangle(0, 0, allocation.width, allocation.height)
            cr.fill()
    
        self.on_realize(widget)
    

    这样,ondraw完成后,自动调用on_realize方法

    我尝试了你的代码,在下面评论中描述的修改之后,它变成了这个样子

    #!/usr/bin/python3
    
    from os import path
    
    import gi
    gi.require_version('Gst', '1.0')
    gi.require_version('Gtk', '3.0')
    gi.require_version('GdkX11', '3.0')
    gi.require_version('GstVideo', '1.0')
    from gi.repository import Gst, Gtk, GLib, GdkX11, GstVideo
    
    from gi.repository import GObject
    
    
    GObject.threads_init()
    Gst.init(None)
    filename = "/home/eduardo/Downloads/tears_of_steel_1080p.webm"
    uri = 'file://' + filename
    
    
    class DialogExample(Gtk.Dialog):
    
        def __init__(self, parent):
            Gtk.Dialog.__init__(self, "My Dialog", parent, 0,
                (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                 Gtk.STOCK_OK, Gtk.ResponseType.OK))
    
            self.drawingarea = Gtk.DrawingArea.new()
    
            self.drawingarea.connect('draw', self.on_draw)
            self.drawingarea.connect('realize', self.on_realize)
            self.drawingarea.connect('unrealize', self.on_unrealize)
            self.drawingarea.set_size_request(800, 600)
            self.set_default_size(1000, 800)
    
            self.btn_Play = Gtk.Button.new_with_label("Play")
            self.btn_Play.connect('clicked', self.on_click)
    
            box = self.get_content_area()
            box.add(self.drawingarea)
            box.add(self.btn_Play)
    
            # Create GStreamer pipeline
            self.pipeline = Gst.Pipeline()
    
            # Create bus to get events from GStreamer pipeline
            self.bus = self.pipeline.get_bus()
            self.bus.add_signal_watch()
            self.bus.connect('message::eos', self.on_eos)
            self.bus.connect('message::error', self.on_error)
    
            # This is needed to make the video output in our DrawingArea:
            self.bus.enable_sync_message_emission()
            self.bus.connect('sync-message::element', self.on_sync_message)
    
            # Create GStreamer elements
            self.playbin = Gst.ElementFactory.make('playbin', None)
    
            # Add playbin to the pipeline
            self.pipeline.add(self.playbin)
    
            # Set properties
            self.playbin.set_property('uri', uri)
    
            self.show_all()
    
    
        def on_click(self, button):
            if self.playbin.get_state(0).state != Gst.State.PAUSED:
                self.pipeline.set_state(Gst.State.PLAYING)
                button.set_label("Stop")
            else:
                self.pipeline.set_state(Gst.State.PAUSED)
                button.set_label("Play")
    
        def on_realize(self, widget, data=None):
            print("on_relaize")
    
            window = widget.get_window()
            self.xid = window.get_xid()
    
    
        def on_draw(self, widget, cr):
            print("ondraw", self.playbin.get_state(0).state)
            if self.playbin.get_state(0).state < Gst.State.PAUSED:
                allocation = widget.get_allocation()
    
                cr.set_source_rgb(0, 0, 0)
                cr.rectangle(0, 0, allocation.width, allocation.height)
                cr.fill()
    
    #       self.on_realize(widget)
    
    
        def on_unrealize(self, widget, data=None):
            # to prevent racing conditions when closing the window while playing
            self.playbin.set_state(Gst.State.NULL)
            self.pipeline.set_state(Gst.State.NULL)
    
        def on_sync_message(self, bus, msg):
            if msg.get_structure().get_name() == 'prepare-window-handle':
                print('prepare-window-handle')
    
                print('on_sync', self.xid)
                self.playbin.set_window_handle(self.xid)
    
                print(msg)
                print(msg.src)
    
    
        def on_eos(self, bus, msg):
            print('on_eos(): seeking to start of video')
            self.pipeline.seek_simple(
                Gst.Format.TIME,
                Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT,
                0
            )
    
        def on_error(self, bus, msg):
            print('on_error():', msg.parse_error())
    
    
    
    class DialogWindow(Gtk.Window):
    
        def __init__(self):
            Gtk.Window.__init__(self, title="Dialog Example")
    
            self.set_border_width(6)
    
            button = Gtk.Button("Open dialog")
            button.connect("clicked", self.on_button_clicked)
    
            self.add(button)
    
    
    
        def on_button_clicked(self, widget):
            dialog = DialogExample(self)
    
    
            response = dialog.run()
    
            if response == Gtk.ResponseType.OK:
                print("The OK button was clicked")
            elif response == Gtk.ResponseType.CANCEL:
                print("The Cancel button was clicked")
    
            dialog.destroy()
    
    
    win = DialogWindow()
    win.connect("delete-event", Gtk.main_quit)
    win.show_all()
    Gtk.main()
    

    【讨论】:

    • 尝试导入这个库 import gi gi.require_version('Gst', '1.0') gi.require_version('Gtk', '3.0') gi.require_version('GdkX11', '3.0' ) gi.require_version('GstVideo', '1.0') from gi.repository import Gst, Gtk, GLib, GdkX11, GstVideo
    • 我使用on_draw 方法和缺失的导入编辑了上面的代码示例。我只忘记了 GLib。我还添加了一个播放/停止按钮。肯定调用了on_realize方法,但是没有显示视频,我只听到音频。它在您的项目中有效吗?
    • 在进程代码部分点击播放我们有“if self.playbin.get_state(0).state == Gst.State.PAUSED:” you must change to “if self. playbin.get_state(0).state != Gst.State.PLAYING:" 导致管道以 NULL 状态开始
    • 我复制了您的代码,并且只更改了电影的路径(avi 或 mkv 文件)。我在上面添加了一张图片,它显示了我按下播放按钮后的 UI。我可以听到电影的声音,但电影本身是不可见的。我错过了一些模块或包吗?我使用带有 Cinnamon 的 Linux Mint 18.1
    • 评论这一行并告诉我它是否与其他窗口中的图像一起运行 [self.bus.connect('sync-message::element', self.on_sync_message)]
    【解决方案2】:

    这是另一种方式,使用 gstreamer 的 gtksink。如果正在运行“讨厌”的合成器,这甚至应该可以工作(正如提问者提到的使用 Cinnamon 的 Linux Mint 中的问题):

    #!/usr/bin/env python3
    
    import gi
    gi.require_version("Gtk", "3.0")
    gi.require_version("Gst", "1.0")
    from gi.repository import Gtk, Gst
    
    import os, sys
    
    Gst.init(None)
    Gst.init_check(None)
    
    
    class PlayerWidget(Gtk.Box):
        """ This is the gtksink widget """
        def __init__(self, parent):
            super().__init__()
            self.parent = parent
            self.player = Gst.ElementFactory.make("playbin")
    
            self.connect('realize', self.on_realize)
    
        def on_realize(self, widget):
            playerFactory = self.player.get_factory()
            gtksink = playerFactory.make('gtksink')
            self.player.set_property("video-sink", gtksink)
    
            self.pack_start(gtksink.props.widget, True, True, 0)
            gtksink.props.widget.show()
    
    
    class VideoDialog(Gtk.Dialog):
        def __init__(self, parent, filename):
            Gtk.Dialog.__init__(self, "VideoDialog", parent, 0,
                                (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                                 Gtk.STOCK_OK, Gtk.ResponseType.OK))
            self.filename = filename
            self.__setupUi()
    
        def __setupUi(self):
            self.set_default_size(800, 400)
    
            self.playerWidget = PlayerWidget(parent=self)
            self.playerWidget.set_size_request(800, 450)
    
            self.player = self.playerWidget.player
            self.player.set_property("uri", self.filename)
    
            self.btnPlay = Gtk.Button(label="Play")
            self.btnPlay.connect("clicked", self.on_btnPlay_clicked)
    
            self.hboxBtn01 = Gtk.Box()
            self.hboxBtn01.add(self.btnPlay)
    
            self.vbox_intern = self.get_content_area()
            self.vbox_intern.add(self.playerWidget)
            self.vbox_intern.add(self.hboxBtn01)
    
            self.show_all()
    
        def on_btnPlay_clicked(self, widget):
            playerState = self.player.get_state(Gst.SECOND).state
            if playerState <= Gst.State.PAUSED:
                self.player.set_state(Gst.State.PLAYING)
                self.btnPlay.set_label("Pause")
            elif playerState is Gst.State.PLAYING:
                self.player.set_state(Gst.State.PAUSED)
                self.btnPlay.set_label("Play")
    
    
    class MainWindow(Gtk.Window):
        def __init__(self):
            Gtk.Window.__init__(self, title="Open VideoDialog")
    
            self.filename = "https://www.freedesktop.org/software/gstreamer-sdk" + \
                                            "/data/media/sintel_trailer-480p.webm"
            self.set_border_width(40)
            self.set_default_size(200, 110)
            button = Gtk.Button("Don't push the button!")
            button.connect("clicked", self.on_button_clicked)
            self.add(button)
    
        def on_button_clicked(self, widget):
            videoDialog = VideoDialog(self, self.filename)
            videoDialog.set_transient_for(self)
            videoDialog.set_modal(True)
    
            if Gtk.ResponseType.OK == videoDialog.run():
                print("Response: OK")
            else:
                print("Response: Cancel")
    
            videoDialog.player.set_state(Gst.State.NULL)
            videoDialog.destroy()
    
    win = MainWindow()
    win.connect("destroy", Gtk.main_quit)
    win.show_all()
    Gtk.main()
    

    编辑: 似乎在基于 debian 的发行版中,gstreamer 的 gtksink 不在它所属的包中(即gst-plugins-good)。在 Debian 9 上,它是 gst-plugins-bad(?) 的一部分。在 Ubuntu 18.04 上,gst-inspect1.0 gtksink 声称它是 gst-plugins-good 的一部分,但事实并非如此。它位于gstreamer1.0-gtk3。所以你必须安装...?

    【讨论】:

    • 如何播放本地url文件:://​​/ ?
    • @vishal-mote:将 self.filename(在 MainWindow 类中)更改为您的本地 URL。示例:self.filename = "file:///home//path/to/file"
    【解决方案3】:

    vishal-mote,我刚刚添加了文件选择器对话框并将 uri/url 编码添加到 file://,以便 gtk 应用程序播放选定的视频。

    #!/usr/bin/env python3
    
    gi.require_version("Gtk", "3.0")
    gi.require_version("Gst", "1.0")
    from gi.repository import Gtk, Gst
    from urllib.parse import urlparse 
    from urllib.parse import quote
    import os, sys
    
    Gst.init(None)
    Gst.init_check(None)
    
    
    class PlayerWidget(Gtk.Box):
        """ This is the gtksink widget """
        def __init__(self, parent):
            super().__init__()
            self.parent = parent
            self.player = Gst.ElementFactory.make("playbin")
    
            self.connect('realize', self.on_realize)
    
        def on_realize(self, widget):
            playerFactory = self.player.get_factory()
            gtksink = playerFactory.make('gtksink')
            self.player.set_property("video-sink", gtksink)
    
            self.pack_start(gtksink.props.widget, True, True, 0)
            gtksink.props.widget.show()
    
    
    class VideoDialog(Gtk.Dialog):
        def __init__(self, parent, filename):
            Gtk.Dialog.__init__(self, "VideoDialog", parent, 0,
                                (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                                 Gtk.STOCK_OK, Gtk.ResponseType.OK))
            self.filename = filename
            self.__setupUi()
    
        def __setupUi(self):
            self.set_default_size(800, 400)
    
            self.playerWidget = PlayerWidget(parent=self)
            self.playerWidget.set_size_request(800, 450)
    
            self.player = self.playerWidget.player
            self.player.set_property("uri", self.filename)
    
            self.btnPlay = Gtk.Button(label="Play")
            self.btnPlay.connect("clicked", self.on_btnPlay_clicked)
    
            self.hboxBtn01 = Gtk.Box()
            self.hboxBtn01.add(self.btnPlay)
    
            self.vbox_intern = self.get_content_area()
            self.vbox_intern.add(self.playerWidget)
            self.vbox_intern.add(self.hboxBtn01)
    
            self.show_all()
    
    
    
        def on_btnPlay_clicked(self, widget):
            playerState = self.player.get_state(Gst.SECOND).state
            if playerState <= Gst.State.PAUSED:
                self.player.set_state(Gst.State.PLAYING)
                self.btnPlay.set_label("Pause")
            elif playerState is Gst.State.PLAYING:
                self.player.set_state(Gst.State.PAUSED)
                self.btnPlay.set_label("Play")
    
    
    class MainWindow(Gtk.Window):
        def __init__(self):
            Gtk.Window.__init__(self, title="Open VideoDialog")
    
            self.filename = "https://www.freedesktop.org/software/gstreamer-sdk" + \
                                            "/data/media/sintel_trailer-480p.webm"
            self.set_border_width(40)
            self.set_default_size(200, 110)
            button = Gtk.Button("Choose File!")
            button.connect("clicked", self.on_file_clicked)
            self.add(button)
            #uncomment below to start the player as if button is pressed
            #self.on_button_clicked(Gtk.Button)
            
            
            
        def on_file_clicked(self, widget):
            dialog = Gtk.FileChooserDialog(
                title="Please choose a file", parent=self, action=Gtk.FileChooserAction.OPEN
            )
            dialog.add_buttons(
                Gtk.STOCK_CANCEL,
                Gtk.ResponseType.CANCEL,
                Gtk.STOCK_OPEN,
                Gtk.ResponseType.OK,
            )
    
            self.add_filters(dialog)
    
            response = dialog.run()
            if response == Gtk.ResponseType.OK:
                print("Open clicked")
                print("File selected: " + dialog.get_filename())
                self.filename = quote(dialog.get_filename())
                #encode URI to file://<path+filename>
                self.filename = "file://"+self.filename
                print(self.filename)
                # when the file is selected cose the file dialog
                dialog.destroy()
                self.on_button_clicked(Gtk.Button)
                
            elif response == Gtk.ResponseType.CANCEL:
                print("Cancel clicked")
    
            dialog.destroy()
    
        def add_filters(self, dialog):
            filter_video = Gtk.FileFilter()
            filter_video.set_name("Video File")
            filter_video.add_mime_type("video/*")
            dialog.add_filter(filter_video)
    
            filter_any = Gtk.FileFilter()
            filter_any.set_name("Any files")
            filter_any.add_pattern("*")
            dialog.add_filter(filter_any)
            
            
    
        def on_button_clicked(self, widget):
            videoDialog = VideoDialog(self, self.filename)
            videoDialog.set_transient_for(self)
            videoDialog.set_modal(True)
    
            if Gtk.ResponseType.OK == videoDialog.run():
                print("Response: OK")
            else:
                print("Response: Cancel")
    
            videoDialog.player.set_state(Gst.State.NULL)
            videoDialog.destroy()
    
    win = MainWindow()
    win.connect("destroy", Gtk.main_quit)
    win.show_all()
    Gtk.main()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多