我正在为使用 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() 函数上中断,你可以从你的调试器中得到一个有意义的回溯。)