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