Trees | Indices | Help |
---|
|
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 25 from flumotion.component import decodercomponent as dc 26 from flumotion.common import messages 27 from flumotion.common.i18n import N_, gettexter 28 29 T_ = gettexter() 30 31 __version__ = "$Rev: 7162 $" 32 33 BASIC_AUDIO_CAPS = "audio/x-raw-int;audio/x-raw-float" 34 BASIC_VIDEO_CAPS = "video/x-raw-yuv;video/x-raw-rgb" 35 36 # FIXME: The GstAutoplugSelectResult enum has no bindings in gst-python. 37 # Replace this when the enum is exposed in the bindings. 38 39 GST_AUTOPLUG_SELECT_TRY = 0 40 GST_AUTOPLUG_SELECT_SKIP = 2 41 42 48 4951 __gstdetails__ = ('SyncKeeper', 'Generic', 52 'Retimestamp the output to be contiguous and maintain ' 53 'the sync', 'Xavier Queralt') 54 _audiosink = gst.PadTemplate("audio-in", 55 gst.PAD_SINK, 56 gst.PAD_ALWAYS, 57 gst.caps_from_string(BASIC_AUDIO_CAPS)) 58 _videosink = gst.PadTemplate("video-in", 59 gst.PAD_SINK, 60 gst.PAD_ALWAYS, 61 gst.caps_from_string(BASIC_VIDEO_CAPS)) 62 _audiosrc = gst.PadTemplate("audio-out", 63 gst.PAD_SRC, 64 gst.PAD_ALWAYS, 65 gst.caps_from_string(BASIC_AUDIO_CAPS)) 66 _videosrc = gst.PadTemplate("video-out", 67 gst.PAD_SRC, 68 gst.PAD_ALWAYS, 69 gst.caps_from_string(BASIC_VIDEO_CAPS)) 70 71 nextVideoTs = 0 72 nextAudioTs = 0 73 videoReceived = False 74 audioReceived = False 75 audioDiscontBuffer = None 76 videoDiscontBuffer = None 77 78 sendVideoNewSegment = True 79 sendAudioNewSegment = True 80 videoPadding = 0 81 audioPadding = 0 82 resetReceived = False 83201 202 gobject.type_register(SyncKeeper) 203 gst.element_register(SyncKeeper, "synckeeper", gst.RANK_MARGINAL) 204 20585 gst.Element.__init__(self) 86 87 self.audiosink = gst.Pad(self._audiosink, "audio-in") 88 self.audiosink.set_chain_function(self.chainfunc) 89 self.audiosink.set_event_function(self.eventfunc) 90 self.add_pad(self.audiosink) 91 self.videosink = gst.Pad(self._videosink, "video-in") 92 self.videosink.set_chain_function(self.chainfunc) 93 self.videosink.set_event_function(self.eventfunc) 94 self.add_pad(self.videosink) 95 96 self.audiosrc = gst.Pad(self._audiosrc, "audio-out") 97 self.add_pad(self.audiosrc) 98 self.videosrc = gst.Pad(self._videosrc, "video-out") 99 self.add_pad(self.videosrc)100102 buffer.timestamp += self.videoPadding 103 self.nextVideoTs = buffer.timestamp + buffer.duration 104 if self.sendVideoNewSegment: 105 self.videosrc.push_event( 106 gst.event_new_new_segment(True, 1.0, gst.FORMAT_TIME, 107 buffer.timestamp, -1, 0)) 108 self.sendVideoNewSegment = False 109 110 self.log( 111 "Output video timestamp: %s, %s" % (gst.TIME_ARGS(buffer.timestamp), 112 gst.TIME_ARGS(buffer.duration))) 113 self.videosrc.push(buffer)114116 buffer.timestamp += self.audioPadding 117 self.nextAudioTs = buffer.timestamp + buffer.duration 118 if self.sendAudioNewSegment: 119 self.audiosrc.push_event( 120 gst.event_new_new_segment(True, 1.0, gst.FORMAT_TIME, 121 buffer.timestamp, -1, 0)) 122 self.sendAudioNewSegment = False 123 124 self.log( 125 "Output audio timestamp: %s, %s" % (gst.TIME_ARGS(buffer.timestamp), 126 gst.TIME_ARGS(buffer.duration))) 127 self.audiosrc.push(buffer)128130 if not self.resetReceived: 131 return False 132 if not videoBuffer or not audioBuffer: 133 return False 134 diff = audioBuffer.timestamp - videoBuffer.timestamp 135 newStart = max(self.nextVideoTs, self.nextAudioTs) 136 137 if diff > 0: 138 self.videoPadding = newStart - videoBuffer.timestamp 139 self.audioPadding = newStart + diff - audioBuffer.timestamp 140 else: 141 self.videoPadding = newStart + diff - videoBuffer.timestamp 142 self.audioPadding = newStart - audioBuffer.timestamp 143 144 self.resetReceived = False 145 146 return True147149 if pad.get_name() == 'audio-in': 150 self.log( 151 "Input audio timestamp: %s, %s" % (gst.TIME_ARGS(buffer.timestamp), 152 gst.TIME_ARGS(buffer.duration))) 153 if self.fixAVPadding(self.videoDiscontBuffer, buffer): 154 self.push_video_buffer(self.videoDiscontBuffer) 155 self.videoDiscontBuffer = None 156 157 # Check contiguous buffer 158 self.audioReceived = True 159 if self.videoReceived: 160 self.push_audio_buffer(buffer) 161 elif self.resetReceived: 162 self.audioDiscontBuffer = buffer 163 164 return gst.FLOW_OK 165 elif pad.get_name() == 'video-in': 166 self.log( 167 "Input video timestamp: %s, %s" % (gst.TIME_ARGS(buffer.timestamp), 168 gst.TIME_ARGS(buffer.duration))) 169 170 if self.fixAVPadding(buffer, self.audioDiscontBuffer): 171 self.push_audio_buffer(self.audioDiscontBuffer) 172 self.audioDiscontBuffer = None 173 174 self.videoReceived = True 175 if self.audioReceived: 176 self.push_video_buffer(buffer) 177 elif self.resetReceived: 178 self.videoDiscontBuffer = buffer 179 180 return gst.FLOW_OK 181 else: 182 return gst.FLOW_ERROR183185 self.debug("Received event %r from %s" % (event, event.src)) 186 if event.type == gst.EVENT_NEWSEGMENT: 187 return False 188 if event.type != gst.EVENT_CUSTOM_DOWNSTREAM: 189 return True 190 if event.get_structure().get_name() != 'flumotion-reset': 191 return True 192 self.resetReceived = True 193 self.videoReceived = False 194 self.audioReceived = False 195 self.sendVideoNewSegment = True 196 self.sendAudioNewSegment = True 197 198 self.audiosrc.push_event(event) 199 self.videosrc.push_event(event) 200 return True207 """ 208 Generic decoder component using decodebin2. 209 210 It listen to the custom gstreamer event flumotion-reset, 211 and reset the decoding pipeline by removing the old one 212 and creating a new one. 213 214 Sub-classes must override _get_feeders_info() and return 215 a list of FeederInfo instances that describe the decoder 216 output. 217 218 When reset, if the new decoded pads do not match the 219 previously negotiated caps, feeder will not be connected, 220 and the decoder will go sad. 221 """ 222 223 logCategory = "gen-decoder" 224 feeder_tmpl = ("identity name=%(ename)s single-segment=true " 225 "silent=true ! %(caps)s ! @feeder:%(pad)s@") 226 227 ### Public Methods ### 228283 284230 self._feeders_info = None # {FEEDER_NAME: FeederInfo}231233 # Retrieve feeder info and build a dict out of it 234 finfo = self._get_feeders_info() 235 assert finfo, "No feeder info specified" 236 self._feeders_info = dict([(i.name, i) for i in finfo]) 237 238 pipeline_parts = [self._get_base_pipeline_string()] 239 240 for i in self._feeders_info.values(): 241 ename = self._get_output_element_name(i.name) 242 pipeline_parts.append( 243 self.feeder_tmpl % dict(ename=ename, caps=i.caps, pad=i.name)) 244 245 pipeline_str = " ".join(pipeline_parts) 246 self.log("Decoder pipeline: %s", pipeline_str) 247 248 self._blacklist = properties.get('blacklist', []) 249 250 return pipeline_str251253 dc.DecoderComponent.configure_pipeline(self, pipeline, 254 properties) 255 256 decoder = self.pipeline.get_by_name("decoder") 257 decoder.connect('autoplug-select', self._autoplug_select_cb)258 259 ### Protected Methods ## 260 263 269 270 ### Private Methods ### 271 274 275 ### Callbacks ### 276278 if factory.get_name() in self._blacklist: 279 self.log("Skipping element %s because it's in the blacklist", 280 factory.get_name()) 281 return GST_AUTOPLUG_SELECT_SKIP 282 return GST_AUTOPLUG_SELECT_TRY286 287 logCategory = "sgen-decoder" 288 289 _caps_lookup = {'audio': BASIC_AUDIO_CAPS, 290 'video': BASIC_VIDEO_CAPS} 291 294307 308296 media_type = properties.get("media-type") 297 if media_type not in ["audio", "video"]: 298 msg = 'Property media-type can only be "audio" or "video"' 299 m = messages.Error(T_(N_(msg)), mid="error-decoder-media-type") 300 addMessage(m) 301 else: 302 self._media_type = media_type303310 311 logCategory = "avgen-decoder" 312 feeder_tmpl = ("identity name=%(ename)s silent=true ! %(caps)s ! " 313 "sync.%(pad)s-in sync.%(pad)s-out ! @feeder:%(pad)s@") 314 318321
Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 on Sat Sep 10 08:59:38 2011 | http://epydoc.sourceforge.net |