[pitivi/ges] Move seek related code to pipeline.py
- From: Thibault Saunier <tsaunier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi/ges] Move seek related code to pipeline.py
- Date: Sat, 5 May 2012 20:18:39 +0000 (UTC)
commit c7fc321279cd73dbfa1ae5253995fa69afa23cc6
Author: Thibault Saunier <thibault saunier collabora com>
Date: Tue May 1 10:04:42 2012 -0400
Move seek related code to pipeline.py
Remove the playback file as it doesn't make sense to have these utils anymore
Do the flushing happen right away, ie do it in the pipeline directly not
through the seeker
pitivi/clipproperties.py | 12 +--
pitivi/effects.py | 10 ++--
pitivi/mainwindow.py | 48 +-------------
pitivi/project.py | 3 +-
pitivi/render.py | 9 +--
pitivi/timeline/ruler.py | 2 +-
pitivi/timeline/timeline.py | 17 +++--
pitivi/utils/Makefile.am | 1 -
pitivi/utils/pipeline.py | 156 +++++++++++++++++++++++++++++++++++++++++--
pitivi/utils/playback.py | 145 ----------------------------------------
pitivi/viewer.py | 6 +-
11 files changed, 181 insertions(+), 228 deletions(-)
---
diff --git a/pitivi/clipproperties.py b/pitivi/clipproperties.py
index ef8f42f..26b4b89 100644
--- a/pitivi/clipproperties.py
+++ b/pitivi/clipproperties.py
@@ -34,7 +34,6 @@ from pitivi.configure import get_ui_dir
from pitivi.dialogs.depsmanager import DepsManager
-from pitivi.utils.playback import Seeker
from pitivi.utils.ui import EFFECT_TUPLE
from pitivi.utils.loggable import Loggable
from pitivi.utils.ui import PADDING, SPACING
@@ -92,7 +91,7 @@ class ClipProperties(gtk.ScrolledWindow, Loggable):
vbox.set_homogeneous(False)
vp.add(vbox)
- self.effect_properties_handling = EffectsPropertiesManager(instance.action_log)
+ self.effect_properties_handling = EffectsPropertiesManager(instance)
self.effect_expander = EffectProperties(instance,
self.effect_properties_handling, self)
@@ -176,8 +175,6 @@ class EffectProperties(gtk.Expander, gtk.HBox):
self._info_bar = None
self._config_ui_h_pos = None
self._timeline = None
- # We use the seeker to flush the pipeline when needed
- self._seeker = Seeker()
self._vcontent = gtk.VPaned()
self.add(self._vcontent)
@@ -370,7 +367,7 @@ class EffectProperties(gtk.Expander, gtk.HBox):
track.add_object(effect)
self._updateAll()
self.app.action_log.commit()
- self._seeker.flush()
+ self.app.current.pipeline.flushSeek()
break
@@ -530,7 +527,6 @@ class TransformationProperties(gtk.Expander):
self.default_values = {}
self.set_label(_("Transformation"))
self.set_sensitive(False)
- self._seeker = Seeker()
if not "Frei0r" in soft_deps:
self.builder = gtk.Builder()
@@ -620,7 +616,7 @@ class TransformationProperties(gtk.Expander):
box.update_from_effect(self.effect)
def _flushPipeLineCb(self, widget):
- self._seeker.flush()
+ self.app.current.pipeline.flushSeek()
def _findEffect(self, name):
for track_effect in self._current_tl_obj.get_track_objects():
@@ -664,7 +660,7 @@ class TransformationProperties(gtk.Expander):
if self._current_tl_obj:
self._current_tl_obj = None
self.zoom_scale.set_value(1.0)
- self._seeker.flush()
+ self.app.current.pipeline.flushSeek()
self.effect = None
self.set_sensitive(False)
self._updateBoxVisibility()
diff --git a/pitivi/effects.py b/pitivi/effects.py
index d2f5cb0..57dfd7b 100644
--- a/pitivi/effects.py
+++ b/pitivi/effects.py
@@ -52,7 +52,6 @@ from pitivi.settings import GlobalSettings
import pitivi.utils.ui as dnd
from pitivi.utils.loggable import Loggable
from pitivi.utils.ui import SPACING
-from pitivi.utils.playback import Seeker
from pitivi.utils.widgets import GstElementSettingsWidget, FractionWidget
@@ -770,12 +769,12 @@ PROPS_TO_IGNORE = ['name', 'qos', 'silent', 'message']
class EffectsPropertiesManager:
- def __init__(self, action_log):
+ def __init__(self, instance):
self.cache_dict = {}
self._current_effect_setting_ui = None
self._current_element_values = {}
- self.action_log = action_log
- self._seeker = Seeker()
+ self.action_log = instance.action_log
+ self.app = instance
def getEffectConfigurationUI(self, effect):
"""
@@ -846,5 +845,6 @@ class EffectsPropertiesManager:
self.action_log.begin("Effect property change")
self._current_effect_setting_ui.element.set_child_property(prop.name, value)
self.action_log.commit()
- self._seeker.flush()
+
+ self.app.current.pipeline.flushSeek()
self._current_element_values[prop.name] = value
diff --git a/pitivi/mainwindow.py b/pitivi/mainwindow.py
index af4b817..e138290 100644
--- a/pitivi/mainwindow.py
+++ b/pitivi/mainwindow.py
@@ -36,7 +36,7 @@ from gettext import gettext as _
from gtk import RecentManager
from hashlib import md5
-from pitivi.utils.pipeline import PipelineError
+from pitivi.utils.pipeline import Seeker
from pitivi.utils.loggable import Loggable
from pitivi.utils.misc import in_devel
from pitivi.settings import GlobalSettings
@@ -752,10 +752,7 @@ class PitiviMainWindow(gtk.Window, Loggable):
if self.app.current.timeline.props.duration != 0:
self.render_button.set_sensitive(True)
- self._seeker = self.app.current.seeker
- self._seeker.connect("seek", self._timelineSeekCb)
- self._seeker.connect("seek-relative", self._timelineSeekRelativeCb)
- self._seeker.connect("flush", self._timelineSeekFlushCb)
+ self._seeker = Seeker()
# preliminary seek to ensure the project pipeline is configured
self._seeker.seek(0)
@@ -1303,47 +1300,6 @@ class PitiviMainWindow(gtk.Window, Loggable):
previewer.play()
- def _timelineSeekRelativeCb(self, unused_seeker, time):
- try:
- position = self.app.current.pipeline.getPosition()
- position += time
-
- self.app.current.pipeline.simple_seek(position)
- self._seeker.setPosition(position)
-
- except PipelineError:
- self.error("seek failed %s %s %s", gst.TIME_ARGS(position), format, e)
-
- def _timelineSeekFlushCb(self, unused_seeker):
- try:
- position = self.app.current.pipeline.getPosition()
- self.app.current.pipeline.simple_seek(position)
- self._seeker.setPosition(position)
-
- except PipelineError:
- self.error("seek failed %s %s %s", gst.TIME_ARGS(position), format, e)
-
- def _timelineSeekCb(self, ruler, position, format):
- """
- The app's main seek method used when the user seeks manually.
-
- We clamp the seeker position so that it cannot go past 0 or the
- end of the timeline.
- """
- # FIXME: ideally gstreamer should allow seeking to the exact end...
- # but since it doesn't, we seek one nanosecond before the end.
- end = self.app.current.timeline.props.duration - 1
- # CLAMP (0, position, self.app.current.timeline.props.duration)
- position = sorted((0, position, end))[1]
-
- try:
- self.app.current.pipeline.simple_seek(position)
- self._seeker.setPosition(position)
- except PipelineError:
- self.warning("Could not seek to %s", gst.TIME_ARGS(position))
- # Ensure that the viewer UI is updated when seeking while paused
- self.viewer.positionCheck()
-
def updateTitle(self):
name = touched = ""
if self.app.current:
diff --git a/pitivi/project.py b/pitivi/project.py
index c32c7d9..883f6f2 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -42,7 +42,7 @@ from pitivi.undo.undo import UndoableAction
from pitivi.configure import get_ui_dir
from pitivi.utils.misc import quote_uri, path_from_uri
-from pitivi.utils.playback import Seeker
+from pitivi.utils.pipeline import Seeker
from pitivi.utils.loggable import Loggable
from pitivi.utils.signal import Signallable
from pitivi.utils.pipeline import Pipeline
@@ -515,6 +515,7 @@ class Project(Signallable, Loggable):
uri = property(getUri, setUri)
def release(self):
+ self.pipeline.release()
self.pipeline = None
self.timeline = None
diff --git a/pitivi/render.py b/pitivi/render.py
index b9fa4db..43a4e55 100644
--- a/pitivi/render.py
+++ b/pitivi/render.py
@@ -29,14 +29,12 @@ import gtk
import gst
import ges
import time
-import gst
import pitivi.utils.loggable as log
from gettext import gettext as _
from pitivi import configure
-from pitivi.utils.playback import Seeker
from pitivi.utils.signal import Signallable
from pitivi.utils.loggable import Loggable
@@ -384,7 +382,6 @@ class RenderDialog(Loggable):
self.app = app
self.project = project
self.system = app.system
- self._seeker = Seeker()
if pipeline != None:
self._pipeline = pipeline
else:
@@ -890,7 +887,7 @@ class RenderDialog(Loggable):
for obj, id in self._gstSigId.iteritems():
obj.disconnect(id)
self._gstSigId = {}
- self._seeker.disconnect_by_function(self._updatePositionCb)
+ self.app.current.pipeline.disconnect_by_function(self._updatePositionCb)
def _updateProjectSettings(self):
"""Updates the settings of the project if the render settings changed.
@@ -960,7 +957,7 @@ class RenderDialog(Loggable):
bus = self._pipeline.get_bus()
bus.add_signal_watch()
self._gstSigId[bus] = bus.connect('message', self._busMessageCb)
- self._seeker.connect("position-changed", self._updatePositionCb)
+ self.app.current.connect("position", self._updatePositionCb)
def _closeButtonClickedCb(self, unused_button):
self.debug("Render dialog's Close button clicked")
@@ -988,7 +985,7 @@ class RenderDialog(Loggable):
else:
self.system.uninhibitSleep(RenderDialog.INHIBIT_REASON)
- def _updatePositionCb(self, seeker, position):
+ def _updatePositionCb(self, pipeline, position):
if self.progress:
text = None
timediff = time.time() - self.timestarted
diff --git a/pitivi/timeline/ruler.py b/pitivi/timeline/ruler.py
index 8ba2002..1c20033 100644
--- a/pitivi/timeline/ruler.py
+++ b/pitivi/timeline/ruler.py
@@ -28,7 +28,7 @@ import gtk
import gst
import cairo
-from pitivi.utils.playback import Seeker
+from pitivi.utils.pipeline import Seeker
from pitivi.utils.timeline import Zoomable
from pitivi.utils.loggable import Loggable
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index 458a4b4..5f2d145 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -329,13 +329,13 @@ class TimelineCanvas(goocanvas.Canvas, Zoomable, Loggable):
return True
def _selectionEnd(self, item, target, event):
- seeker = self.app.current.seeker
self.pointer_ungrab(self.get_root_item(), event.time)
self._selecting = False
self._marquee.props.visibility = goocanvas.ITEM_INVISIBLE
if not self._got_motion_notify:
self._timeline.selection.setSelection([], 0)
- seeker.seek(Zoomable.pixelToNs(event.x))
+ #FIXME Do we need to seek here?
+ #self._pipeline.seek(Zoomable.pixelToNs(event.x))
else:
self._got_motion_notify = False
mode = 0
@@ -799,7 +799,7 @@ class Timeline(gtk.Table, Loggable, Zoomable):
elif mod & gtk.gdk.CONTROL_MASK:
self._seeker.seek(rtime + 1)
else:
- self.seeker.seekRelative(long(self.rate * gst.SECOND))
+ self._seeker.seekRelative(long(self.rate * gst.SECOND))
finally:
return True
@@ -880,7 +880,7 @@ class Timeline(gtk.Table, Loggable, Zoomable):
track.add_object(effect)
self.app.action_log.commit()
self._factories = None
- self.seeker.seek(self._position)
+ self._seeker.seek(self._position)
context.drop_finish(True, timestamp)
self.timeline.selection.setSelection(timeline_objs, SELECT)
@@ -1184,7 +1184,8 @@ class Timeline(gtk.Table, Loggable, Zoomable):
self.debug("Setting project %s", project)
if self._project:
self._project.disconnect_by_function(self._settingsChangedCb)
- self.seeker.disconnect_by_function(self.positionChangedCb)
+ self._pipeline.disconnect_by_func(self.positionChangedCb)
+ self.pipeline = None
self._project = project
if self._project:
@@ -1192,8 +1193,10 @@ class Timeline(gtk.Table, Loggable, Zoomable):
self.ruler.setProjectFrameRate(self._project.getSettings().videorate)
self.ruler.zoomChanged()
self._settingsChangedCb(self._project, None, self._project.getSettings())
- self.seeker = self._project.seeker
- self.seeker.connect("position-changed", self.positionChangedCb)
+
+ self._seeker = self._project.seeker
+ self._pipeline = self._project.pipeline
+ self._pipeline.connect("position", self.positionChangedCb)
self._project.connect("settings-changed", self._settingsChangedCb)
def _settingsChangedCb(self, project, old, new):
diff --git a/pitivi/utils/Makefile.am b/pitivi/utils/Makefile.am
index 32e7c8f..1bdb05c 100644
--- a/pitivi/utils/Makefile.am
+++ b/pitivi/utils/Makefile.am
@@ -5,7 +5,6 @@ utils_PYTHON = \
extract.py \
timeline.py \
loggable.py \
- playback.py \
pipeline.py \
signal.py \
ui.py \
diff --git a/pitivi/utils/pipeline.py b/pitivi/utils/pipeline.py
index d7c4922..8b3e7c2 100644
--- a/pitivi/utils/pipeline.py
+++ b/pitivi/utils/pipeline.py
@@ -27,6 +27,8 @@
High-level pipelines
"""
from pitivi.utils.loggable import Loggable
+from pitivi.utils.signal import Signallable
+
import gobject
import gst
import ges
@@ -37,6 +39,118 @@ class PipelineError(Exception):
pass
+class Seeker(Signallable, Loggable):
+ """
+ The Seeker is a singleton helper class to do various seeking
+ operations in the pipeline.
+ """
+ _instance = None
+ __signals__ = {
+ 'seek': ['position', 'format'],
+ 'flush': [],
+ 'seek-relative': ['time'],
+ }
+
+ def __new__(cls, *args, **kwargs):
+ """
+ Override the new method to return the singleton instance if available.
+ Otherwise, create one.
+ """
+ if not cls._instance:
+ cls._instance = super(Seeker, cls).__new__(cls, *args, **kwargs)
+ return cls._instance
+
+ def __init__(self, timeout=80):
+ """
+ @param timeout (optional): the amount of miliseconds for a seek attempt
+ """
+ Signallable.__init__(self)
+ Loggable.__init__(self)
+
+ self.timeout = timeout
+ self.pending_seek_id = None
+ self.position = None
+ self.format = None
+ self._time = None
+
+ def seek(self, position, format=gst.FORMAT_TIME, on_idle=False):
+ self.format = format
+ self.position = position
+
+ if self.pending_seek_id is None:
+ if on_idle:
+ gobject.idle_add(self._seekTimeoutCb)
+ else:
+ self._seekTimeoutCb()
+ self.pending_seek_id = self._scheduleSeek(self.timeout,
+ self._seekTimeoutCb)
+
+ def seekRelative(self, time, on_idle=False):
+ if self.pending_seek_id is None:
+ self._time = time
+ if on_idle:
+ gobject.idle_add(self._seekRelativeTimeoutCb)
+ else:
+ self._seekTimeoutCb()
+ self.pending_seek_id = self._scheduleSeek(self.timeout,
+ self._seekTimeoutCb, True)
+
+ def flush(self):
+ try:
+ self.emit('flush')
+ except:
+ self.error("Error while flushing")
+
+ def _scheduleSeek(self, timeout, callback, relative=False):
+ return gobject.timeout_add(timeout, callback, relative)
+
+ def _seekTimeoutCb(self, relative=False):
+ self.pending_seek_id = None
+ if relative:
+ try:
+ self.emit('seek-relative', self._time)
+ except:
+ self.error("Error while seeking %s relative",
+ self._time)
+ # if an exception happened while seeking, properly
+ # reset ourselves
+ return False
+
+ self._time = None
+ elif self.position != None and self.format != None:
+ position, self.position = self.position, None
+ format, self.format = self.format, None
+ try:
+ self.emit('seek', position, format)
+ except:
+ self.error("Error while seeking to position:%s format: %r",
+ gst.TIME_ARGS(position), format)
+ # if an exception happened while seeking, properly
+ # reset ourselves
+ return False
+ return False
+
+ def setPosition(self, position):
+ self.emit("position-changed", position)
+
+
+#-----------------------------------------------------------------------------#
+# Pipeline utils #
+def togglePlayback(pipeline):
+ if int(pipeline.get_state()[1]) == int(gst.STATE_PLAYING):
+ state = gst.STATE_PAUSED
+ else:
+ state = gst.STATE_PLAYING
+
+ res = pipeline.set_state(state)
+ if res == gst.STATE_CHANGE_FAILURE:
+ gst.error("Could no set state to %s")
+ state = gst.STATE_NULL
+ pipeline.set_state(state)
+
+ return state
+
+
class Pipeline(ges.TimelinePipeline, Loggable):
"""
@@ -63,7 +177,7 @@ class Pipeline(ges.TimelinePipeline, Loggable):
"eos": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
()),
"error": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
- (gobject.TYPE_POINTER, gobject.TYPE_POINTER)),
+ (gobject.TYPE_STRING, gobject.TYPE_STRING)),
"element-message": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
(gobject.TYPE_POINTER,))}
@@ -77,6 +191,12 @@ class Pipeline(ges.TimelinePipeline, Loggable):
self._listening = False # for the position handler
self._listeningInterval = 300 # default 300ms
self._listeningSigId = 0
+ self._seeker = Seeker()
+
+ self._duration = gst.CLOCK_TIME_NONE
+ self._seeker.connect("seek", self._seekCb)
+ self._seeker.connect("seek-relative", self._seekRelativeCb)
+ self._seeker.connect("flush", self._seekFlushCb)
def release(self):
"""
@@ -92,10 +212,30 @@ class Pipeline(ges.TimelinePipeline, Loggable):
self._bus.disconnect_by_func(self._busMessageCb)
self._bus.remove_signal_watch()
self._bus.set_sync_handler(None)
+
+ self._seeker.disconnect_by_func(self._seekRelativeCb)
+ self._seeker.disconnect_by_func(self._seekFlushCb)
+ self._seeker.disconnect_by_func(self._seekCb)
+
self.setState(gst.STATE_NULL)
self._bus = None
- def flushSeekVideo(self):
+ def _seekRelativeCb(self, unused_seeker, time):
+ self.seekRelative(time)
+
+ def _seekFlushCb(self, unused_seeker):
+ self.flushSeek()
+
+ def _seekCb(self, ruler, position, format):
+ """
+ The app's main seek method used when the user seeks manually.
+
+ We clamp the seeker position so that it cannot go past 0 or the
+ end of the timeline.
+ """
+ self.simple_seek(position)
+
+ def flushSeek(self):
self.pause()
try:
self.seekRelative(0)
@@ -194,11 +334,16 @@ class Pipeline(ges.TimelinePipeline, Loggable):
try:
dur, format = self.query_duration(format)
except Exception, e:
+
self.handleException(e)
raise PipelineError("Couldn't get duration")
self.log("Got duration %s" % gst.TIME_ARGS(dur))
- self.emit("duration-changed", dur)
+ if self._duration != dur:
+ self.emit("duration-changed", dur)
+
+ self._duration = dur
+
return dur
def activatePositionListener(self, interval=300):
@@ -275,13 +420,14 @@ class Pipeline(ges.TimelinePipeline, Loggable):
if not res:
self.debug("seeking failed")
raise PipelineError("seek failed")
+
self.debug("seeking succesfull")
self.emit('position', position)
def seekRelative(self, time):
seekvalue = max(0, min(self.getPosition() + time,
self.getDuration()))
- self.seek(seekvalue)
+ self.simple_seek(seekvalue)
#}
## Private methods
@@ -332,7 +478,7 @@ class Pipeline(ges.TimelinePipeline, Loggable):
def _handleErrorMessage(self, error, detail, source):
self.error("error from %s: %s (%s)" % (source, error, detail))
- self.emit('error', error, detail)
+ self.emit('error', error.message, detail)
def _busSyncMessageHandler(self, unused_bus, message):
if message.type == gst.MESSAGE_ELEMENT:
diff --git a/pitivi/viewer.py b/pitivi/viewer.py
index b73761b..f48f794 100644
--- a/pitivi/viewer.py
+++ b/pitivi/viewer.py
@@ -31,7 +31,7 @@ from math import pi
from pitivi.utils.loggable import Loggable
from pitivi.settings import GlobalSettings
-from pitivi.utils.playback import Seeker
+from pitivi.utils.pipeline import Seeker
from pitivi.utils.ui import SPACING, hex_to_rgb
from pitivi.utils.widgets import TimeWidget
@@ -148,7 +148,7 @@ class PitiviViewer(gtk.VBox, Loggable):
self.pipeline.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, position)
self._setUiActive()
- self.seeker = self.app.projectManager.current.seeker
+ self.seeker = Seeker()
def _disconnectFromPipeline(self):
self.debug("pipeline:%r", self.pipeline)
@@ -402,7 +402,7 @@ class PitiviViewer(gtk.VBox, Loggable):
def _goToEndCb(self, unused_button):
try:
- end = self.app.current.timeline.props.duration
+ end = self.app.current.pipeline.getDuration()
except:
self.warning("Couldn't get timeline duration")
try:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]