home *** CD-ROM | disk | FTP | other *** search
- #!/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
- gobject.threads_init()
-
- 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")
- self.videowidget = videowidget
-
- 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_state(gst.STATE_NULL)
- self.player.set_property('uri', location)
-
- def get_location(self):
- return self.player.get_property('uri')
-
- 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_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 TimeControl(gtk.HBox):
- # all labels same size
- sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
- __gproperties__ = {'time': (gobject.TYPE_UINT64, 'Time', 'Time',
- # not actually usable: see #335854
- # kept for .notify() usage
- 0L, (1<<63)-1, 0L,
- gobject.PARAM_READABLE)}
-
- def __init__(self, window, label):
- gtk.HBox.__init__(self)
- self.pwindow = window
- self.label = label
- self.create_ui()
-
- def get_property(self, param, pspec):
- if param == 'time':
- return self.get_time()
- else:
- assert param in self.__gproperties__, \
- 'Unknown property: %s' % param
-
- def create_ui(self):
- label = gtk.Label(self.label + ": ")
- label.show()
- a = gtk.Alignment(1.0, 0.5)
- a.add(label)
- a.set_padding(0, 0, 12, 0)
- a.show()
- self.sizegroup.add_widget(a)
- self.pack_start(a, True, False, 0)
-
- self.minutes = minutes = gtk.Entry(5)
- minutes.set_width_chars(5)
- minutes.set_alignment(1.0)
- minutes.connect('changed', lambda *x: self.notify('time'))
- minutes.connect_after('activate', lambda *x: self.activated())
- label2 = gtk.Label(":")
- self.seconds = seconds = gtk.Entry(2)
- seconds.set_width_chars(2)
- seconds.set_alignment(1.0)
- seconds.connect('changed', lambda *x: self.notify('time'))
- seconds.connect_after('activate', lambda *x: self.activated())
- label3 = gtk.Label(".")
- self.milliseconds = milliseconds = gtk.Entry(3)
- milliseconds.set_width_chars(3)
- milliseconds.set_alignment(0.0)
- milliseconds.connect('changed', lambda *x: self.notify('time'))
- milliseconds.connect_after('activate', lambda *x: self.activated())
- set = gtk.Button('Set')
- goto = gtk.Button('Go')
- goto.set_property('image',
- gtk.image_new_from_stock(gtk.STOCK_JUMP_TO,
- gtk.ICON_SIZE_BUTTON))
- for w in minutes, label2, seconds, label3, milliseconds:
- w.show()
- self.pack_start(w, False)
- set.show()
- self.pack_start(set, False, False, 6)
- goto.show()
- self.pack_start(goto, False, False, 0)
- set.connect('clicked', lambda *x: self.set_now())
- goto.connect('clicked', lambda *x: self.activated())
- pad = gtk.Label("")
- pad.show()
- self.pack_start(pad, True, False, 0)
-
- def get_time(self):
- time = 0
- for w, multiplier in ((self.minutes, gst.SECOND*60),
- (self.seconds, gst.SECOND),
- (self.milliseconds, gst.MSECOND)):
- text = w.get_text()
- try:
- val = int(text)
- except ValueError:
- val = 0
- w.set_text(val and str(val) or '0')
- time += val * multiplier
- return time
-
- def set_time(self, time):
- if time == gst.CLOCK_TIME_NONE:
- print "Can't set '%s' (invalid time)" % self.label
- return
- self.freeze_notify()
- for w, multiplier in ((self.minutes, gst.SECOND*60),
- (self.seconds, gst.SECOND),
- (self.milliseconds, gst.MSECOND)):
- val = time // multiplier
- w.set_text(str(val))
- time -= val * multiplier
- self.thaw_notify()
-
- def set_now(self):
- time, dur = self.pwindow.player.query_position()
- self.set_time(time)
-
- def activated(self):
- time = self.get_time()
- if self.pwindow.player.is_playing():
- self.pwindow.play_toggled()
- self.pwindow.player.seek(time)
- self.pwindow.player.get_state(timeout=gst.MSECOND * 200)
-
- class ProgressDialog(gtk.Dialog):
- def __init__(self, title, description, task, parent, flags, buttons):
- gtk.Dialog.__init__(self, title, parent, flags, buttons)
- self._create_ui(title, description, task)
-
- def _create_ui(self, title, description, task):
- self.set_border_width(6)
- self.set_resizable(False)
- self.set_has_separator(False)
-
- vbox = gtk.VBox()
- vbox.set_border_width(6)
- vbox.show()
- self.vbox.pack_start(vbox, False)
-
- label = gtk.Label('<big><b>%s</b></big>' % title)
- label.set_use_markup(True)
- label.set_alignment(0.0, 0.0)
- label.show()
- vbox.pack_start(label, False)
-
- label = gtk.Label(description)
- label.set_use_markup(True)
- label.set_alignment(0.0, 0.0)
- label.set_line_wrap(True)
- label.set_padding(0, 12)
- label.show()
- vbox.pack_start(label, False)
-
- self.progress = progress = gtk.ProgressBar()
- progress.show()
- vbox.pack_start(progress, False)
-
- self.progresstext = label = gtk.Label('')
- label.set_line_wrap(True)
- label.set_use_markup(True)
- label.set_alignment(0.0, 0.0)
- label.show()
- vbox.pack_start(label)
- self.set_task(task)
-
- def set_task(self, task):
- self.progresstext.set_markup('<i>%s</i>' % task)
-
- UNKNOWN = 0
- SUCCESS = 1
- FAILURE = 2
- CANCELLED = 3
-
- class RemuxProgressDialog(ProgressDialog):
- def __init__(self, parent, start, stop, fromname, toname):
- ProgressDialog.__init__(self,
- "Writing to disk",
- ('Writing the selected segment of <b>%s</b> '
- 'to <b>%s</b>. This may take some time.'
- % (fromname, toname)),
- 'Starting media pipeline',
- parent,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_CANCEL, CANCELLED,
- gtk.STOCK_CLOSE, SUCCESS))
- self.start = start
- self.stop = stop
- self.update_position(start)
- self.set_completed(False)
-
- def update_position(self, pos):
- pos = min(max(pos, self.start), self.stop)
- remaining = self.stop - pos
- minutes = remaining // (gst.SECOND * 60)
- seconds = (remaining - minutes * gst.SECOND * 60) // gst.SECOND
- self.progress.set_text('%d:%02d of video remaining' % (minutes, seconds))
- self.progress.set_fraction(1.0 - float(remaining) / (self.stop - self.start))
-
- def set_completed(self, completed):
- self.set_response_sensitive(CANCELLED, not completed)
- self.set_response_sensitive(SUCCESS, completed)
-
- def set_connection_blocked_async_marshalled(pads, proc, *args, **kwargs):
- def clear_list(l):
- while l:
- l.pop()
-
- to_block = list(pads)
- to_relink = [(x, x.get_peer()) for x in pads]
-
- def on_pad_blocked_sync(pad, is_blocked):
- if pad not in to_block:
- # can happen after the seek and before unblocking -- racy,
- # but no prob, bob.
- return
- to_block.remove(pad)
- if not to_block:
- # marshal to main thread
- gobject.idle_add(on_pads_blocked)
-
- def on_pads_blocked():
- for src, sink in to_relink:
- src.link(sink)
- proc(*args, **kwargs)
- for src, sink in to_relink:
- src.set_blocked_async(False, lambda *x: None)
- clear_list(to_relink)
-
- for src, sink in to_relink:
- src.unlink(sink)
- src.set_blocked_async(True, on_pad_blocked_sync)
-
- class Remuxer(gst.Pipeline):
-
- __gsignals__ = {'done': (gobject.SIGNAL_RUN_LAST, None, (int,))}
-
- def __init__(self, fromuri, touri, start, stop):
- # HACK: should do Pipeline.__init__, but that doesn't do what we
- # want; there's a bug open aboooot that
- self.__gobject_init__()
-
- assert start >= 0
- assert stop > start
-
- self.fromuri = fromuri
- self.touri = None
- self.start_time = start
- self.stop_time = stop
-
- self.src = self.remuxbin = self.sink = None
- self.resolution = UNKNOWN
-
- self.window = None
- self.pdialog = None
-
- self._query_id = -1
-
- def do_setup_pipeline(self):
- self.src = gst.element_make_from_uri(gst.URI_SRC, self.fromuri)
- self.remuxbin = RemuxBin(self.start_time, self.stop_time)
- self.sink = gst.element_make_from_uri(gst.URI_SINK, self.touri)
- self.resolution = UNKNOWN
-
- if gobject.signal_lookup('allow-overwrite', self.sink.__class__):
- self.sink.connect('allow-overwrite', lambda *x: True)
-
- self.add(self.src, self.remuxbin, self.sink)
-
- self.src.link(self.remuxbin)
- self.remuxbin.link(self.sink)
-
- def do_get_touri(self):
- chooser = gtk.FileChooserDialog('Save as...',
- self.window,
- action=gtk.FILE_CHOOSER_ACTION_SAVE,
- buttons=(gtk.STOCK_CANCEL,
- CANCELLED,
- gtk.STOCK_SAVE,
- SUCCESS))
- chooser.set_uri(self.fromuri) # to select the folder
- chooser.unselect_all()
- chooser.set_do_overwrite_confirmation(True)
- name = self.fromuri.split('/')[-1][:-4] + '-remuxed.ogg'
- chooser.set_current_name(name)
- resp = chooser.run()
- uri = chooser.get_uri()
- chooser.destroy()
-
- if resp == SUCCESS:
- return uri
- else:
- return None
-
- def _start_queries(self):
- def do_query():
- try:
- # HACK: self.remuxbin.query() should do the same
- # (requires implementing a vmethod, dunno how to do that
- # although i think it's possible)
- # HACK: why does self.query_position(..) not give useful
- # answers?
- pad = self.remuxbin.get_pad('src')
- pos, duration = pad.query_position(gst.FORMAT_TIME)
- if pos != gst.CLOCK_TIME_NONE:
- self.pdialog.update_position(pos)
- except:
- # print 'query failed'
- pass
- return True
- if self._query_id == -1:
- self._query_id = gobject.timeout_add(100, # 10 Hz
- do_query)
-
- def _stop_queries(self):
- if self._query_id != -1:
- gobject.source_remove(self._query_id)
- self._query_id = -1
-
- def _bus_watch(self, bus, message):
- if message.type == gst.MESSAGE_ERROR:
- print 'error', message
- self._stop_queries()
- m = gtk.MessageDialog(self.window,
- gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_ERROR,
- gtk.BUTTONS_CLOSE,
- "Error processing file")
- gerror, debug = message.parse_error()
- txt = ('There was an error processing your file: %s\n\n'
- 'Debug information:\n%s' % (gerror, debug))
- m.format_secondary_text(txt)
- m.run()
- m.destroy()
- self.response(FAILURE)
- elif message.type == gst.MESSAGE_WARNING:
- print 'warning', message
- elif message.type == gst.MESSAGE_SEGMENT_DONE:
- # print 'eos, woot', message.src
- name = self.touri
- if name.startswith('file://'):
- name = name[7:]
- self.pdialog.set_task('Finished writing %s' % name)
- self.pdialog.update_position(self.stop_time)
- self._stop_queries()
- self.pdialog.set_completed(True)
- elif message.type == gst.MESSAGE_STATE_CHANGED:
- if message.src == self:
- old, new, pending = message.parse_state_changed()
- if ((old, new, pending) ==
- (gst.STATE_READY, gst.STATE_PAUSED,
- gst.STATE_VOID_PENDING)):
- self.pdialog.set_task('Processing file')
- self.pdialog.update_position(self.start_time)
- self._start_queries()
- self.set_state(gst.STATE_PLAYING)
-
- def response(self, response):
- assert self.resolution == UNKNOWN
- self.resolution = response
- self.set_state(gst.STATE_NULL)
- self.pdialog.destroy()
- self.pdialog = None
- self.window.set_sensitive(True)
- self.emit('done', response)
-
- def start(self, main_window):
- self.window = main_window
- self.touri = self.do_get_touri()
- if not self.touri:
- return False
- self.do_setup_pipeline()
- bus = self.get_bus()
- bus.add_signal_watch()
- bus.connect('message', self._bus_watch)
- if self.window:
- # can be None if we are debugging...
- self.window.set_sensitive(False)
- fromname = self.fromuri.split('/')[-1]
- toname = self.touri.split('/')[-1]
- self.pdialog = RemuxProgressDialog(main_window, self.start_time,
- self.stop_time, fromname, toname)
- self.pdialog.show()
- self.pdialog.connect('response', lambda w, r: self.response(r))
-
- self.set_state(gst.STATE_PAUSED)
- return True
-
- def run(self, main_window):
- if self.start(main_window):
- loop = gobject.MainLoop()
- self.connect('done', lambda *x: gobject.idle_add(loop.quit))
- loop.run()
- else:
- self.resolution = CANCELLED
- return self.resolution
-
- class RemuxBin(gst.Bin):
- def __init__(self, start_time, stop_time):
- self.__gobject_init__()
-
- self.parsefactories = self._find_parsers()
- self.parsers = []
-
- self.demux = gst.element_factory_make('oggdemux')
- self.mux = gst.element_factory_make('oggmux')
-
- self.add(self.demux, self.mux)
-
- self.add_pad(gst.GhostPad('sink', self.demux.get_pad('sink')))
- self.add_pad(gst.GhostPad('src', self.mux.get_pad('src')))
-
- self.demux.connect('pad-added', self._new_demuxed_pad)
- self.demux.connect('no-more-pads', self._no_more_pads)
-
- self.start_time = start_time
- self.stop_time = stop_time
-
- def _find_parsers(self):
- registry = gst.registry_get_default()
- ret = {}
- for f in registry.get_feature_list(gst.ElementFactory):
- if f.get_klass().find('Parser') >= 0:
- for t in f.get_static_pad_templates():
- if t.direction == gst.PAD_SINK:
- for s in t.get_caps():
- ret[s.get_name()] = f.get_name()
- break
- return ret
-
- def _new_demuxed_pad(self, element, pad):
- format = pad.get_caps()[0].get_name()
-
- if format not in self.parsefactories:
- self.async_error("Unsupported media type: %s", format)
- return
-
- queue = gst.element_factory_make('queue', 'queue_' + format)
- parser = gst.element_factory_make(self.parsefactories[format])
- self.add(queue)
- self.add(parser)
- queue.set_state(gst.STATE_PAUSED)
- parser.set_state(gst.STATE_PAUSED)
- pad.link(queue.get_compatible_pad(pad))
- queue.link(parser)
- parser.link(self.mux)
- self.parsers.append(parser)
-
- def _do_segment_seek(self):
- flags = gst.SEEK_FLAG_SEGMENT | gst.SEEK_FLAG_FLUSH
- # HACK: self.seek should work, should try that at some point
- return self.demux.seek(1.0, gst.FORMAT_TIME, flags,
- gst.SEEK_TYPE_SET, self.start_time,
- gst.SEEK_TYPE_SET, self.stop_time)
-
- def _no_more_pads(self, element):
- pads = [x.get_pad('src') for x in self.parsers]
- set_connection_blocked_async_marshalled(pads,
- self._do_segment_seek)
-
-
- class PlayerWindow(gtk.Window):
- UPDATE_INTERVAL = 500
- def __init__(self):
- gtk.Window.__init__(self)
- self.set_default_size(600, 425)
-
- 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):
- filename = location.split('/')[-1]
- self.set_title('%s munger' % filename)
- self.player.set_location(location)
- if self.videowidget.flags() & gtk.REALIZED:
- self.play_toggled()
- else:
- self.videowidget.connect_after('realize',
- lambda *x: self.play_toggled())
-
- def create_ui(self):
- vbox = gtk.VBox()
- vbox.show()
- self.add(vbox)
-
- self.videowidget = VideoWidget()
- self.videowidget.show()
- vbox.pack_start(self.videowidget)
-
- hbox = gtk.HBox()
- hbox.show()
- vbox.pack_start(hbox, fill=False, expand=False)
-
- 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)
- hscale.show()
- self.hscale = hscale
-
- table = gtk.Table(2,3)
- table.show()
- vbox.pack_start(table, fill=False, expand=False, padding=6)
-
- self.button = button = gtk.Button(stock=gtk.STOCK_MEDIA_PLAY)
- button.set_property('can-default', True)
- button.set_focus_on_click(False)
- button.show()
-
- # problem: play and paused are of different widths and cause the
- # window to re-layout
- # "solution": add more buttons to a vbox so that the horizontal
- # width is enough
- bvbox = gtk.VBox()
- bvbox.add(button)
- bvbox.add(gtk.Button(stock=gtk.STOCK_MEDIA_PLAY))
- bvbox.add(gtk.Button(stock=gtk.STOCK_MEDIA_PAUSE))
- sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
- for kid in bvbox.get_children():
- sizegroup.add_widget(kid)
- bvbox.show()
- table.attach(bvbox, 0, 1, 0, 2, gtk.FILL, gtk.FILL)
-
- # can't set this property before the button has a window
- button.set_property('has-default', True)
- button.connect('clicked', lambda *args: self.play_toggled())
-
- self.cutin = cut = TimeControl(self, "Cut in time")
- cut.show()
- table.attach(cut, 1, 2, 0, 1, gtk.EXPAND, 0, 12)
-
- self.cutout = cut = TimeControl(self, "Cut out time")
- cut.show()
- table.attach(cut, 1, 2, 1, 2, gtk.EXPAND, 0, 12)
-
- button = gtk.Button("_Open other movie...")
- button.show()
- button.connect('clicked', lambda *x: self.do_choose_file())
- table.attach(button, 2, 3, 0, 1, gtk.FILL, gtk.FILL)
-
- button = gtk.Button("_Write to disk")
- button.set_property('image',
- gtk.image_new_from_stock(gtk.STOCK_SAVE_AS,
- gtk.ICON_SIZE_BUTTON))
- button.connect('clicked', lambda *x: self.do_remux())
- button.show()
- table.attach(button, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
-
- self.cutin.connect('notify::time', lambda *x: self.check_cutout())
- self.cutout.connect('notify::time', lambda *x: self.check_cutin())
-
- def do_remux(self):
- if self.player.is_playing():
- self.play_toggled()
- in_uri = self.player.get_location()
- out_uri = in_uri[:-4] + '-remuxed.ogg'
- r = Remuxer(in_uri, out_uri,
- self.cutin.get_time(), self.cutout.get_time())
- r.run(self)
-
- def do_choose_file(self):
- if self.player.is_playing():
- self.play_toggled()
- chooser = gtk.FileChooserDialog('Choose a movie to cut cut cut',
- self,
- buttons=(gtk.STOCK_CANCEL,
- CANCELLED,
- gtk.STOCK_OPEN,
- SUCCESS))
- chooser.set_local_only(False)
- chooser.set_select_multiple(False)
- f = gtk.FileFilter()
- f.set_name("All files")
- f.add_pattern("*")
- chooser.add_filter(f)
- f = gtk.FileFilter()
- f.set_name("Ogg files")
- f.add_pattern("*.ogg") # as long as this is the only thing we
- # support...
- chooser.add_filter(f)
- chooser.set_filter(f)
-
- prev = self.player.get_location()
- if prev:
- chooser.set_uri(prev)
-
- resp = chooser.run()
- uri = chooser.get_uri()
- chooser.destroy()
-
- if resp == SUCCESS:
- self.load_file(uri)
- return True
- else:
- return False
-
- def check_cutout(self):
- if self.cutout.get_time() <= self.cutin.get_time():
- pos, dur = self.player.query_position()
- self.cutout.set_time(dur)
-
- def check_cutin(self):
- if self.cutin.get_time() >= self.cutout.get_time():
- self.cutin.set_time(0)
-
- def play_toggled(self):
- if self.player.is_playing():
- self.player.pause()
- self.button.set_label(gtk.STOCK_MEDIA_PLAY)
- else:
- self.player.play()
- if self.update_id == -1:
- self.update_id = gobject.timeout_add(self.UPDATE_INTERVAL,
- self.update_scale_cb)
- self.button.set_label(gtk.STOCK_MEDIA_PAUSE)
-
- 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):
- had_duration = self.p_duration != gst.CLOCK_TIME_NONE
- 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)
- if not had_duration:
- self.cutin.set_time(0)
- return True
-
- def main(args):
- def usage():
- sys.stderr.write("usage: %s [URI-OF-MEDIA-FILE]\n" % args[0])
- return 1
-
- w = PlayerWindow()
- w.show()
-
- if len(args) == 1:
- if not w.do_choose_file():
- return 1
- elif len(args) == 2:
- if not gst.uri_is_valid(args[1]):
- sys.stderr.write("Error: Invalid URI: %s\n" % args[1])
- return 1
- w.load_file(args[1])
- else:
- return usage()
-
- gtk.main()
-
- if __name__ == '__main__':
- sys.exit(main(sys.argv))
-