Riff player update, and random things I learned about wxpython
posted in programming by jon on 2009-01-04
Over the holidays I decided to revisit my riffplayer project, and see if I could rework it a bit, using wxpython for the GUI, and gstreamer to handle media playback. The advantage to this approach is that I get native widgets and dialogs, and I'm no longer dependent on openGL acceleration for video playback.
Obligatory screenshot of current progress
After several hours, and 500 lines of python or so, I can say that I'm reasonably happy with both wxpython and gstreamer so far. With both though, there were a few things that I either had to figure out by trial and error, or piece together from random blogs. For the benefit of anybody who might run across this page, here are a few things I learned.
1.) Don't bother with IDs. The wxpython wiki's tutorial and examples usually show widgets being created with explicit integer ids, and then being later referenced by these same ids. Working this way requires you to worry about making sure each id is unique, and then having to look up the id later when you get ready to bind events to widgets. Instead, just use -1 as the id, and use the instance of the widget directly instead of using the id.
2.) Use built-in event hooks for updating the gui and background tasks. Rather than using a separate thread to update state in your application (an approach shown in one of the tutorials), use the built-in event hooks: wx.EVT_UPDATE_UI and wx.EVT_IDLE to update your gui or perform background tasks. Just bind these as you would any other events
An example, (from my media player):
class RiffPlayerFrame(wx.Frame): def __init__(self, parent, id, title): """Initialize the main riff player frame.""" wx.Frame.__init__(self, parent, wx.ID_ANY, title, size = (700, 500)) self.Bind(wx.EVT_CLOSE, self.Destroy) .... (omitted) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI) self.Bind(wx.EVT_IDLE, self.OnIdle) def OnIdle(self, event): """Event handler for the Idle task. Used to update non-gui background processes """ if not int(time.time()) % 5: if not self._sync_done: self._ApplyOffset() self._sync_done = True else: self._sync_done = False def OnUpdateUI(self, event): """Event handler for the EVT_UPDATE_UI psuedo-signal.""" self.offset_timer.SetLabel('Offset: %s' % self.offset) if self.synced: self.sync_button.SetValue(True) self.riff_slider.Disable() else: self.sync_button.SetValue(False) self.riff_slider.Enable() try: vid_duration_nano, _ = self.video.query_duration(gst.FORMAT_TIME) vid_position_nano, _ = self.video.query_position(gst.FORMAT_TIME) riff_duration_nano, _ = self.riff.query_position(gst.FORMAT_TIME) riff_position_nano, _ = self.riff.query_position(gst.FORMAT_TIME) except Exception, e: # will be raised if stream isn't rolled for playback return self.video_timer.SetLabel(self._FormatTimestamp(vid_position_nano)) self.video_slider.SetValue(vid_position_nano/1000000000) self.riff_timer.SetLabel(self._FormatTimestamp(riff_position_nano)) self.riff_slider.SetValue(riff_position_nano/1000000000)
- When using gstreamer to play multiple files simultaneously, you can cram multiple playbins into a single pipeline. After fighting with adders and demuxers for several hours, I came across an example that used this approach.
An example:
def _InitGstreamer(self): # set up gstreamer pipeline self.player = gst.Pipeline('player') self.riff = gst.element_factory_make('playbin', 'riff-pbin') self.riff.set_property('volume', 5.0) self.video = gst.element_factory_make('playbin', 'video-pbin') self.video.set_property('volume', 5.0) self.video_sink = gst.element_factory_make('autovideosink', 'video-sink') self.video.set_property('video-sink', self.video_sink) self.player.add(self.riff, self.video) bus = self.player.get_bus() bus.add_signal_watch() bus.enable_sync_message_emission() bus.connect('message', self.OnMessage) bus.connect('sync-message::element', self.OnSyncMessage)
Subscribe 