def _pipelineInit(self, factory, sbin): self.spacing = 0 self.audioSink = ExtractionSink() self.audioSink.set_stopped_cb(self._finishSegment) # This audiorate element ensures that the extracted raw-data # timeline matches the timestamps used for seeking, even if the # audio source has gaps or other timestamp abnormalities. audiorate = gst.element_factory_make("audiorate") conv = gst.element_factory_make("audioconvert") q = gst.element_factory_make("queue") self.audioPipeline = pipeline({ sbin: audiorate, audiorate: conv, conv: q, q: self.audioSink, self.audioSink: None}) bus = self.audioPipeline.get_bus() bus.add_signal_watch() bus.connect("message::error", self._busMessageErrorCb) self._donecb_id = bus.connect("message::async-done", self._busMessageAsyncDoneCb) self.audioPipeline.set_state(gst.STATE_PAUSED)
class RandomAccessAudioExtractor(RandomAccessExtractor): """ L{Extractor} for random access audio streams. Closely inspired by L{RandomAccessAudioPreviewer}. """ def __init__(self, factory, stream_): self._queue = deque() RandomAccessExtractor.__init__(self, factory, stream_) self._ready = False def _pipelineInit(self, factory, sbin): self.spacing = 0 self.audioSink = ExtractionSink() self.audioSink.set_stopped_cb(self._finishSegment) # This audiorate element ensures that the extracted raw-data # timeline matches the timestamps used for seeking, even if the # audio source has gaps or other timestamp abnormalities. audiorate = gst.element_factory_make("audiorate") conv = gst.element_factory_make("audioconvert") q = gst.element_factory_make("queue") self.audioPipeline = pipeline({ sbin: audiorate, audiorate: conv, conv: q, q: self.audioSink, self.audioSink: None}) bus = self.audioPipeline.get_bus() bus.add_signal_watch() bus.connect("message::error", self._busMessageErrorCb) self._donecb_id = bus.connect("message::async-done", self._busMessageAsyncDoneCb) self.audioPipeline.set_state(gst.STATE_PAUSED) # The audiopipeline.set_state() method does not take effect # immediately, but the extraction process (and in particular # self._startSegment) will not work properly until # self.audioPipeline reaches the desired state (STATE_PAUSED). # To ensure that this is the case, we wait until the ASYNC_DONE # message is received before setting self._ready = True, # which enables extraction to proceed. def _busMessageErrorCb(self, bus, message): error, debug = message.parse_error() self.error("Event bus error: %s; %s", error, debug) return gst.BUS_PASS def _busMessageAsyncDoneCb(self, bus, message): self.debug("Pipeline is ready for seeking") bus.disconnect(self._donecb_id) # Don't call me again self._ready = True if self._queue: # Someone called .extract() before we were ready self._run() def _startSegment(self, timestamp, duration): self.debug("processing segment with timestamp=%i and duration=%i", timestamp, duration) res = self.audioPipeline.seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, timestamp, gst.SEEK_TYPE_SET, timestamp + duration) if not res: self.warning("seek failed %s", timestamp) self.audioPipeline.set_state(gst.STATE_PLAYING) return res def _finishSegment(self): self.audioSink.extractee.finalize() self.audioSink.reset() self._queue.popleft() # If there's more to do, keep running if self._queue: self._run() def extract(self, extractee, start, duration): stopped = not self._queue self._queue.append((extractee, start, duration)) if stopped and self._ready: self._run() # if self._ready is False, self._run() will be called from # self._busMessageDoneCb(). def _run(self): # Control flows in a cycle: # _run -> _startSegment -> busMessageSegmentDoneCb -> _finishSegment -> _run # This forms a loop that extracts an entire segment (i.e. satisfies an # extract request) in each cycle. The cycle # runs until the queue of Extractees empties. If the cycle is not # running, extract() will kick it off again. extractee, start, duration = self._queue[0] self.audioSink.set_extractee(extractee) self._startSegment(start, duration)