pitivi r1237 - in branches/SOC_2008_BLEWIS: . pitivi/elements



Author: edwardrv
Date: Mon Aug 25 15:34:48 2008
New Revision: 1237
URL: http://svn.gnome.org/viewvc/pitivi?rev=1237&view=rev

Log:
* pitivi/elements/Makefile.am:
* pitivi/elements/imagefreeze.py:
New image-to-video element. Still needs more testing before being
usable in a timeline.


Added:
   branches/SOC_2008_BLEWIS/pitivi/elements/imagefreeze.py
Modified:
   branches/SOC_2008_BLEWIS/ChangeLog
   branches/SOC_2008_BLEWIS/pitivi/elements/Makefile.am

Modified: branches/SOC_2008_BLEWIS/pitivi/elements/Makefile.am
==============================================================================
--- branches/SOC_2008_BLEWIS/pitivi/elements/Makefile.am	(original)
+++ branches/SOC_2008_BLEWIS/pitivi/elements/Makefile.am	Mon Aug 25 15:34:48 2008
@@ -2,6 +2,7 @@
 
 elements_PYTHON = \
 	__init__.py \
+	imagefreeze.py \
 	singledecodebin.py \
 	smartscale.py	\
 	thumbnailsink.py \

Added: branches/SOC_2008_BLEWIS/pitivi/elements/imagefreeze.py
==============================================================================
--- (empty file)
+++ branches/SOC_2008_BLEWIS/pitivi/elements/imagefreeze.py	Mon Aug 25 15:34:48 2008
@@ -0,0 +1,209 @@
+# PiTiVi , Non-linear video editor
+#
+#       pitivi/elements/singledecodebin.py
+#
+# Copyright (c) 2005, Edward Hervey <bilboed bilboed com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+"""
+Image-to-video element
+"""
+
+# Goal:
+#
+# We want to take a raw image source and output a continuous
+# video feed (by default [0,GST_CLOCK_TIME_NONE]) according to
+# the srcpad negotiated caps (i.e. proper timestamps)
+#
+# In the event of seeks, this is the element that will handle the seeks
+# and output the proper segments.
+#
+# for a given negotiated framerate R (in frames/second):
+# The outputted buffer timestamps will be X * 1/R
+# where X is an integer.
+# EXCEPT for accurate segment seeks where the first/last buffers will be
+# adjusted to the requested start/stop values
+
+import gobject
+import gst
+
+
+class ImageFreeze(gst.Element):
+
+    __gstdetails__ = (
+        "ImageFreeze plugin",
+        "imagefreeze.py",
+        "Outputs a video feed out an incoming frame",
+        "Edward Hervey <bilboed bilboed com>")
+
+    _srctemplate = gst.PadTemplate("src",
+                                   gst.PAD_SRC,
+                                   gst.PAD_ALWAYS,
+                                   gst.caps_new_any())
+    _sinktemplate = gst.PadTemplate("sink",
+                                   gst.PAD_SINK,
+                                   gst.PAD_ALWAYS,
+                                   gst.caps_new_any())
+    __gsttemplates__ = (_srctemplate, _sinktemplate)
+
+    def __init__(self, *args, **kwargs):
+        gst.Element.__init__(self, *args, **kwargs)
+        self.srcpad = gst.Pad(self._srctemplate)
+        self.srcpad.set_event_function(self._src_event)
+
+        self.sinkpad = gst.Pad(self._sinktemplate)
+        self.sinkpad.set_chain_function(self._sink_chain)
+        self.sinkpad.set_event_function(self._sink_event)
+        self.sinkpad.set_setcaps_function(self._sink_setcaps)
+
+        self.add_pad(self.srcpad)
+        self.add_pad(self.sinkpad)
+
+        self._reset()
+
+    def _reset(self):
+        gst.debug("resetting ourselves")
+        self._outputrate = None
+        self._srccaps = None
+        # number of outputted buffers
+        self._offset = 0
+        self._segment = gst.Segment()
+        self._segment.init(gst.FORMAT_TIME)
+        self._needsegment = True
+        self._bufferduration = 0
+
+    def _sink_setcaps(self, pad, caps):
+        gst.debug("caps %s" % caps.to_string())
+        downcaps = self.srcpad.peer_get_caps().copy()
+        gst.debug("downcaps %s" % downcaps.to_string())
+
+        # methodology
+        # 1. We override any incoming framerate
+        ccaps = caps.make_writable()
+        for struct in ccaps:
+            if struct.has_key("framerate"):
+                try:
+                    del struct["framerate"]
+                except:
+                    gst.warning("Couldn't remove 'framerate' from %s" % struct.to_string())
+
+        # 2. we do the intersection of our incoming stripped caps
+        #    and the downstream caps
+        intersect = ccaps.intersect(downcaps)
+        if intersect.is_empty():
+            gst.warning("no negotiation possible !")
+            return False
+
+        # 3. for each candidate in the intersection, we try to set that
+        #    candidate downstream
+        for candidate in intersect:
+            gst.debug("Trying %s" % candidate.to_string())
+            if self.srcpad.peer_accept_caps(candidate):
+                gst.debug("accepted !")
+                # 4. When we have an accepted caps downstream, we store the negotiated
+                #    framerate and return
+                self._outputrate = candidate["framerate"]
+                self._bufferduration = gst.SECOND * self._outputrate.denom / self._outputrate.num
+                self._srccaps = candidate
+                return self.srcpad.set_caps(candidate)
+
+        # 5. If we can't find an accepted candidate, we return False
+        return False
+
+    def _src_event(self, pad, event):
+        # for the moment we just push it upstream
+        gst.debug("event %r" % event)
+        if event.type == gst.EVENT_SEEK:
+            rate,fmt,flags,startt,start,stopt,stop = event.parse_seek()
+            gst.debug("Handling seek event %r" % flags)
+            self._segment.set_seek(*event.parse_seek())
+            gst.debug("_segment start:%s stop:%s" % (gst.TIME_ARGS(self._segment.start),
+                                                     gst.TIME_ARGS(self._segment.stop)))
+            # create a new initial seek
+            self._needsegment = True
+            seek = gst.event_new_seek(1.0, gst.FORMAT_TIME, flags,
+                                      gst.SEEK_TYPE_NONE, 0,
+                                      gst.SEEK_TYPE_NONE, 0)
+            return self.sinkpad.push_event(seek)
+        return self.sinkpad.push_event(event)
+
+    def _sink_event(self, pad, event):
+        gst.debug("event %r" % event)
+        if event.type == gst.EVENT_NEWSEGMENT:
+            gst.debug("dropping new segment !")
+            return True
+        elif event.type == gst.EVENT_FLUSH_START:
+            self._reset()
+        return self.srcpad.push_event(event)
+
+    def _sink_chain(self, pad, buffer):
+        gst.debug("buffer %s %s" % (gst.TIME_ARGS(buffer.timestamp),
+                                    gst.TIME_ARGS(buffer.duration)))
+        res = gst.FLOW_OK
+
+        # and now we go in a glorious loop :)
+        while res == gst.FLOW_OK:
+            gst.debug("needsegment: %r" % self._needsegment)
+
+            if self._needsegment:
+                gst.debug("Need to output a new segment")
+                segment = gst.event_new_new_segment(False,
+                                                    self._segment.rate,
+                                                    self._segment.format,
+                                                    self._segment.start,
+                                                    self._segment.stop,
+                                                    self._segment.start)
+                self.srcpad.push_event(segment)
+                # calculate offset
+                # offset is int(segment.start / outputrate)
+                self._offset = int(self._segment.start * self._outputrate.num / self._outputrate.denom / gst.SECOND)
+                self._needsegment = False
+
+            # new position
+            position = self._offset * gst.SECOND * self._outputrate.denom / self._outputrate.num
+            if self._segment.stop != -1 and position > self._segment.stop:
+                gst.debug("end of configured segment (position:%s / segment_stop:%s)" % (gst.TIME_ARGS(position),
+                                                                                         gst.TIME_ARGS(self._segment.stop)))
+                # end of stream/segment handling
+                if self._segment.flags & gst.SEEK_FLAG_SEGMENT:
+                    # emit a gst.MESSAGE_SEGMENT_DONE
+                    self.post_message(gst.message_new_segment_done(self, gst.FORMAT_TIME, self._segment.stop))
+                else:
+                    self.srcpad.push_event(gst.event_new_eos())
+                res = gst.FLOW_WRONG_STATE
+                break
+
+            # we need to update the caps here !
+            obuf = buffer.make_metadata_writable()
+            ok, nstart, nstop = self._segment.clip(gst.FORMAT_TIME,
+                                                   position, position + self._bufferduration)
+            if ok:
+                obuf.timestamp = nstart
+                obuf.duration = nstop - nstart
+                obuf.caps = self._srccaps
+                gst.debug("Pushing out buffer %s" % gst.TIME_ARGS(obuf.timestamp))
+                res = self.srcpad.push(obuf)
+            self._offset += 1
+
+        return res
+
+    def do_change_state(self, transition):
+        if transition in [gst.STATE_CHANGE_READY_TO_PAUSED, gst.STATE_CHANGE_PAUSED_TO_READY]:
+            self._reset()
+        return gst.Element.do_change_state(self, transition)
+
+gobject.type_register(ImageFreeze)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]