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