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