Package flumotion :: Package component :: Package producers :: Package playlist :: Module smartscale
[hide private]

Source Code for Module flumotion.component.producers.playlist.smartscale

  1  # -*- Mode: Python -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3   
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007,2008,2009 Fluendo, S.L. 
  6  # Copyright (C) 2010,2011 Flumotion Services, S.A. 
  7  # All rights reserved. 
  8  # 
  9  # This file may be distributed and/or modified under the terms of 
 10  # the GNU Lesser General Public License version 2.1 as published by 
 11  # the Free Software Foundation. 
 12  # This file is distributed without any warranty; without even the implied 
 13  # warranty of merchantability or fitness for a particular purpose. 
 14  # See "LICENSE.LGPL" in the source distribution for more information. 
 15  # 
 16  # Headers in this file shall remain intact. 
 17   
 18  # Originally part of PiTiVi, 
 19  # Copyright (C) 2005-2007 Edward Hervey <bilboed@bilboed.com>, 
 20   
 21  """ 
 22  Smart video scaler 
 23  """ 
 24   
 25  # Algorithm logic 
 26  # 
 27  # PAR is the same in videobox (automatic) 
 28  # DAR is the same in videoscale (We need to make sure) 
 29  # 
 30  # The whole idea is to modify the caps between videobox and videoscale so that 
 31  # the 
 32   
 33  import gobject 
 34  import gst 
 35   
 36  __version__ = "$Rev$" 
 37   
 38   
39 -class SmartVideoScale(gst.Bin):
40 """ 41 Element to do proper videoscale. 42 Keeps Display Aspect Ratio. 43 Adds black borders if needed. 44 """ 45
46 - def __init__(self):
47 gst.Bin.__init__(self) 48 self.videoscale = gst.element_factory_make( 49 "videoscale", "smart-videoscale") 50 # set the scaling method to bilinear (cleaner) 51 # FIXME : we should figure out if better methods are available in the 52 # future, or ask the user which method he wants to use 53 # FIXME : Instead of having the set_caps() method, 54 # use proper caps negotiation 55 self.videoscale.props.method = 1 56 self.videobox = gst.element_factory_make( 57 "videobox", "smart-videobox") 58 self.capsfilter = gst.element_factory_make( 59 "capsfilter", "smart-capsfilter") 60 self.add(self.videoscale, self.capsfilter, self.videobox) 61 gst.element_link_many(self.videoscale, self.capsfilter, self.videobox) 62 63 self._sinkPad = gst.GhostPad("sink", self.videoscale.get_pad("sink")) 64 self._sinkPad.set_active(True) 65 self._srcPad = gst.GhostPad("src", self.videobox.get_pad("src")) 66 self._srcPad.set_active(True) 67 68 self.add_pad(self._sinkPad) 69 self.add_pad(self._srcPad) 70 71 self._sinkPad.set_setcaps_function(self._sinkSetCaps) 72 73 74 # input/output values 75 self.capsin = None 76 self.widthin = -1 77 self.heightin = -1 78 self.parin = gst.Fraction(1, 1) 79 self.darin = gst.Fraction(1, 1) 80 self.capsout = None 81 self.widthout = -1 82 self.heightout = -1 83 self.parout = gst.Fraction(1, 1) 84 self.darout = gst.Fraction(1, 1)
85
86 - def set_caps(self, caps):
87 """ set the outgoing caps, because gst.BaseTransform 88 is full of CRACK ! """ 89 (self.widthout, 90 self.heightout, 91 self.parout, 92 self.darout) = self._getValuesFromCaps(caps, True)
93
94 - def _sinkSetCaps(self, unused_pad, caps):
95 self.log("caps:%s" % caps.to_string()) 96 (self.widthin, 97 self.heightin, 98 self.parin, 99 self.darin) = self._getValuesFromCaps(caps) 100 self._computeAndSetValues() 101 res = self.videoscale.get_pad("sink").set_caps(caps) 102 return res
103
104 - def _srcSetCaps(self, unused_pad, caps):
105 self.log("caps:%s" % caps.to_string()) 106 (self.widthout, 107 self.heightout, 108 self.parout, 109 self.darout) = self._getValuesFromCaps(caps) 110 res = self.videobox.get_pad("src").set_caps(caps) 111 if res: 112 self.capsout = caps 113 self._computeAndSetValues() 114 return res
115
116 - def _sinkPadCapsNotifyCb(self, pad, unused_prop):
117 caps = pad.get_negotiated_caps() 118 self.log("caps:%r" % caps) 119 (self.widthin, 120 self.heightin, 121 self.parin, 122 self.darin) = self._getValuesFromCaps(caps) 123 self.capsin = caps 124 self._computeAndSetValues()
125
126 - def _srcPadCapsNotifyCb(self, pad, unused_prop):
127 caps = pad.get_negotiated_caps() 128 self.log("caps:%r" % caps) 129 (self.widthout, 130 self.heightout, 131 self.parout, 132 self.darout) = self._getValuesFromCaps(caps) 133 self.capsout = caps 134 self._computeAndSetValues()
135
136 - def _getValuesFromCaps(self, caps, force=False):
137 """ 138 returns (width, height, par, dar) from given caps. 139 If caps are None, or not negotiated, it will return 140 (-1, -1, gst.Fraction(1,1), gst.Fraction(1,1)) 141 """ 142 width = -1 143 height = -1 144 par = gst.Fraction(1, 1) 145 dar = gst.Fraction(1, 1) 146 if force or (caps and caps.is_fixed()): 147 struc = caps[0] 148 width = struc["width"] 149 height = struc["height"] 150 if struc.has_field('pixel-aspect-ratio'): 151 par = struc['pixel-aspect-ratio'] 152 dar = gst.Fraction(width * par.num, height * par.denom) 153 return (width, height, par, dar)
154
155 - def _computeAndSetValues(self):
156 """ Calculate the new values to set on capsfilter and videobox. """ 157 if (self.widthin == -1 or self.heightin == -1 or 158 self.widthout == -1 or self.heightout == -1): 159 # FIXME : should we reset videobox/capsfilter properties here ? 160 self.error( 161 "We don't have input and output caps, " 162 "we can't calculate videobox values") 163 return 164 165 self.log("incoming width/height/PAR/DAR : %d/%d/%r/%r" % ( 166 self.widthin, self.heightin, 167 self.parin, self.darin)) 168 self.log("outgoing width/height/PAR/DAR : %d/%d/%r/%r" % ( 169 self.widthout, self.heightout, 170 self.parout, self.darout)) 171 172 if self.darin == self.darout: 173 self.log( 174 "We have same input and output caps, " 175 "resetting capsfilter and videobox settings") 176 # same DAR, set inputcaps on capsfilter, reset videobox values 177 caps = gst.caps_new_any() 178 left = 0 179 right = 0 180 top = 0 181 bottom = 0 182 else: 183 par = self.parout 184 dar = self.darin 185 fdarin = float(self.darin.num) / float(self.darin.denom) 186 fdarout = float(self.darout.num) / float(self.darout.denom) 187 if fdarin > fdarout: 188 self.log("incoming DAR is greater that ougoing DAR. " 189 "Adding top/bottom borders") 190 # width, PAR stays the same as output 191 # calculate newheight = (PAR * widthout) / DAR 192 newheight = ((par.num * self.widthout * dar.denom) / 193 (par.denom * dar.num)) 194 self.log("newheight should be %d" % newheight) 195 extra = self.heightout - newheight 196 top = extra / 2 197 bottom = extra - top # compensate for odd extra 198 left = right = 0 199 # calculate filter caps 200 astr = "width=%d,height=%d" % (self.widthout, newheight) 201 else: 202 self.log("incoming DAR is smaller than outgoing DAR. " 203 "Adding left/right borders") 204 # height, PAR stays the same as output 205 # calculate newwidth = (DAR * heightout) / PAR 206 newwidth = ((dar.num * self.heightout * par.denom) / 207 (dar.denom * par.num)) 208 self.log("newwidth should be %d" % newwidth) 209 extra = self.widthout - newwidth 210 left = extra / 2 211 right = extra - left # compensate for odd extra 212 top = bottom = 0 213 # calculate filter caps 214 astr = "width=%d,height=%d" % (newwidth, self.heightout) 215 caps = gst.caps_from_string( 216 "video/x-raw-yuv,%s;video/x-raw-rgb,%s" % (astr, astr)) 217 218 # set properties on elements 219 self.debug( 220 "About to set left/right/top/bottom : %d/%d/%d/%d" % ( 221 -left, -right, -top, -bottom)) 222 self.videobox.props.left = -left 223 self.videobox.props.right = -right 224 self.videobox.props.top = -top 225 self.videobox.props.bottom = -bottom 226 self.debug("Settings filter caps %s" % caps.to_string()) 227 self.capsfilter.props.caps = caps 228 self.debug("done")
229 230 gobject.type_register(SmartVideoScale) 231