msmith - in flumotion/branches/playlist-1: .
flumotion/component/producers/playlist
flumotion-commit at lists.fluendo.com
flumotion-commit at lists.fluendo.com
Wed May 2 18:48:38 CEST 2007
Author: msmith
Date: Wed May 2 18:48:35 2007
New Revision: 4870
Added:
flumotion/branches/playlist-1/flumotion/component/producers/playlist/singledecodebin.py
flumotion/branches/playlist-1/flumotion/component/producers/playlist/smartscale.py
Modified:
flumotion/branches/playlist-1/ChangeLog
flumotion/branches/playlist-1/flumotion/component/producers/playlist/Makefile.am
flumotion/branches/playlist-1/flumotion/component/producers/playlist/playlist.py
flumotion/branches/playlist-1/flumotion/component/producers/playlist/playlist.xml
Log:
* flumotion/component/producers/playlist/Makefile.am:
* flumotion/component/producers/playlist/playlist.py:
* flumotion/component/producers/playlist/playlist.xml:
* flumotion/component/producers/playlist/singledecodebin.py:
* flumotion/component/producers/playlist/smartscale.py:
GNonLin framework up and running, ready for the real playlist stuff
to go in.
Modified: flumotion/branches/playlist-1/ChangeLog
==============================================================================
--- flumotion/branches/playlist-1/ChangeLog (original)
+++ flumotion/branches/playlist-1/ChangeLog Wed May 2 18:48:35 2007
@@ -1,5 +1,15 @@
2007-05-02 Michael Smith <msmith at fluendo.com>
+ * flumotion/component/producers/playlist/Makefile.am:
+ * flumotion/component/producers/playlist/playlist.py:
+ * flumotion/component/producers/playlist/playlist.xml:
+ * flumotion/component/producers/playlist/singledecodebin.py:
+ * flumotion/component/producers/playlist/smartscale.py:
+ GNonLin framework up and running, ready for the real playlist stuff
+ to go in.
+
+2007-05-02 Michael Smith <msmith at fluendo.com>
+
* flumotion/component/producers/playlist/playlist.py:
Refactor to make things cleaner before returning to new development.
Modified: flumotion/branches/playlist-1/flumotion/component/producers/playlist/Makefile.am
==============================================================================
--- flumotion/branches/playlist-1/flumotion/component/producers/playlist/Makefile.am (original)
+++ flumotion/branches/playlist-1/flumotion/component/producers/playlist/Makefile.am Wed May 2 18:48:35 2007
@@ -1,6 +1,6 @@
include $(top_srcdir)/common/python.mk
-component_PYTHON = __init__.py playlist.py
+component_PYTHON = __init__.py playlist.py singledecodebin.py smartscale.py
componentdir = $(libdir)/flumotion/python/flumotion/component/producers/playlist
component_DATA = playlist.xml
Modified: flumotion/branches/playlist-1/flumotion/component/producers/playlist/playlist.py
==============================================================================
--- flumotion/branches/playlist-1/flumotion/component/producers/playlist/playlist.py (original)
+++ flumotion/branches/playlist-1/flumotion/component/producers/playlist/playlist.py Wed May 2 18:48:35 2007
@@ -30,6 +30,7 @@
from flumotion.common.messages import N_
import smartscale
+import singledecodebin
T_ = messages.gettexter('flumotion')
@@ -38,7 +39,19 @@
gnlsrc = gst.element_factory_make('gnlsource', name)
gnlsrc.props.start = start
gnlsrc.props.duration = duration
- gnlsrc.props.media_start = start
+ gnlsrc.props.media_start = 0
+ gnlsrc.props.media_duration = duration
+ gnlsrc.props.priority = priority
+ gnlsrc.add(src)
+
+ return gnlsrc
+
+def file_gnl_src(name, uri, caps, start, duration, priority):
+ src = singledecodebin.SingleDecodeBin(caps, uri)
+ gnlsrc = gst.element_factory_make('gnlsource', name)
+ gnlsrc.props.start = start
+ gnlsrc.props.duration = duration
+ gnlsrc.props.media_start = 0
gnlsrc.props.media_duration = duration
gnlsrc.props.priority = priority
gnlsrc.add(src)
@@ -50,7 +63,7 @@
gnlsrc = gst.element_factory_make('gnlsource', name)
gnlsrc.props.start = start
gnlsrc.props.duration = duration
- gnlsrc.props.media_start = start
+ gnlsrc.props.media_start = 0
gnlsrc.props.media_duration = duration
gnlsrc.props.priority = priority
gnlsrc.add(src)
@@ -68,35 +81,34 @@
def init(self):
pass
- def _padAddedCb(self, element, pad, target):
- self.debug("Pad added, linking")
- pad.link(target)
-
- def _buildAudioPipeline(self, pipeline):
+ def _buildAudioPipeline(self, pipeline, queue):
audioconvert = gst.element_factory_make('audioconvert')
audioresample = gst.element_factory_make('audioresample')
- pipeline.add_many(audioconvert, audioresample)
- self.audiocomp.connect('pad-added', self._padAddedCb,
- audioconvert.get_pad("sink"))
+ pipeline.add(audioconvert, audioresample)
+ queue.link(audioconvert)
audioconvert.link(audioresample)
return audioresample.get_pad('src')
- def _buildVideoPipeline(self, pipeline):
- cspace = gst.element_factory_make("ffmpegcolorspace")
- videocaps = gst.caps_from_string(
+ def _buildVideoPipeline(self, pipeline, queue):
+ outcaps = gst.caps_from_string(
"video/x-raw-yuv,width=%d,height=%d,framerate=%d/%d" %
(320, 240, 15, 1))
+
+ cspace = gst.element_factory_make("ffmpegcolorspace")
scaler = smartscale.SmartVideoScale()
- scaler.set_caps(videocaps)
+ scaler.set_caps(outcaps)
videorate = gst.element_factory_make("videorate")
- pipeline.add_many(scaler, cspace, videorate)
- self.videocomp.connect('pad-added', self._padAddedCb,
- cspace.get_pad("sink"))
+ capsfilter = gst.element_factory_make("capsfilter")
+ capsfilter.props.caps = outcaps
+
+ pipeline.add(cspace, scaler, videorate, capsfilter)
+
+ queue.link(cspace)
cspace.link(scaler)
scaler.link(videorate)
-
- return videorate.get_pad('src')
+ videorate.link(capsfilter)
+ return capsfilter.get_pad('src')
def _buildPipeline(self):
pipeline = gst.Pipeline()
@@ -104,19 +116,32 @@
for mediatype in ['audio', 'video']:
composition = gst.element_factory_make("gnlcomposition",
mediatype + "-composition")
- pipeline.add(composition)
- if mediatype == 'audio':
- self.audiocomp = composition
- srcpad = self._buildAudioPipeline(pipeline)
- else:
- self.videocomp = composition
- srcpad = self._buildVideoPipeline(pipeline)
+ queue = gst.element_factory_make("queue")
identity = gst.element_factory_make("identity")
identity.set_property("sync", True)
+ identity.set_property("single-segment", True)
identity.set_property("silent", True)
- pipeline.add(identity)
- srcpad.link(identity.get_pad('sink'))
+
+ pipeline.add(composition, identity, queue)
+
+ def _padAddedCb(element, pad, target):
+ self.debug("Pad added, linking")
+ pad.link(target)
+ composition.connect('pad-added', _padAddedCb,
+ identity.get_pad("sink"))
+ identity.link(queue)
+
+ if mediatype == 'audio':
+ self.audiocomp = composition
+ self.audiocomp.props.caps = \
+ gst.Caps("audio/x-raw-int;audio/x-raw-float")
+ srcpad = self._buildAudioPipeline(pipeline, queue)
+ else:
+ self.videocomp = composition
+ self.videocomp.props.caps = \
+ gst.Caps("video/x-raw-yuv;video/x-raw-rgb")
+ srcpad = self._buildVideoPipeline(pipeline, queue)
feedername = 'feeder:%s:%s' % (self.name, mediatype)
chunk = self.FEEDER_TMPL % {'name': feedername}
@@ -129,22 +154,30 @@
bin.add_pad(ghostpad)
pipeline.add(bin)
- identity.get_pad("src").link(ghostpad)
+ srcpad.link(ghostpad)
return pipeline
- def create_pipeline(self):
- pipeline = self._buildPipeline()
-
+ def _createDefaultSources(self):
vsrc = videotest_gnl_src("videotestdefault", 0, 2**63 - 1,
- 2**32 - 2)
- vsrc.props.caps = gst.Caps("video/x-raw-yuv,format=(fourcc)I420")
+ 2**31 - 1)
self.videocomp.add(vsrc)
asrc = audiotest_gnl_src("videotestdefault", 0, 2**63 - 1,
- 2**32 - 2)
+ 2**31 - 1)
self.audiocomp.add(asrc)
+ def create_pipeline(self):
+ pipeline = self._buildPipeline()
+
+ self._createDefaultSources()
+
+ uri = "file:///home/msmith/media/testfiles/matrix.avi"
+ caps = gst.Caps("video/x-raw-yuv")
+ vsrc = file_gnl_src('testgnlfile', uri, caps, 10 * gst.SECOND,
+ 15 * gst.SECOND, 0)
+ self.videocomp.add(vsrc)
+
self.connect_feeders(pipeline)
return pipeline
Modified: flumotion/branches/playlist-1/flumotion/component/producers/playlist/playlist.xml
==============================================================================
--- flumotion/branches/playlist-1/flumotion/component/producers/playlist/playlist.xml (original)
+++ flumotion/branches/playlist-1/flumotion/component/producers/playlist/playlist.xml Wed May 2 18:48:35 2007
@@ -36,6 +36,7 @@
<directory name="flumotion/component/producers/playlist">
<filename location="__init__.py" />
<filename location="smartscale.py" />
+ <filename location="singledecodebin.py" />
<filename location="playlist.py" />
</directory>
</directories>
Added: flumotion/branches/playlist-1/flumotion/component/producers/playlist/singledecodebin.py
==============================================================================
--- (empty file)
+++ flumotion/branches/playlist-1/flumotion/component/producers/playlist/singledecodebin.py Wed May 2 18:48:35 2007
@@ -0,0 +1,308 @@
+# -*- Mode: Python -*-
+# vi:si:et:sw=4:sts=4:ts=4
+#
+# Flumotion - a streaming media server
+# Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com).
+# All rights reserved.
+
+# This file may be distributed and/or modified under the terms of
+# the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+# This file is distributed without any warranty; without even the implied
+# warranty of merchantability or fitness for a particular purpose.
+# See "LICENSE.GPL" in the source distribution for more information.
+
+# Licensees having purchased or holding a valid Flumotion Advanced
+# Streaming Server license may use this file in accordance with the
+# Flumotion Advanced Streaming Server Commercial License Agreement.
+# See "LICENSE.Flumotion" in the source distribution for more information.
+
+# Headers in this file shall remain intact.
+
+
+# Originally part of PiTiVi,
+# Copyright (C) 2005-2007 Edward Hervey <bilboed at bilboed.com>,
+# Relicensed under the above dual license with his permission.
+
+"""
+Single-stream queue-less decodebin
+"""
+
+import gobject
+import gst
+
+def is_raw(caps):
+ """ returns True if the caps are RAW """
+ rep = caps.to_string()
+ valid = ["video/x-raw", "audio/x-raw", "text/plain", "text/x-pango-markup"]
+ for val in valid:
+ if rep.startswith(val):
+ return True
+ return False
+
+class SingleDecodeBin(gst.Bin):
+
+ __gsttemplates__ = (
+ gst.PadTemplate ("sinkpadtemplate",
+ gst.PAD_SINK,
+ gst.PAD_ALWAYS,
+ gst.caps_new_any()),
+ gst.PadTemplate ("srcpadtemplate",
+ gst.PAD_SRC,
+ gst.PAD_SOMETIMES,
+ gst.caps_new_any())
+ )
+ def __init__(self, caps=None, uri=None, *args, **kwargs):
+ gst.Bin.__init__(self, *args, **kwargs)
+ if not caps:
+ caps = gst.caps_new_any()
+ self.caps = caps
+ self.typefind = gst.element_factory_make("typefind", "internal-typefind")
+ self.add(self.typefind)
+
+ self.uri = uri
+ if self.uri and gst.uri_is_valid(self.uri):
+ self.urisrc = gst.element_make_from_uri(gst.URI_SRC, uri, "urisrc")
+ self.log("created urisrc %s / %r" % (self.urisrc.get_name(),
+ self.urisrc))
+ self.add(self.urisrc)
+ self.urisrc.link(self.typefind)
+ else:
+ self._sinkpad = gst.GhostPad("sink", self.typefind.get_pad("sink"))
+ self._sinkpad.set_active(True)
+ self.add_pad(self._sinkpad)
+
+ self.typefind.connect("have_type", self._typefindHaveTypeCb)
+
+ self._srcpad = None
+
+ self._dynamics = []
+
+ self._validelements = [] #added elements
+
+ self._factories = self._getSortedFactoryList()
+
+
+ ## internal methods
+
+ def _controlDynamicElement(self, element):
+ self.log("element:%s" % element.get_name())
+ self._dynamics.append(element)
+ element.connect("pad-added", self._dynamicPadAddedCb)
+ element.connect("no-more-pads", self._dynamicNoMorePadsCb)
+
+ def _getSortedFactoryList(self):
+ """
+ Returns the list of demuxers, decoders and parsers available, sorted
+ by rank
+ """
+ def myfilter(fact):
+ if fact.get_rank() < 64 :
+ return False
+ klass = fact.get_klass()
+ if not ("Demuxer" in klass or "Decoder" in klass or "Parse" in klass):
+ return False
+ return True
+ reg = gst.registry_get_default()
+ res = [x for x in reg.get_feature_list(gst.ElementFactory) if myfilter(x)]
+ res.sort(lambda a, b: int(b.get_rank() - a.get_rank()))
+ return res
+
+ def _findCompatibleFactory(self, caps):
+ """
+ Returns a list of factories (sorted by rank) which can take caps as
+ input. Returns empty list if none are compatible
+ """
+ self.debug("caps:%s" % caps.to_string())
+ res = []
+ for factory in self._factories:
+ for template in factory.get_static_pad_templates():
+ if template.direction == gst.PAD_SINK:
+ intersect = caps.intersect(template.static_caps.get())
+ if not intersect.is_empty():
+ res.append(factory)
+ break
+ self.debug("returning %r" % res)
+ return res
+
+ def _closeLink(self, element):
+ """
+ Inspects element and tries to connect something on the srcpads.
+ If there are dynamic pads, it sets up a signal handler to
+ continue autoplugging when they become available.
+ """
+ to_connect = []
+ dynamic = False
+ templates = element.get_pad_template_list()
+ for template in templates:
+ if not template.direction == gst.PAD_SRC:
+ continue
+ if template.presence == gst.PAD_ALWAYS:
+ pad = element.get_pad(template.name_template)
+ to_connect.append(pad)
+ elif template.presence == gst.PAD_SOMETIMES:
+ pad = element.get_pad(template.name_template)
+ if pad:
+ to_connect.append(pad)
+ else:
+ dynamic = True
+ else:
+ self.log("Template %s is a request pad, ignoring" % pad.name_template)
+
+ if dynamic:
+ self.debug("%s is a dynamic element" % element.get_name())
+ self._controlDynamicElement(element)
+
+ for pad in to_connect:
+ self._closePadLink(element, pad, pad.get_caps())
+
+ def _tryToLink1(self, source, pad, factories):
+ """
+ Tries to link one of the factories' element to the given pad.
+
+ Returns the element that was successfully linked to the pad.
+ """
+ self.debug("source:%s, pad:%s , factories:%r" % (source.get_name(),
+ pad.get_name(),
+ factories))
+ result = None
+ for factory in factories:
+ element = factory.create()
+ if not element:
+ self.warning("weren't able to create element from %r" % factory)
+ continue
+
+ sinkpad = element.get_pad("sink")
+ if not sinkpad:
+ continue
+
+ self.add(element)
+ try:
+ pad.link(sinkpad)
+ except:
+ element.set_state(gst.STATE_NULL)
+ self.remove(element)
+ continue
+
+ self._closeLink(element)
+ element.set_state(gst.STATE_PAUSED)
+
+ result = element
+ break
+
+ return result
+
+ def _closePadLink(self, element, pad, caps):
+ """
+ Finds the list of elements that could connect to the pad.
+ If the pad has the desired caps, it will create a ghostpad.
+ If no compatible elements could be found, the search will stop.
+ """
+ self.debug("element:%s, pad:%s, caps:%s" % (element.get_name(),
+ pad.get_name(),
+ caps.to_string()))
+ if caps.is_empty():
+ self.log("unknown type")
+ return
+ if caps.is_any():
+ self.log("type is not know yet, waiting")
+ return
+ if caps.intersect(self.caps):
+ # This is the desired caps
+ if not self._srcpad:
+ self._wrapUp(element, pad)
+ elif is_raw(caps):
+ self.log("We hit a raw caps which isn't the wanted one")
+ # FIXME : recursively remove everything until demux/typefind
+
+ else:
+ # Find something
+ if len(caps) > 1:
+ self.log("many possible types, delaying")
+ return
+ facts = self._findCompatibleFactory(caps)
+ if not facts:
+ self.log("unknown type")
+ return
+ self._tryToLink1(element, pad, facts)
+
+ def _wrapUp(self, element, pad):
+ """
+ Ghost the given pad of element.
+ Remove non-used elements.
+ """
+
+ if self._srcpad:
+ return
+ self._markValidElements(element)
+ self._removeUnusedElements(self.typefind)
+ self.log("ghosting pad %s" % pad.get_name)
+ self._srcpad = gst.GhostPad("src", pad)
+ self._srcpad.set_active(True)
+ self.add_pad(self._srcpad)
+ self.post_message(gst.message_new_state_dirty(self))
+
+ def _markValidElements(self, element):
+ """
+ Mark this element and upstreams as valid
+ """
+ self.log("element:%s" % element.get_name())
+ if element == self.typefind:
+ return
+ self._validelements.append(element)
+ # find upstream element
+ pad = list(element.sink_pads())[0]
+ parent = pad.get_peer().get_parent()
+ self._markValidElements(parent)
+
+ def _removeUnusedElements(self, element):
+ """
+ Remove unused elements connected to srcpad(s) of element
+ """
+ self.log("element:%s" % element)
+ for pad in element.src_pads():
+ if pad.is_linked():
+ peer = pad.get_peer().get_parent()
+ self._removeUnusedElements(peer)
+ if not peer in self._validelements:
+ self.log("removing %s" % peer.get_name())
+ pad.unlink(pad.get_peer())
+ peer.set_state(gst.STATE_NULL)
+ self.remove(peer)
+
+ def _cleanUp(self):
+ self.log("")
+ if self._srcpad:
+ self.remove_pad(self._srcpad)
+ self._srcpad = None
+ for element in self._validelements:
+ element.set_state(gst.STATE_NULL)
+ self.remove(element)
+ self._validelements = []
+
+ ## Overrides
+
+ def do_change_state(self, transition):
+ self.debug("transition:%r" % transition)
+ res = gst.Bin.do_change_state(self, transition)
+ if transition in [gst.STATE_CHANGE_PAUSED_TO_READY, gst.STATE_CHANGE_READY_TO_NULL]:
+ self._cleanUp()
+ return res
+
+ ## Signal callbacks
+
+ def _typefindHaveTypeCb(self, typefind, probability, caps):
+ self.debug("probability:%d, caps:%s" % (probability, caps.to_string()))
+ self._closePadLink(typefind, typefind.get_pad("src"), caps)
+
+ ## Dynamic element Callbacks
+
+ def _dynamicPadAddedCb(self, element, pad):
+ self.log("element:%s, pad:%s" % (element.get_name(), pad.get_name()))
+ if not self._srcpad:
+ self._closePadLink(element, pad, pad.get_caps())
+
+ def _dynamicNoMorePadsCb(self, element):
+ self.log("element:%s" % element.get_name())
+
+gobject.type_register(SingleDecodeBin)
Added: flumotion/branches/playlist-1/flumotion/component/producers/playlist/smartscale.py
==============================================================================
--- (empty file)
+++ flumotion/branches/playlist-1/flumotion/component/producers/playlist/smartscale.py Wed May 2 18:48:35 2007
@@ -0,0 +1,199 @@
+# -*- Mode: Python -*-
+# vi:si:et:sw=4:sts=4:ts=4
+#
+# Flumotion - a streaming media server
+# Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com).
+# All rights reserved.
+
+# This file may be distributed and/or modified under the terms of
+# the GNU General Public License version 2 as published by
+# the Free Software Foundation.
+# This file is distributed without any warranty; without even the implied
+# warranty of merchantability or fitness for a particular purpose.
+# See "LICENSE.GPL" in the source distribution for more information.
+
+# Licensees having purchased or holding a valid Flumotion Advanced
+# Streaming Server license may use this file in accordance with the
+# Flumotion Advanced Streaming Server Commercial License Agreement.
+# See "LICENSE.Flumotion" in the source distribution for more information.
+
+# Headers in this file shall remain intact.
+
+
+# Originally part of PiTiVi,
+# Copyright (C) 2005-2007 Edward Hervey <bilboed at bilboed.com>,
+# Relicensed under the above dual license with his permission.
+
+"""
+Smart video scaler
+"""
+
+# Algorithm logic
+#
+# PAR is the same in videobox (automatic)
+# DAR is the same in videoscale (We need to make sure)
+#
+# The whole idea is to modify the caps between videobox and videoscale so that
+# the
+
+import gobject
+import gst
+
+class SmartVideoScale(gst.Bin):
+ """
+ Element to do proper videoscale.
+ Keeps Display Aspect Ratio.
+ Adds black borders if needed.
+ """
+
+ def __init__(self):
+ gst.Bin.__init__(self)
+ self.videoscale = gst.element_factory_make("videoscale", "smart-videoscale")
+ # set the scaling method to bilinear (cleaner)
+ # FIXME : we should figure out if better methods are available in the
+ # future, or ask the user which method he wants to use
+ # FIXME : Instead of having the set_caps() method, use proper caps negotiation
+ self.videoscale.props.method = 1
+ self.videobox = gst.element_factory_make("videobox", "smart-videobox")
+ self.capsfilter = gst.element_factory_make("capsfilter", "smart-capsfilter")
+ self.add(self.videoscale, self.capsfilter, self.videobox)
+ gst.element_link_many(self.videoscale, self.capsfilter, self.videobox)
+
+ self._sinkPad = gst.GhostPad("sink", self.videoscale.get_pad("sink"))
+ self._sinkPad.set_active(True)
+ self._srcPad = gst.GhostPad("src", self.videobox.get_pad("src"))
+ self._srcPad.set_active(True)
+
+ self.add_pad(self._sinkPad)
+ self.add_pad(self._srcPad)
+
+ self._sinkPad.set_setcaps_function(self._sinkSetCaps)
+
+
+ # input/output values
+ self.capsin = None
+ self.widthin = -1
+ self.heightin = -1
+ self.parin = gst.Fraction(1,1)
+ self.darin = gst.Fraction(1,1)
+ self.capsout = None
+ self.widthout = -1
+ self.heightout = -1
+ self.parout = gst.Fraction(1,1)
+ self.darout = gst.Fraction(1,1)
+
+ def set_caps(self, caps):
+ """ set the outgoing caps, because gst.BaseTransform is full of CRACK ! """
+ self.widthout, self.heightout, self.parout, self.darout = self._getValuesFromCaps(caps, True)
+
+ def _sinkSetCaps(self, unused_pad, caps):
+ self.log("caps:%s" % caps.to_string())
+ self.widthin, self.heightin, self.parin, self.darin = self._getValuesFromCaps(caps)
+ self._computeAndSetValues()
+ res = self.videoscale.get_pad("sink").set_caps(caps)
+ return res
+
+ def _srcSetCaps(self, unused_pad, caps):
+ self.log("caps:%s" % caps.to_string())
+ self.widthout, self.heightout, self.parout, self.darout = self._getValuesFromCaps(caps)
+ res = self.videobox.get_pad("src").set_caps(caps)
+ if res:
+ self.capsout = caps
+ self._computeAndSetValues()
+ return res
+
+ def _sinkPadCapsNotifyCb(self, pad, unused_prop):
+ caps = pad.get_negotiated_caps()
+ self.log("caps:%r" % caps)
+ self.widthin, self.heightin, self.parin, self.darin = self._getValuesFromCaps(caps)
+ self.capsin = caps
+ self._computeAndSetValues()
+
+ def _srcPadCapsNotifyCb(self, pad, unused_prop):
+ caps = pad.get_negotiated_caps()
+ self.log("caps:%r" % caps)
+ self.widthout, self.heightout, self.parout, self.darout = self._getValuesFromCaps(caps)
+ self.capsout = caps
+ self._computeAndSetValues()
+
+ def _getValuesFromCaps(self, caps, force=False):
+ """
+ returns (width, height, par, dar) from given caps.
+ If caps are None, or not negotiated, it will return
+ (-1, -1, gst.Fraction(1,1), gst.Fraction(1,1))
+ """
+ width = -1
+ height = -1
+ par = gst.Fraction(1,1)
+ dar = gst.Fraction(1,1)
+ if force or (caps and caps.is_fixed()):
+ struc = caps[0]
+ width = struc["width"]
+ height = struc["height"]
+ if struc.has_field('pixel-aspect-ratio'):
+ par = struc['pixel-aspect-ratio']
+ dar = gst.Fraction(width * par.num, height * par.denom)
+ return (width, height, par, dar)
+
+ def _computeAndSetValues(self):
+ """ Calculate the new values to set on capsfilter and videobox. """
+ if self.widthin == -1 or self.heightin == -1 or self.widthout == -1 or self.heightout == -1:
+ # FIXME : should we reset videobox/capsfilter properties here ?
+ self.error("We don't have input and output caps, we can't calculate videobox values")
+ return
+
+ self.log("incoming width/height/PAR/DAR : %d/%d/%r/%r" % (self.widthin, self.heightin,
+ self.parin, self.darin))
+ self.log("outgoing width/height/PAR/DAR : %d/%d/%r/%r" % (self.widthout, self.heightout,
+ self.parout, self.darout))
+
+ if self.darin == self.darout:
+ self.log("We have same input and output caps, resetting capsfilter and videobox settings")
+ # same DAR, set inputcaps on capsfilter, reset videobox values
+ caps = gst.caps_new_any()
+ left = 0
+ right = 0
+ top = 0
+ bottom = 0
+ else:
+ par = self.parout
+ dar = self.darin
+ if float(self.darin) > float(self.darout):
+ self.log("incoming DAR is greater that ougoing DAR. Adding top/bottom borders")
+ # width, PAR stays the same as output
+ # calculate newheight = (PAR * widthout) / DAR
+ newheight = (par.num * self.widthout * dar.denom) / (par.denom * dar.num)
+ self.log("newheight should be %d" % newheight)
+ extra = self.heightout - newheight
+ top = extra / 2
+ bottom = extra - top # compensate for odd extra
+ left = right = 0
+ # calculate filter caps
+ astr = "width=%d,height=%d" % (self.widthout, newheight)
+ else:
+ self.log("incoming DAR is smaller than outgoing DAR. Adding left/right borders")
+ # height, PAR stays the same as output
+ # calculate newwidth = (DAR * heightout) / PAR
+ newwidth = (dar.num * self.heightout * par.denom) / (dar.denom * par.num)
+ self.log("newwidth should be %d" % newwidth)
+ extra = self.widthout - newwidth
+ left = extra / 2
+ right = extra - left # compensate for odd extra
+ top = bottom = 0
+ # calculate filter caps
+ astr = "width=%d,height=%d" % (newwidth, self.heightout)
+ caps = gst.caps_from_string("video/x-raw-yuv,%s;video/x-raw-rgb,%s" % (astr, astr))
+
+ # set properties on elements
+ self.debug("About to set left/right/top/bottom : %d/%d/%d/%d" % (-left, -right, -top, -bottom))
+ self.videobox.props.left = -left
+ self.videobox.props.right = -right
+ self.videobox.props.top = -top
+ self.videobox.props.bottom = -bottom
+ self.debug("Settings filter caps %s" % caps.to_string())
+ self.capsfilter.props.caps = caps
+ self.debug("done")
+
+
+
+gobject.type_register(SmartVideoScale)
More information about the flumotion-commit
mailing list