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: 6125 $" 
 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("videoscale", "smart-videoscale") 54 # set the scaling method to bilinear (cleaner) 55 # FIXME : we should figure out if better methods are available in the 56 # future, or ask the user which method he wants to use 57 # FIXME : Instead of having the set_caps() method, use proper caps negotiation 58 self.videoscale.props.method = 1 59 self.videobox = gst.element_factory_make("videobox", "smart-videobox") 60 self.capsfilter = gst.element_factory_make("capsfilter", "smart-capsfilter") 61 self.add(self.videoscale, self.capsfilter, self.videobox) 62 gst.element_link_many(self.videoscale, self.capsfilter, self.videobox) 63 64 self._sinkPad = gst.GhostPad("sink", self.videoscale.get_pad("sink")) 65 self._sinkPad.set_active(True) 66 self._srcPad = gst.GhostPad("src", self.videobox.get_pad("src")) 67 self._srcPad.set_active(True) 68 69 self.add_pad(self._sinkPad) 70 self.add_pad(self._srcPad) 71 72 self._sinkPad.set_setcaps_function(self._sinkSetCaps) 73 74 75 # input/output values 76 self.capsin = None 77 self.widthin = -1 78 self.heightin = -1 79 self.parin = gst.Fraction(1,1) 80 self.darin = gst.Fraction(1,1) 81 self.capsout = None 82 self.widthout = -1 83 self.heightout = -1 84 self.parout = gst.Fraction(1,1) 85 self.darout = gst.Fraction(1,1)
86
87 - def set_caps(self, caps):
88 """ set the outgoing caps, because gst.BaseTransform is full of CRACK ! """ 89 self.widthout, self.heightout, self.parout, self.darout = self._getValuesFromCaps(caps, True)
90
91 - def _sinkSetCaps(self, unused_pad, caps):
92 self.log("caps:%s" % caps.to_string()) 93 self.widthin, self.heightin, self.parin, self.darin = self._getValuesFromCaps(caps) 94 self._computeAndSetValues() 95 res = self.videoscale.get_pad("sink").set_caps(caps) 96 return res
97
98 - def _srcSetCaps(self, unused_pad, caps):
99 self.log("caps:%s" % caps.to_string()) 100 self.widthout, self.heightout, self.parout, self.darout = self._getValuesFromCaps(caps) 101 res = self.videobox.get_pad("src").set_caps(caps) 102 if res: 103 self.capsout = caps 104 self._computeAndSetValues() 105 return res
106
107 - def _sinkPadCapsNotifyCb(self, pad, unused_prop):
108 caps = pad.get_negotiated_caps() 109 self.log("caps:%r" % caps) 110 self.widthin, self.heightin, self.parin, self.darin = self._getValuesFromCaps(caps) 111 self.capsin = caps 112 self._computeAndSetValues()
113
114 - def _srcPadCapsNotifyCb(self, pad, unused_prop):
115 caps = pad.get_negotiated_caps() 116 self.log("caps:%r" % caps) 117 self.widthout, self.heightout, self.parout, self.darout = self._getValuesFromCaps(caps) 118 self.capsout = caps 119 self._computeAndSetValues()
120
121 - def _getValuesFromCaps(self, caps, force=False):
122 """ 123 returns (width, height, par, dar) from given caps. 124 If caps are None, or not negotiated, it will return 125 (-1, -1, gst.Fraction(1,1), gst.Fraction(1,1)) 126 """ 127 width = -1 128 height = -1 129 par = gst.Fraction(1,1) 130 dar = gst.Fraction(1,1) 131 if force or (caps and caps.is_fixed()): 132 struc = caps[0] 133 width = struc["width"] 134 height = struc["height"] 135 if struc.has_field('pixel-aspect-ratio'): 136 par = struc['pixel-aspect-ratio'] 137 dar = gst.Fraction(width * par.num, height * par.denom) 138 return (width, height, par, dar)
139
140 - def _computeAndSetValues(self):
141 """ Calculate the new values to set on capsfilter and videobox. """ 142 if self.widthin == -1 or self.heightin == -1 or self.widthout == -1 or self.heightout == -1: 143 # FIXME : should we reset videobox/capsfilter properties here ? 144 self.error("We don't have input and output caps, we can't calculate videobox values") 145 return 146 147 self.log("incoming width/height/PAR/DAR : %d/%d/%r/%r" % (self.widthin, self.heightin, 148 self.parin, self.darin)) 149 self.log("outgoing width/height/PAR/DAR : %d/%d/%r/%r" % (self.widthout, self.heightout, 150 self.parout, self.darout)) 151 152 if self.darin == self.darout: 153 self.log("We have same input and output caps, resetting capsfilter and videobox settings") 154 # same DAR, set inputcaps on capsfilter, reset videobox values 155 caps = gst.caps_new_any() 156 left = 0 157 right = 0 158 top = 0 159 bottom = 0 160 else: 161 par = self.parout 162 dar = self.darin 163 fdarin = float(self.darin.num) / float(self.darin.denom) 164 fdarout = float(self.darout.num) / float(self.darout.denom) 165 if fdarin > fdarout: 166 self.log("incoming DAR is greater that ougoing DAR. Adding top/bottom borders") 167 # width, PAR stays the same as output 168 # calculate newheight = (PAR * widthout) / DAR 169 newheight = (par.num * self.widthout * dar.denom) / (par.denom * dar.num) 170 self.log("newheight should be %d" % newheight) 171 extra = self.heightout - newheight 172 top = extra / 2 173 bottom = extra - top # compensate for odd extra 174 left = right = 0 175 # calculate filter caps 176 astr = "width=%d,height=%d" % (self.widthout, newheight) 177 else: 178 self.log("incoming DAR is smaller than outgoing DAR. Adding left/right borders") 179 # height, PAR stays the same as output 180 # calculate newwidth = (DAR * heightout) / PAR 181 newwidth = (dar.num * self.heightout * par.denom) / (dar.denom * par.num) 182 self.log("newwidth should be %d" % newwidth) 183 extra = self.widthout - newwidth 184 left = extra / 2 185 right = extra - left # compensate for odd extra 186 top = bottom = 0 187 # calculate filter caps 188 astr = "width=%d,height=%d" % (newwidth, self.heightout) 189 caps = gst.caps_from_string("video/x-raw-yuv,%s;video/x-raw-rgb,%s" % (astr, astr)) 190 191 # set properties on elements 192 self.debug("About to set left/right/top/bottom : %d/%d/%d/%d" % (-left, -right, -top, -bottom)) 193 self.videobox.props.left = -left 194 self.videobox.props.right = -right 195 self.videobox.props.top = -top 196 self.videobox.props.bottom = -bottom 197 self.debug("Settings filter caps %s" % caps.to_string()) 198 self.capsfilter.props.caps = caps 199 self.debug("done")
200 201 202 203 gobject.type_register(SmartVideoScale) 204