examples/synchronizer.py: Actually appears to work now, will have to try with guadec videos on Monday.

Original commit message from CVS:
2006-08-05  Andy Wingo  <wingo@pobox.com>

* examples/synchronizer.py: Actually appears to work now, will
have to try with guadec videos on Monday.

* examples/remuxer.py (PlayerWindow.create_ui): Disable the
auto-adjusting of cut in and cut out times, it's annoying.
This commit is contained in:
Andy Wingo 2006-08-05 17:15:52 +00:00
parent ee44607e9f
commit e462546b9a
3 changed files with 64 additions and 80 deletions

View file

@ -1,3 +1,11 @@
2006-08-05 Andy Wingo <wingo@pobox.com>
* examples/synchronizer.py: Actually appears to work now, will
have to try with guadec videos on Monday.
* examples/remuxer.py (PlayerWindow.create_ui): Disable the
auto-adjusting of cut in and cut out times, it's annoying.
2006-08-04 Andy Wingo <wingo@pobox.com> 2006-08-04 Andy Wingo <wingo@pobox.com>
* examples/synchronizer.py: New file, a bit of a hack to * examples/synchronizer.py: New file, a bit of a hack to
@ -416,6 +424,14 @@
2006-04-19 Andy Wingo <wingo@pobox.com> 2006-04-19 Andy Wingo <wingo@pobox.com>
<<<<<<< ChangeLog
* gst/gstpad.override (pad_block_callback_marshal)
(_wrap_gst_pad_set_blocked_async): Fix refcounting problems and
indent.
2006-04-19 Andy Wingo <wingo@pobox.com>
=======
* gst/arg-types.py (GstCapsArg.write_const_param) * gst/arg-types.py (GstCapsArg.write_const_param)
(GstCapsArg.write_param): If there is a default value, initialize (GstCapsArg.write_param): If there is a default value, initialize
the py_caps variable to NULL. PyArgs_Parse* doesn't touch c the py_caps variable to NULL. PyArgs_Parse* doesn't touch c
@ -428,6 +444,7 @@
2006-04-19 Andy Wingo <wingo@pobox.com> 2006-04-19 Andy Wingo <wingo@pobox.com>
>>>>>>> 1.447
* examples/remuxer.py: Another code dump. I know it breaks the * examples/remuxer.py: Another code dump. I know it breaks the
freeze but it's just a wee example :) freeze but it's just a wee example :)

View file

@ -675,8 +675,8 @@ class PlayerWindow(gtk.Window):
button.show() button.show()
table.attach(button, 2, 3, 1, 2, gtk.FILL, gtk.FILL) table.attach(button, 2, 3, 1, 2, gtk.FILL, gtk.FILL)
self.cutin.connect('notify::time', lambda *x: self.check_cutout()) #self.cutin.connect('notify::time', lambda *x: self.check_cutout())
self.cutout.connect('notify::time', lambda *x: self.check_cutin()) #self.cutout.connect('notify::time', lambda *x: self.check_cutin())
def do_remux(self): def do_remux(self):
if self.player.is_playing(): if self.player.is_playing():

View file

@ -209,6 +209,18 @@ class SyncPoints(gtk.VBox):
v.connect("clicked", lambda *x: self.set_selected_video_now()) v.connect("clicked", lambda *x: self.set_selected_video_now())
hbox.pack_start(v) hbox.pack_start(v)
def get_sync_points(self):
def get_value(row, i):
return self.model.get_value(row.iter, i)
pairs = [(get_value(row, 1), get_value(row, 0)) for row in self.model]
pairs.sort()
ret = []
maxdiff = 0
for pair in pairs:
maxdiff = max(maxdiff, abs(pair[1] - pair[0]))
ret.extend(pair)
return ret, maxdiff
def changed(self): def changed(self):
print 'Sync times now:' print 'Sync times now:'
for index, row in enumerate(self.model): for index, row in enumerate(self.model):
@ -302,10 +314,10 @@ FAILURE = 2
CANCELLED = 3 CANCELLED = 3
class RemuxProgressDialog(ProgressDialog): class RemuxProgressDialog(ProgressDialog):
def __init__(self, parent, start, stop, fromname, toname): def __init__(self, parent, fromname, toname):
ProgressDialog.__init__(self, ProgressDialog.__init__(self,
"Writing to disk", "Writing to disk",
('Writing the selected segment of <b>%s</b> ' ('Writing the newly synchronized <b>%s</b> '
'to <b>%s</b>. This may take some time.' 'to <b>%s</b>. This may take some time.'
% (fromname, toname)), % (fromname, toname)),
'Starting media pipeline', 'Starting media pipeline',
@ -313,71 +325,34 @@ class RemuxProgressDialog(ProgressDialog):
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CANCEL, CANCELLED, (gtk.STOCK_CANCEL, CANCELLED,
gtk.STOCK_CLOSE, SUCCESS)) gtk.STOCK_CLOSE, SUCCESS))
self.start = start
self.stop = stop
self.update_position(start)
self.set_completed(False) self.set_completed(False)
def update_position(self, pos): def update_position(self, pos, dur):
pos = min(max(pos, self.start), self.stop) remaining = dur - pos
remaining = self.stop - pos
minutes = remaining // (gst.SECOND * 60) minutes = remaining // (gst.SECOND * 60)
seconds = (remaining - minutes * gst.SECOND * 60) // gst.SECOND seconds = (remaining - minutes * gst.SECOND * 60) // gst.SECOND
self.progress.set_text('%d:%02d of video remaining' % (minutes, seconds)) self.progress.set_text('%d:%02d of video remaining' % (minutes, seconds))
self.progress.set_fraction(1.0 - float(remaining) / (self.stop - self.start)) self.progress.set_fraction(1.0 - float(remaining) / dur)
def set_completed(self, completed): def set_completed(self, completed):
self.set_response_sensitive(CANCELLED, not completed) self.set_response_sensitive(CANCELLED, not completed)
self.set_response_sensitive(SUCCESS, completed) self.set_response_sensitive(SUCCESS, completed)
def set_connection_blocked_async_marshalled(pads, proc, *args, **kwargs): class Resynchronizer(gst.Pipeline):
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,))} __gsignals__ = {'done': (gobject.SIGNAL_RUN_LAST, None, (int,))}
def __init__(self, fromuri, touri, start, stop): def __init__(self, fromuri, touri, (syncpoints, maxdiff)):
# HACK: should do Pipeline.__init__, but that doesn't do what we # HACK: should do Pipeline.__init__, but that doesn't do what we
# want; there's a bug open aboooot that # want; there's a bug open aboooot that
self.__gobject_init__() self.__gobject_init__()
assert start >= 0
assert stop > start
self.fromuri = fromuri self.fromuri = fromuri
self.touri = None self.touri = None
self.start_time = start self.syncpoints = syncpoints
self.stop_time = stop self.maxdiff = maxdiff
self.src = self.remuxbin = self.sink = None self.src = self.resyncbin = self.sink = None
self.resolution = UNKNOWN self.resolution = UNKNOWN
self.window = None self.window = None
@ -387,17 +362,17 @@ class Remuxer(gst.Pipeline):
def do_setup_pipeline(self): def do_setup_pipeline(self):
self.src = gst.element_make_from_uri(gst.URI_SRC, self.fromuri) self.src = gst.element_make_from_uri(gst.URI_SRC, self.fromuri)
self.remuxbin = RemuxBin(self.start_time, self.stop_time) self.resyncbin = ResyncBin(self.syncpoints, self.maxdiff)
self.sink = gst.element_make_from_uri(gst.URI_SINK, self.touri) self.sink = gst.element_make_from_uri(gst.URI_SINK, self.touri)
self.resolution = UNKNOWN self.resolution = UNKNOWN
if gobject.signal_lookup('allow-overwrite', self.sink.__class__): if gobject.signal_lookup('allow-overwrite', self.sink.__class__):
self.sink.connect('allow-overwrite', lambda *x: True) self.sink.connect('allow-overwrite', lambda *x: True)
self.add(self.src, self.remuxbin, self.sink) self.add(self.src, self.resyncbin, self.sink)
self.src.link(self.remuxbin) self.src.link(self.resyncbin)
self.remuxbin.link(self.sink) self.resyncbin.link(self.sink)
def do_get_touri(self): def do_get_touri(self):
chooser = gtk.FileChooserDialog('Save as...', chooser = gtk.FileChooserDialog('Save as...',
@ -429,10 +404,11 @@ class Remuxer(gst.Pipeline):
# although i think it's possible) # although i think it's possible)
# HACK: why does self.query_position(..) not give useful # HACK: why does self.query_position(..) not give useful
# answers? # answers?
pad = self.remuxbin.get_pad('src') pad = self.resyncbin.get_pad('src')
pos, duration = pad.query_position(gst.FORMAT_TIME) pos, format = pad.query_position(gst.FORMAT_TIME)
dur, format = pad.query_duration(gst.FORMAT_TIME)
if pos != gst.CLOCK_TIME_NONE: if pos != gst.CLOCK_TIME_NONE:
self.pdialog.update_position(pos) self.pdialog.update_position(pos, duration)
except: except:
# print 'query failed' # print 'query failed'
pass pass
@ -470,7 +446,7 @@ class Remuxer(gst.Pipeline):
if name.startswith('file://'): if name.startswith('file://'):
name = name[7:] name = name[7:]
self.pdialog.set_task('Finished writing %s' % name) self.pdialog.set_task('Finished writing %s' % name)
self.pdialog.update_position(self.stop_time) self.pdialog.update_position(1,1)
self._stop_queries() self._stop_queries()
self.pdialog.set_completed(True) self.pdialog.set_completed(True)
elif message.type == gst.MESSAGE_STATE_CHANGED: elif message.type == gst.MESSAGE_STATE_CHANGED:
@ -480,7 +456,6 @@ class Remuxer(gst.Pipeline):
(gst.STATE_READY, gst.STATE_PAUSED, (gst.STATE_READY, gst.STATE_PAUSED,
gst.STATE_VOID_PENDING)): gst.STATE_VOID_PENDING)):
self.pdialog.set_task('Processing file') self.pdialog.set_task('Processing file')
self.pdialog.update_position(self.start_time)
self._start_queries() self._start_queries()
self.set_state(gst.STATE_PLAYING) self.set_state(gst.STATE_PLAYING)
@ -507,8 +482,7 @@ class Remuxer(gst.Pipeline):
self.window.set_sensitive(False) self.window.set_sensitive(False)
fromname = self.fromuri.split('/')[-1] fromname = self.fromuri.split('/')[-1]
toname = self.touri.split('/')[-1] toname = self.touri.split('/')[-1]
self.pdialog = RemuxProgressDialog(main_window, self.start_time, self.pdialog = RemuxProgressDialog(main_window, fromname, toname)
self.stop_time, fromname, toname)
self.pdialog.show() self.pdialog.show()
self.pdialog.connect('response', lambda w, r: self.response(r)) self.pdialog.connect('response', lambda w, r: self.response(r))
@ -524,8 +498,8 @@ class Remuxer(gst.Pipeline):
self.resolution = CANCELLED self.resolution = CANCELLED
return self.resolution return self.resolution
class RemuxBin(gst.Bin): class ResyncBin(gst.Bin):
def __init__(self, start_time, stop_time): def __init__(self, sync_points, maxdiff):
self.__gobject_init__() self.__gobject_init__()
self.parsefactories = self._find_parsers() self.parsefactories = self._find_parsers()
@ -540,10 +514,9 @@ class RemuxBin(gst.Bin):
self.add_pad(gst.GhostPad('src', self.mux.get_pad('src'))) self.add_pad(gst.GhostPad('src', self.mux.get_pad('src')))
self.demux.connect('pad-added', self._new_demuxed_pad) 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.sync_points = sync_points
self.stop_time = stop_time self.maxdiff = maxdiff
def _find_parsers(self): def _find_parsers(self):
registry = gst.registry_get_default() registry = gst.registry_get_default()
@ -565,7 +538,10 @@ class RemuxBin(gst.Bin):
return return
queue = gst.element_factory_make('queue', 'queue_' + format) queue = gst.element_factory_make('queue', 'queue_' + format)
queue.set_property('max-size-buffers', 1000) queue.set_property('max-size-buffers', 0)
queue.set_property('max-size-bytes', 0)
print self.maxdiff
queue.set_property('max-size-time', int(self.maxdiff * 1.5))
parser = gst.element_factory_make(self.parsefactories[format]) parser = gst.element_factory_make(self.parsefactories[format])
self.add(queue) self.add(queue)
self.add(parser) self.add(parser)
@ -576,18 +552,11 @@ class RemuxBin(gst.Bin):
parser.link(self.mux) parser.link(self.mux)
self.parsers.append(parser) self.parsers.append(parser)
def _do_seek(self): print repr(self.sync_points)
flags = 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_seek)
if 'video' in format:
parser.set_property('synchronization-points',
self.sync_points)
class PlayerWindow(gtk.Window): class PlayerWindow(gtk.Window):
UPDATE_INTERVAL = 500 UPDATE_INTERVAL = 500
@ -706,9 +675,7 @@ class PlayerWindow(gtk.Window):
self.play_toggled() self.play_toggled()
in_uri = self.player.get_location() in_uri = self.player.get_location()
out_uri = in_uri[:-4] + '-remuxed.ogg' out_uri = in_uri[:-4] + '-remuxed.ogg'
raise NotImplementedError() r = Resynchronizer(in_uri, out_uri, self.sync.get_sync_points())
r = Remuxer(in_uri, out_uri,
self.cutin.get_time(), self.cutout.get_time())
r.run(self) r.run(self)
def do_choose_file(self): def do_choose_file(self):