Package flumotion :: Package component :: Package effects :: Package deinterlace :: Module deinterlace
[hide private]

Source Code for Module flumotion.component.effects.deinterlace.deinterlace

  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  import gst 
 23  import gobject 
 24  from twisted.internet import reactor 
 25   
 26  from flumotion.component import feedcomponent 
 27   
 28  __version__ = "$Rev$" 
 29   
 30  GST_DEINTERLACER = "deinterlace" 
 31  FF_DEINTERLACER = "ffdeinterlace" 
 32  PASSTHROUGH_DEINTERLACER = "identity" 
 33   
 34  DEINTERLACE_MODE = [ 
 35      "auto", 
 36      "interlaced", 
 37      "disabled"] 
 38   
 39  DEINTERLACE_METHOD = { 
 40      # deinterlace2 methods 
 41      "tomsmocomp": GST_DEINTERLACER, 
 42      "greedyh": GST_DEINTERLACER, 
 43      "greedyl": GST_DEINTERLACER, 
 44      "vfir": GST_DEINTERLACER, 
 45      "linear": GST_DEINTERLACER, 
 46      "linearblend": GST_DEINTERLACER, 
 47      "scalerbob": GST_DEINTERLACER, 
 48      "weave": GST_DEINTERLACER, 
 49      "weavetff": GST_DEINTERLACER, 
 50      "weavebff": GST_DEINTERLACER, 
 51      # ffmpeg methods 
 52      "ffmpeg": FF_DEINTERLACER} 
 53   
 54   
55 -class DeinterlaceBin(gst.Bin):
56 """ 57 I am a GStreamer bin that can deinterlace a video stream from its 58 source pad using different methods. 59 """ 60 logCategory = "deinterlace" 61 DEFAULT_MODE = 'auto' 62 DEFAULT_METHOD = 'ffmpeg' 63 64 __gproperties__ = { 65 'keep-framerate': (gobject.TYPE_BOOLEAN, 'keeps the input framerate', 66 'keeps in the output the same framerate as in the output ' 67 'even if the deinterlacer changes it', 68 True, gobject.PARAM_READWRITE), 69 'mode': (gobject.TYPE_STRING, 'deinterlace mode', 70 'mode used to deinterlace incoming frames', 71 'auto', gobject.PARAM_READWRITE), 72 'method': (gobject.TYPE_STRING, 'deinterlace method', 73 'method/algorithm used to deinterlace incoming frames', 74 'ffmpeg', gobject.PARAM_READWRITE)} 75
76 - def __init__(self, mode, method):
77 gst.Bin.__init__(self) 78 79 self.keepFR = True 80 self.deinterlacerName = PASSTHROUGH_DEINTERLACER 81 self._interlaced = False 82 83 # Create elements 84 self._colorspace = gst.element_factory_make("ffmpegcolorspace") 85 self._colorfilter = gst.element_factory_make("capsfilter") 86 self._deinterlacer = gst.element_factory_make(PASSTHROUGH_DEINTERLACER) 87 self._deinterlacer.set_property('silent', True) 88 self._videorate = gst.element_factory_make("videorate") 89 self._ratefilter = gst.element_factory_make("capsfilter") 90 91 # Add elements to the bin 92 self.add(self._colorspace, self._colorfilter, self._deinterlacer, 93 self._videorate, self._ratefilter) 94 95 # FIXME: I420 is the only format support by the ffmpeg deinterlacer. 96 # Forcing it simplifies renegotiation issues if the input colorspace 97 # is different and the ffmpeg deinterlacer is added after the 98 # negotiation happened in a different colorspace. This makes this 99 # element not-passthrough. 100 self._colorfilter.set_property('caps', gst.Caps( 101 'video/x-raw-yuv, format=(fourcc)I420')) 102 103 # Link elements 104 self._colorspace.link(self._colorfilter) 105 self._colorfilter.link(self._deinterlacer) 106 self._deinterlacer.link(self._videorate) 107 self._videorate.link(self._ratefilter) 108 109 # Create source and sink pads 110 self._sinkPad = gst.GhostPad('sink', self._colorspace.get_pad('sink')) 111 self._srcPad = gst.GhostPad('src', self._ratefilter.get_pad('src')) 112 self.add_pad(self._sinkPad) 113 self.add_pad(self._srcPad) 114 115 # Store deinterlacer's sink and source peer pads 116 self._sinkPeerPad = self._colorspace.get_pad('src') 117 self._srcPeerPad = self._videorate.get_pad('sink') 118 119 # Add setcaps callback in the sink pad 120 self._sinkPad.set_setcaps_function(self._sinkSetCaps) 121 122 # Set the mode and method in the deinterlacer 123 self._setMethod(method) 124 self._setMode(mode)
125
126 - def isPassthrough(self):
127 return self.deinterlacerName == PASSTHROUGH_DEINTERLACER
128
129 - def _sinkSetCaps(self, pad, caps):
130 struct = caps[0] 131 # Set in the source pad the same framerate as in the sink pad 132 if self.keepFR: 133 try: 134 framerate = struct['framerate'] 135 except KeyError: 136 framerate = gst.Fraction(25, 1) 137 fr = '%s/%s' % (framerate.num, framerate.denom) 138 self._ratefilter.set_property('caps', gst.Caps( 139 'video/x-raw-yuv, framerate=%s;' 140 'video/x-raw-rgb, framerate=%s' % (fr, fr))) 141 # Detect if it's an interlaced stream using the 'interlaced' field 142 try: 143 interlaced = struct['interlaced'] 144 except KeyError: 145 interlaced = False 146 if interlaced == self._interlaced: 147 return True 148 else: 149 self.debug("Input is%sinterlaced" % 150 (interlaced and " " or " not ")) 151 self._interlaced = interlaced 152 # If we are in 'auto' mode and the interlaced field has changed, 153 # switch to the appropiate deinterlacer 154 if self.mode == 'auto': 155 if self._interlaced == True and self.isPassthrough(): 156 self._replaceDeinterlacer(self._sinkPeerPad, 157 DEINTERLACE_METHOD[self.method]) 158 elif self._interlaced == False and not self.isPassthrough(): 159 self._replaceDeinterlacer(self._sinkPeerPad, 160 PASSTHROUGH_DEINTERLACER) 161 return True
162
163 - def _replaceDeinterlacer(self, blockPad, deinterlacerName):
164 165 def unlinkAndReplace(Pad, blocked, deinterlacerName): 166 oldDeinterlacer = self._deinterlacer 167 self._deinterlacer = gst.element_factory_make(deinterlacerName) 168 if deinterlacerName == GST_DEINTERLACER: 169 self._deinterlacer.set_property("method", self.method) 170 elif deinterlacerName == PASSTHROUGH_DEINTERLACER: 171 self._deinterlacer.set_property("silent", True) 172 self._deinterlacer.set_state(gst.STATE_PLAYING) 173 self.add(self._deinterlacer) 174 # unlink the sink and source pad of the old deinterlacer 175 self._colorfilter.unlink(oldDeinterlacer) 176 oldDeinterlacer.unlink(self._videorate) 177 # remove the old deinterlacer from the bin 178 oldDeinterlacer.set_state(gst.STATE_NULL) 179 self.remove(oldDeinterlacer) 180 self._colorfilter.link(self._deinterlacer) 181 self._deinterlacer.link(self._videorate) 182 reactor.callFromThread(self._sinkPeerPad.set_blocked, False) 183 self.debug("%s has been replaced succesfully" % 184 self.deinterlacerName) 185 self.deinterlacerName = deinterlacerName
186 187 # We might be called from the streaming thread 188 self.debug("Replacing %s deinterlacer with %s:%s" % 189 (self.deinterlacerName, deinterlacerName, self.method)) 190 reactor.callFromThread(blockPad.set_blocked_async, 191 True, unlinkAndReplace, deinterlacerName)
192
193 - def _setMode(self, mode):
194 if mode not in DEINTERLACE_MODE: 195 raise AttributeError('unknown mode %s' % mode) 196 197 self.mode = mode 198 199 # If the new mode is 'disabled' use the passthrough deinterlacer 200 if self.mode == 'disabled': 201 if not self.isPassthrough(): 202 self._replaceDeinterlacer(self._sinkPeerPad, 203 PASSTHROUGH_DEINTERLACER) 204 # If the new mode is 'interlaced' force deinterlacing by replacing 205 # the deinterlacer if it was the passthrough one 206 elif self.mode == 'interlaced': 207 if self.isPassthrough(): 208 self._replaceDeinterlacer(self._sinkPeerPad, 209 DEINTERLACE_METHOD[self.method]) 210 # If the new mode is 'auto' replace the deinterlacer if the old one is 211 # passthough and the input content is interlaced 212 elif self.mode == 'auto': 213 if self._interlaced and self.isPassthrough(): 214 self._replaceDeinterlacer(self._sinkPeerPad, 215 DEINTERLACE_METHOD[self.method])
216
217 - def _setMethod(self, method):
218 if method not in DEINTERLACE_METHOD: 219 raise AttributeError('unknown mode %s' % method) 220 221 self.method = method 222 deinterlacerName = DEINTERLACE_METHOD[method] 223 if self.deinterlacerName == deinterlacerName: 224 # If the deinterlacer is 'deinterlace2', change 225 # the method property in the component 226 if self.deinterlacerName == GST_DEINTERLACER \ 227 and not self._passthrough: 228 self.debug("Changed method to %s" % method) 229 self._deinterlacer.set_property("method", method) 230 return 231 232 if not self.isPassthrough(): 233 # Replace the deinterlacer 234 self._replaceDeinterlacer(self._sinkPeerPad, deinterlacerName)
235
236 - def do_set_property(self, property, value):
237 if property.name == 'mode': 238 if value != self.mode: 239 self._setMode(value) 240 elif property.name == 'method': 241 if value != self.method: 242 self._setMethod(value) 243 elif property.name == 'keep-framerate': 244 self.keepFR = value 245 else: 246 raise AttributeError('uknown property %s' % property.name)
247
248 - def do_get_property(self, property):
249 if property.name == 'mode': 250 return self.mode 251 elif property.name == 'method': 252 return self.method 253 elif property.name == 'keep-framerate': 254 return self.keepFR 255 else: 256 raise AttributeError('uknown property %s' % property.name)
257 258
259 -class Deinterlace(feedcomponent.PostProcEffect):
260 """ 261 I am an effect that can be added to any component that has a deinterlacer 262 component and a way of changing the deinterlace method. 263 """ 264 logCategory = "deinterlace" 265
266 - def __init__(self, name, sourcePad, pipeline, mode, method):
267 """ 268 @param element: the video source element on which the post 269 processing effect will be added 270 @param pipeline: the pipeline of the element 271 @param mode: deinterlace mode 272 @param methid: deinterlace method 273 """ 274 feedcomponent.PostProcEffect.__init__(self, name, sourcePad, 275 DeinterlaceBin(mode, method), pipeline)
276
277 - def setUIState(self, state):
278 feedcomponent.Effect.setUIState(self, state) 279 if state: 280 for k in 'mode', 'method': 281 state.addKey('deinterlace-%s' % k, 282 self.effectBin.get_property(k))
283
284 - def effect_setMethod(self, method):
285 """ 286 Sets the deinterlacing method 287 288 @param value: the method to set to deinterlace 289 290 @return: the actual method set to deinterlace 291 """ 292 self.effectBin.set_property('method', method) 293 self.info('Changing deinterlacing method to %s', method) 294 # notify admin clients 295 self.uiState.set('deinterlace-method', method) 296 return method
297
298 - def effect_getMethod(self):
299 """ 300 Gets the deinterlacing method 301 302 @return: the method set for deinterlacing 303 @rtype: string 304 """ 305 return self.effectBin.get_property('method')
306
307 - def effect_setMode(self, mode):
308 """ 309 Sets the deinterlacing mode 310 311 @param value: the method to set to deinterlace 312 313 @return: the actual method set to deinterlace 314 """ 315 self.effectBin.set_property('mode', mode) 316 self.info('Changing deinterlacing mode to %s', mode) 317 # notify admin clients 318 self.uiState.set('deinterlace-mode', mode) 319 return mode
320
321 - def effect_getMode(self, mode):
322 """ 323 GetSets the deinterlacing method 324 325 @param value: the method used for deinterlacing 326 327 Returns: the actual method used to deinterlace 328 """ 329 return self.effectBin.get_property('mode')
330