Package flumotion :: Package component :: Package producers :: Package playlist :: Module singledecodebin
[hide private]

Source Code for Module flumotion.component.producers.playlist.singledecodebin

  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  Single-stream queue-less decodebin 
 28  """ 
 29   
 30  import gobject 
 31  import gst 
 32   
 33  __version__ = "$Rev: 7827 $" 
 34   
 35   
36 -def find_upstream_demuxer_and_pad(pad):
37 while pad: 38 if pad.props.direction == gst.PAD_SRC \ 39 and isinstance(pad, gst.GhostPad): 40 pad = pad.get_target() 41 continue 42 43 if pad.props.direction == gst.PAD_SINK: 44 pad = pad.get_peer() 45 continue 46 47 element = pad.get_parent() 48 if isinstance(element, gst.Pad): 49 # pad is a proxy pad 50 element = element.get_parent() 51 52 if element is None: 53 pad = None 54 continue 55 56 element_factory = element.get_factory() 57 element_klass = element_factory.get_klass() 58 59 if 'Demuxer' in element_klass: 60 return element, pad 61 62 sink_pads = list(element.sink_pads()) 63 if len(sink_pads) > 1: 64 if element_factory.get_name() == 'multiqueue': 65 pad = element.get_pad(pad.get_name().replace('src', 'sink')) 66 else: 67 raise Exception('boom!') 68 69 elif len(sink_pads) == 0: 70 pad = None 71 else: 72 pad = sink_pads[0] 73 74 return None, None
75 76
77 -def get_type_from_decoder(decoder):
78 log.debug("stream", "%r" % decoder) 79 klass = decoder.get_factory().get_klass() 80 parts = klass.split('/', 2) 81 if len(parts) != 3: 82 return None 83 84 return parts[2].lower()
85 86
87 -def get_pad_id(pad):
88 lst = [] 89 while pad: 90 demuxer, pad = find_upstream_demuxer_and_pad(pad) 91 if (demuxer, pad) != (None, None): 92 lst.append([demuxer.get_factory().get_name(), pad.get_name()]) 93 94 # FIXME: we always follow back the first sink 95 try: 96 pad = list(demuxer.sink_pads())[0] 97 except IndexError: 98 pad = None 99 100 return lst
101 102
103 -def is_raw(caps):
104 """ returns True if the caps are RAW """ 105 rep = caps.to_string() 106 valid = ["video/x-raw", "audio/x-raw", "text/plain", "text/x-pango-markup"] 107 for val in valid: 108 if rep.startswith(val): 109 return True 110 return False
111 112
113 -class SingleDecodeBin(gst.Bin):
114 """ 115 A variant of decodebin. 116 117 * Only outputs one stream 118 * Doesn't contain any internal queue 119 """ 120 121 QUEUE_SIZE = 1 * gst.SECOND 122 123 __gsttemplates__ = ( 124 gst.PadTemplate("sinkpadtemplate", gst.PAD_SINK, gst.PAD_ALWAYS, 125 gst.caps_new_any()), 126 gst.PadTemplate("srcpadtemplate", gst.PAD_SRC, gst.PAD_SOMETIMES, 127 gst.caps_new_any())) 128
129 - def __init__(self, caps=None, uri=None, stream=None, *args, **kwargs):
130 gst.Bin.__init__(self, *args, **kwargs) 131 132 if not caps: 133 caps = gst.caps_new_any() 134 self.caps = caps 135 self.stream = stream 136 self.typefind = gst.element_factory_make("typefind", 137 "internal-typefind") 138 self.add(self.typefind) 139 140 self.uri = uri 141 if self.uri and gst.uri_is_valid(self.uri): 142 self.urisrc = gst.element_make_from_uri(gst.URI_SRC, uri, "urisrc") 143 self.log("created urisrc %s / %r" % (self.urisrc.get_name(), 144 self.urisrc)) 145 self.add(self.urisrc) 146 self.urisrc.link(self.typefind) 147 else: 148 self._sinkpad = gst.GhostPad("sink", self.typefind.get_pad("sink")) 149 self._sinkpad.set_active(True) 150 self.add_pad(self._sinkpad) 151 152 self.typefind.connect("have_type", self._typefindHaveTypeCb) 153 154 self._srcpad = None 155 156 self._dynamics = [] 157 158 self._validelements = [] #added elements 159 160 self._factories = self._getSortedFactoryList()
161 162 163 ## internal methods 164
165 - def _controlDynamicElement(self, element):
166 self.log("element:%s" % element.get_name()) 167 self._dynamics.append(element) 168 element.connect("pad-added", self._dynamicPadAddedCb) 169 element.connect("no-more-pads", self._dynamicNoMorePadsCb)
170
171 - def _getSortedFactoryList(self):
172 """ 173 Returns the list of demuxers, decoders and parsers available, sorted 174 by rank 175 """ 176 177 def _myfilter(fact): 178 if fact.get_rank() < 64: 179 return False 180 klass = fact.get_klass() 181 if not ("Demuxer" in klass or "Decoder" in klass \ 182 or "Parse" in klass): 183 return False 184 return True
185 reg = gst.registry_get_default() 186 res = [x for x in reg.get_feature_list(gst.ElementFactory) \ 187 if _myfilter(x)] 188 res.sort(lambda a, b: int(b.get_rank() - a.get_rank())) 189 return res
190
191 - def _findCompatibleFactory(self, caps):
192 """ 193 Returns a list of factories (sorted by rank) which can take caps as 194 input. Returns empty list if none are compatible 195 """ 196 self.debug("caps:%s" % caps.to_string()) 197 res = [] 198 for factory in self._factories: 199 for template in factory.get_static_pad_templates(): 200 if template.direction == gst.PAD_SINK: 201 intersect = caps.intersect(template.static_caps.get()) 202 if not intersect.is_empty(): 203 res.append(factory) 204 break 205 self.debug("returning %r" % res) 206 return res
207 239
240 - def _isDemuxer(self, element):
241 if not 'Demux' in element.get_factory().get_klass(): 242 return False 243 244 potential_src_pads = 0 245 for template in element.get_pad_template_list(): 246 if template.direction != gst.PAD_SRC: 247 continue 248 249 if template.presence == gst.PAD_REQUEST or \ 250 "%" in template.name_template: 251 potential_src_pads += 2 252 break 253 else: 254 potential_src_pads += 1 255 256 return potential_src_pads > 1
257
258 - def _plugDecodingQueue(self, pad):
259 queue = gst.element_factory_make("queue") 260 queue.props.max_size_time = self.QUEUE_SIZE 261 self.add(queue) 262 queue.sync_state_with_parent() 263 pad.link(queue.get_pad("sink")) 264 pad = queue.get_pad("src") 265 266 return pad
267
268 - def _tryToLink1(self, source, pad, factories):
269 """ 270 Tries to link one of the factories' element to the given pad. 271 272 Returns the element that was successfully linked to the pad. 273 """ 274 self.debug("source:%s, pad:%s , factories:%r" % (source.get_name(), 275 pad.get_name(), 276 factories)) 277 278 if self._isDemuxer(source): 279 pad = self._plugDecodingQueue(pad) 280 281 result = None 282 for factory in factories: 283 element = factory.create() 284 if not element: 285 self.warning("weren't able to create element from %r" % ( 286 factory)) 287 continue 288 289 sinkpad = element.get_pad("sink") 290 if not sinkpad: 291 continue 292 293 self.add(element) 294 element.set_state(gst.STATE_READY) 295 try: 296 pad.link(sinkpad) 297 except: 298 element.set_state(gst.STATE_NULL) 299 self.remove(element) 300 continue 301 302 self._closeLink(element) 303 element.set_state(gst.STATE_PAUSED) 304 305 result = element 306 break 307 308 return result
309 345
346 - def _wrapUp(self, element, pad):
347 """ 348 Ghost the given pad of element. 349 Remove non-used elements. 350 """ 351 352 if self._srcpad: 353 return 354 self._markValidElements(element) 355 self._removeUnusedElements(self.typefind) 356 self.log("ghosting pad %s" % pad.get_name()) 357 self._srcpad = gst.GhostPad("src", pad) 358 self._srcpad.set_active(True) 359 self.add_pad(self._srcpad) 360 self.post_message(gst.message_new_state_dirty(self))
361
362 - def _markValidElements(self, element):
363 """ 364 Mark this element and upstreams as valid 365 """ 366 self.log("element:%s" % element.get_name()) 367 if element == self.typefind: 368 return 369 self._validelements.append(element) 370 # find upstream element 371 pad = list(element.sink_pads())[0] 372 parent = pad.get_peer().get_parent() 373 self._markValidElements(parent)
374
375 - def _removeUnusedElements(self, element):
376 """ 377 Remove unused elements connected to srcpad(s) of element 378 """ 379 self.log("element:%r" % element) 380 for pad in element.src_pads(): 381 if pad.is_linked(): 382 peer = pad.get_peer().get_parent() 383 self._removeUnusedElements(peer) 384 if not peer in self._validelements: 385 self.log("removing %s" % peer.get_name()) 386 pad.unlink(pad.get_peer()) 387 peer.set_state(gst.STATE_NULL) 388 self.remove(peer)
389
390 - def _cleanUp(self):
391 self.log("") 392 if self._srcpad: 393 self.remove_pad(self._srcpad) 394 self._srcpad = None 395 for element in self._validelements: 396 element.set_state(gst.STATE_NULL) 397 self.remove(element) 398 self._validelements = []
399 400 ## Overrides 401
402 - def do_change_state(self, transition):
403 self.debug("transition:%r" % transition) 404 res = gst.Bin.do_change_state(self, transition) 405 if transition == gst.STATE_CHANGE_PAUSED_TO_READY: 406 self._cleanUp() 407 return res
408 409 ## Signal callbacks 410
411 - def _typefindHaveTypeCb(self, typefind, probability, caps):
412 self.debug("probability:%d, caps:%s" % (probability, caps.to_string())) 413 self._closePadLink(typefind, typefind.get_pad("src"), caps)
414 415 ## Dynamic element Callbacks 416
417 - def _dynamicPadAddedCb(self, element, pad):
418 self.log("element:%s, pad:%s" % (element.get_name(), pad.get_name())) 419 if not self._srcpad: 420 self._closePadLink(element, pad, pad.get_caps())
421
422 - def _dynamicNoMorePadsCb(self, element):
423 self.log("element:%s" % element.get_name())
424 425 gobject.type_register(SingleDecodeBin) 426