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