GNOME Bugzilla – Bug 342294
Setting playbin property current-audio=-1 also stops the video stream
Last modified: 2008-05-05 10:03:27 UTC
Please describe the problem: Setting playbin property current-audio=-1 correctly disconnects audio but also stops the video stream Steps to reproduce: 1. Using gst-python play.py example, set up playbin property current-audio=-1 in the GstPlayer class, leave video as is 2. 3. Actual results: Audio stops as expected, video stops too Expected results: Video should continue to play Does this happen every time? yes Other information:
#!/usr/bin/env python # -*- Mode: Python -*- # vi:si:et:sw=4:sts=4:ts=4 import pygtk pygtk.require('2.0') import sys import gobject import pygst pygst.require('0.10') import gst import gst.interfaces import gtk class GstPlayer: def __init__(self, videowidget): self.playing = False self.player = gst.element_factory_make("playbin", "player") #<my mod here> #this should only stop the audio stream, but breaks video as well #against my A/V DV AVI clip self.player.set_property( "current-audio", -1 ) #</my mod> self.videowidget = videowidget self.on_eos = False bus = self.player.get_bus() bus.enable_sync_message_emission() bus.add_signal_watch() bus.connect('sync-message::element', self.on_sync_message) bus.connect('message', self.on_message) def on_sync_message(self, bus, message): if message.structure is None: return if message.structure.get_name() == 'prepare-xwindow-id': self.videowidget.set_sink(message.src) message.src.set_property('force-aspect-ratio', True) def on_message(self, bus, message): t = message.type if t == gst.MESSAGE_ERROR: err, debug = message.parse_error() print "Error: %s" % err, debug if self.on_eos: self.on_eos() self.playing = False elif t == gst.MESSAGE_EOS: if self.on_eos: self.on_eos() self.playing = False def set_location(self, location): self.player.set_property('uri', location) def query_position(self): "Returns a (position, duration) tuple" try: position, format = self.player.query_position(gst.FORMAT_TIME) except: position = gst.CLOCK_TIME_NONE try: duration, format = self.player.query_duration(gst.FORMAT_TIME) except: duration = gst.CLOCK_TIME_NONE return (position, duration) def seek(self, location): """ @param location: time to seek to, in nanoseconds """ gst.debug("seeking to %r" % location) event = gst.event_new_seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, location, gst.SEEK_TYPE_NONE, 0) res = self.player.send_event(event) if res: gst.info("setting new stream time to 0") self.player.set_new_stream_time(0L) else: gst.error("seek to %r failed" % location) def pause(self): gst.info("pausing player") self.player.set_state(gst.STATE_PAUSED) self.playing = False def play(self): gst.info("playing player") self.player.set_state(gst.STATE_PLAYING) self.playing = True def stop(self): self.player.set_state(gst.STATE_NULL) gst.info("stopped player") def get_state(self, timeout=1): return self.player.get_state(timeout=timeout) def is_playing(self): return self.playing class VideoWidget(gtk.DrawingArea): def __init__(self): gtk.DrawingArea.__init__(self) self.imagesink = None self.unset_flags(gtk.DOUBLE_BUFFERED) def do_expose_event(self, event): if self.imagesink: self.imagesink.expose() return False else: return True def set_sink(self, sink): assert self.window.xid self.imagesink = sink self.imagesink.set_xwindow_id(self.window.xid) class PlayerWindow(gtk.Window): UPDATE_INTERVAL = 500 def __init__(self): gtk.Window.__init__(self) self.set_default_size(410, 325) self.create_ui() self.player = GstPlayer(self.videowidget) def on_eos(): self.player.seek(0L) self.play_toggled() self.player.on_eos = lambda *x: on_eos() self.update_id = -1 self.changed_id = -1 self.seek_timeout_id = -1 self.p_position = gst.CLOCK_TIME_NONE self.p_duration = gst.CLOCK_TIME_NONE def on_delete_event(): self.player.stop() gtk.main_quit() self.connect('delete-event', lambda *x: on_delete_event()) def load_file(self, location): self.player.set_location(location) def create_ui(self): vbox = gtk.VBox() self.add(vbox) self.videowidget = VideoWidget() vbox.pack_start(self.videowidget) hbox = gtk.HBox() vbox.pack_start(hbox, fill=False, expand=False) self.pause_image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PAUSE, gtk.ICON_SIZE_BUTTON) self.pause_image.show() self.play_image = gtk.image_new_from_stock(gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_BUTTON) self.play_image.show() self.button = button = gtk.Button() button.add(self.play_image) button.set_property('can-default', True) button.set_focus_on_click(False) button.show() hbox.pack_start(button, False) button.set_property('has-default', True) button.connect('clicked', lambda *args: self.play_toggled()) self.adjustment = gtk.Adjustment(0.0, 0.00, 100.0, 0.1, 1.0, 1.0) hscale = gtk.HScale(self.adjustment) hscale.set_digits(2) hscale.set_update_policy(gtk.UPDATE_CONTINUOUS) hscale.connect('button-press-event', self.scale_button_press_cb) hscale.connect('button-release-event', self.scale_button_release_cb) hscale.connect('format-value', self.scale_format_value_cb) hbox.pack_start(hscale) self.hscale = hscale self.videowidget.connect_after('realize', lambda *x: self.play_toggled()) def play_toggled(self): self.button.remove(self.button.child) if self.player.is_playing(): self.player.pause() self.button.add(self.play_image) else: self.player.play() if self.update_id == -1: self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL, self.update_scale_cb) self.button.add(self.pause_image) def scale_format_value_cb(self, scale, value): if self.p_duration == -1: real = 0 else: real = value * self.p_duration / 100 seconds = real / gst.SECOND return "%02d:%02d" % (seconds / 60, seconds % 60) def scale_button_press_cb(self, widget, event): # see seek.c:start_seek gst.debug('starting seek') self.button.set_sensitive(False) self.was_playing = self.player.is_playing() if self.was_playing: self.player.pause() # don't timeout-update position during seek if self.update_id != -1: gobject.source_remove(self.update_id) self.update_id = -1 # make sure we get changed notifies if self.changed_id == -1: self.changed_id = self.hscale.connect('value-changed', self.scale_value_changed_cb) def scale_value_changed_cb(self, scale): # see seek.c:seek_cb real = long(scale.get_value() * self.p_duration / 100) # in ns gst.debug('value changed, perform seek to %r' % real) self.player.seek(real) # allow for a preroll self.player.get_state(timeout=50*gst.MSECOND) # 50 ms def scale_button_release_cb(self, widget, event): # see seek.cstop_seek widget.disconnect(self.changed_id) self.changed_id = -1 self.button.set_sensitive(True) if self.seek_timeout_id != -1: gobject.source_remove(self.seek_timeout_id) self.seek_timeout_id = -1 else: gst.debug('released slider, setting back to playing') if self.was_playing: self.player.play() if self.update_id != -1: self.error('Had a previous update timeout id') else: self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL, self.update_scale_cb) def update_scale_cb(self): self.p_position, self.p_duration = self.player.query_position() if self.p_position != gst.CLOCK_TIME_NONE: value = self.p_position * 100.0 / self.p_duration self.adjustment.set_value(value) return True def main(args): def usage(): sys.stderr.write("usage: %s URI-OF-MEDIA-FILE\n" % args[0]) sys.exit(1) w = PlayerWindow() if len(args) != 2: usage() if not gst.uri_is_valid(args[1]): sys.stderr.write("Error: Invalid URI: %s\n" % args[1]) sys.exit(1) w.load_file(args[1]) w.show_all() gtk.main() if __name__ == '__main__': sys.exit(main(sys.argv))
(Please attach such things as an attachment to the bug in the future :) ) This looks like playbin will still have an audio sink although no buffers will ever pass to them... and so the pipeline simply stalls. Your code works fine with playbin2 btw...
Ok, fixed in old playbin. This will only work after the pipeline is running btw as playbin will select the first stream as the one that is to be played after going to PAUSED anyway. Consider setting fakesink as audiosink or using playbin2. 2008-05-05 Sebastian Dröge <slomo@circular-chaos.org> * gst/playback/gstplaybasebin.c: (set_audio_mute), (set_active_source): * gst/playback/gstplaybasebin.h: * gst/playback/gstplaybin.c: (gst_play_bin_class_init), (playbin_set_audio_mute): Allow setting -1 as current-audio to mute the current audio stream, similar to what is done for subtitles. Fixes bug #342294.