2

我正在为使用 gstreamer 的 linux 开发 wx Python 媒体 ctrl(感觉 wx 小部件版本必须是自定义编译的)......而且我发现 gstreamer 要么很糟糕,要么没有很好的文档,因为它是最令人困惑的事情。这是我当前的代码,我认为我要做的是获取 MESSAGE_TAG 类型的消息并从那里做一些事情,但我现在还不确定,当我当前运行它时,我得到一个错误。

代码

'''
Adapts GStreamer to fit wx.media.MediaCtrl
Why?
    - Yes wx.media.MediaCtrl has a gstreamer backend
      but it is useless and well doesn't even work 
      because apparently the wx widgets C++ code
      needs to be compiled with gstreamer support
      now why they don't do that for the linux
      build that will be running on Linux with 
      a likely hood of using gstreamer I have no
      clue why but they don't.

Largely Ported from wxWidgets old code.
'''
import os

import wx
from wx.lib.newevent import NewCommandEvent
import gst
import gobject

from twisted.python import log


# Start threads otherwise watching messages from other threads gives us a segfault
gobject.threads_init()

# States
# might not be the numbers wx.media.MediaCtrl uses
MEDIASTATE_PLAYING = 0
MEDIASTATE_PAUSED =  1 
MEDIASTATE_STOPPED = 2

# Events
media_loaded_event  , EVT_MEDIA_LOADED   = NewCommandEvent()
media_play_event    , EVT_MEDIA_PLAY     = NewCommandEvent()
media_pause_event   , EVT_MEDIA_PAUSE    = NewCommandEvent()
media_stop_event    , EVT_MEDIA_STOP     = NewCommandEvent()
media_finished_event, EVT_MEDIA_FINISHED = NewCommandEvent()

class GstreamerCtrl(wx.Panel):

    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id, size = (0,0))
        loop = gobject.MainLoop()
        self.context = loop.get_context()

        # use GetState to get new state
        self.state = gst.STATE_NULL # current state 

        self.no_resource = True

        self.video_size = (0,0)

        self.caps = []

        self.paused_playback_position = 0

        wx.CallLater(500, self.gstreamer_loop)

        self.playbin = gst.element_factory_make("playbin2")

        # Code from http://www.majorsilence.com/pygtk_audio_and_video_playback_gstreamer
        bus = self.playbin.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)

        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnWinDestroy, self)

    def on_sync_message(self, bus, message):
        # Code from http://pythonide.blogspot.com/2008/03/howto-write-wxpython-video-player-with.html
        # See  also http://pygstdocs.berlios.de/pygst-tutorial/videomixer.html
        print "===================="
        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.GetHandle())

    def SetInitialSize(self):
        pass# for cross compartibilitry with others but gstreamer does it automatically

    def on_message(self, bus, message):
        if message.type == gst.MESSAGE_STATE_CHANGED:
            # http://pygstdocs.berlios.de/pygst-reference/class-gstmessage.htm
            state = message.parse_state_changed()#current state [oldstate, newstate, pendingstate]
            self.state = state[1]# update the state (we don't have to do this if it is a repeat because it was set the first time around)
            if self.state == gst.STATE_READY:# if this is the first load state (sense it isn't a repeat and it is a STATE_READY)
                print "Firing EVT_MEDIA_LOADED"
                self._send_event(media_loaded_event)

        # get gstreamer information
        elif message.type == gst.MESSAGE_TAG:
            self.caps.append(message)
            # is this where we get the size; why is it so unclear  

        # Detect end of stream and set state to to NULL
        # Code From http://www.majorsilence.com/pygtk_audio_and_video_playback_gstreamer
        elif message.type == gst.MESSAGE_EOS:
            log.msg("Gstreaner [Debug]: EOS")
            # end of file so stop and say at the end
            self.Stop()
            # at end of file
            self._send_event(media_finished_event)

        elif message.type == gst.MESSAGE_ERROR:
            (err, debug) = message.parse_error()
            print "Gstreamer [ERROR]: %s" % err
            print "Gstreamer [Debug]: %s" % debug
            # Some unknown error have to set to null not anything else cause could be error with path name
            self.playbin.set_state(gst.STATE_NULL)
            self._send_event(media_stop_event)
            self._send_event(media_finished_event)


    def gstreamer_loop(self):
        self.context.iteration(True)
        wx.CallLater(500, self.gstreamer_loop)

    def _send_event(self, event):
        print "Firing an event"
        evt = event(self.GetId())
        wx.PostEvent(self, evt)

    def Load(self, file_path):
        self.no_resource = False
        self.paused_playback_position = 0
        file_path = os.path.abspath(file_path)
        print "Loading File " + file_path
        print "Loading through URI file://"
        self.LoadURI("file://" + file_path )
        return True

    def LoadURI(self, uri):
        self.no_resource = False
        log.msg("URI: %s" % uri)
        self.paused_playback_position = 0
        self.playbin.set_state(gst.STATE_NULL) 
        self.playbin.set_property("uri", uri)
        print self.state
        # Start download
        self.playbin.set_state(gst.STATE_PLAYING)
        self.playbin.set_state(gst.STATE_PAUSED)
        return True

    def Play(self):
        if self.no_resource:
            return False # see stop() for why false here but true for stop
        self.playbin.set_state(gst.STATE_PLAYING)
        print "Firing EVT_MEDIA_PLAY"
        self._send_event(media_play_event)
        return True


    def Pause(self):
        if self.no_resource:
            return False # see stop() for why false here but true for stop
        self.paused_playback_position = self.Tell() 
        self.playbin.set_state(gst.STATE_PAUSED)        
        print "Firing EVT_MEDIA_PAUSE"
        self._send_event(media_pause_event)
        return True

    def Stop(self):
        # it seeems that to stop on gstreamer you set the state to paused
        # and then set the stream position to 0
        # why not just have STOPPED state or have NULL be stopped
        # What is used in wxWidgets code now set
        if self.no_resource:
            # confirms stop was good because we are "stopped" play pause however say they failed because not "playing" or "paused"
            print "Firing EVT_MEDIA_STOPPED"
            self._send_event(media_stop_event)
            return True
        self.paused_playback_position = 0
        self.Seek(0)
        self.playbin.set_state(gst.STATE_PAUSED)
        print "Firing EVT_MEDIA_STOPPED"
        self._send_event(media_stop_event)
        return True

    def GetState(self):
        # Stop  - 0
        # Pause - 1
        # Play  - 2    
        state = self.state
        # see http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstElement.html#gst-element-get-state
        if state == gst.STATE_PAUSED:
            if self.paused_playback_position == 0:
                return MEDIASTATE_STOPPED
            else:
                return MEDIASTATE_PAUSED
        elif state == gst.STATE_PLAYING:
            return MEDIASTATE_PLAYING
        else:# Other states (NULL, READY, etc)
            return MEDIASTATE_STOPPED


    def SetVolume(self, volume):
        # wx media media ctrl takes a value between 0.0 and 1.0
        # gstreamer takes a value between 0 and 10
        # maybe not volume seems funny
        # try setting it to * 10 again still weird setting it back
        self.playbin.set_property("volume", volume)

    def Tell(self):
        if self.state != gst.STATE_PLAYING:
            # Any other state other than playing return the paused time otherwise something strange could be returned
            return self.paused_playback_position
        time_format = gst.Format(gst.FORMAT_TIME) #time in nanoseconds
        try:
            data = self.playbin.query_position(time_format)
        except gst.QueryError:
            return -1
        position = int(data[0] / 1000000.0)
        return position

    def Length(self):
        time_format = gst.Format(gst.FORMAT_TIME) #time in nanoseconds
        try:
            data = self.playbin.query_duration(time_format)
        except gst.QueryError:
            return -1
        length = int(data[0] / 1000000.0)
        return length

    def Seek(self, position):
        if self.no_resource:
            return False
        print "Seeking to %s" % position
        if position < 0 or position > self.Length():
            return
        self.paused_playback_position = position
        position_ns = position * 1000000
        self.playbin.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, position_ns)
        return True

    def OnWinDestroy(self, event):
        self.playbin.set_state(gst.STATE_NULL)


if __name__ == "__main__":
    a = wx.App(False)
    f = wx.Frame(None, -1, size = (500,300))

    PlayerSizer = wx.BoxSizer(wx.VERTICAL)
    PlayerSizer.Add(self.player, flag = wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border = 10, proportion = 1)
    f.d = GstreamerCtrl(f, -1)
    f.Show()
    wx.CallLater(10000, f.d.LoadURI,  "http://www.tools4movies.com/dvd_catalyst_profile_samples/Harold%20Kumar%203%20Christmas%20bionic.mp4")
    a.MainLoop()

当前误差

程序“Test.py”收到 X Window 系统错误。这可能反映了程序中的错误。错误是“BadValue(整数参数超出操作范围)”。(详情:serial 62 error_code 2 request_code 133 minor_code 19) (程序员注意:通常X错误是异步报告的,也就是说,你会在导致错误后一段时间收到错误。要调试你的程序,请使用--同步命令行选项来改变这种行为。如果你在 gdk_x_error() 函数上中断,你可以从你的调试器中得到一个有意义的回溯。)

4

0 回答 0