def _bufferprobe(self, pad, inbuf): start = inbuf.timestamp if start == gst.CLOCK_TIME_NONE: pad.warning("Got buffer without timestamp ! Forwarding") return True if inbuf.duration == gst.CLOCK_TIME_NONE: stop = inbuf.timestamp + (inbuf.size * gst.SECOND) / self.bitrate else: stop = inbuf.timestamp + inbuf.duration pad.debug("inbuf %s %s" % (gst.TIME_ARGS(start), gst.TIME_ARGS(stop))) clip, nstart, nstop = self.segment.clip(gst.FORMAT_TIME, start, stop) pad.debug("clip:%r, nstart:%s, nstop:%s" % (clip, gst.TIME_ARGS(nstart), gst.TIME_ARGS(nstop))) if clip is True: if nstart != start or nstop != stop: pad.debug("clipping") nduration = nstop - nstart # clip the buffer offset = (nstart - start) * self.bitrate # size nsize = nduration * self.bitrate pad.debug("changing data") #inbuf.data = inbuf.data[offset:offset+nsize] #inbuf = inbuf.create_sub(offset, nsize) inbuf.timestamp = nstart inbuf.duration = nstop - nstart pad.debug("buffer clipped") return False return clip
def _chain(self, pad, inbuf): pad.debug("inbuf %r %s %s" % (inbuf, gst.TIME_ARGS( inbuf.timestamp), gst.TIME_ARGS(inbuf.duration))) start = inbuf.timestamp if inbuf.duration == gst.CLOCK_TIME_NONE: stop = inbuf.timestamp + (inbuf.size * gst.SECOND) / self.bitrate else: stop = inbuf.timestamp + inbuf.duration clip, nstart, nstop = self.segment.clip(gst.FORMAT_TIME, start, stop) self.debug("clip:%r, nstart:%s, nstop:%s" % (clip, gst.TIME_ARGS(nstart), gst.TIME_ARGS(nstop))) if clip is True: if nstart != start and nstop != stop: self.debug("clipping") nduration = nstop - nstart # clip the buffer offset = (nstart - start) * self.bitrate # size nsize = nduration * self.bitrate b2 = inbuf.create_sub(offset, nsize) b2.timestamp = nstart b2.duration = nstop - nstart self.debug("buffer clipped") return self.srcpad.push(b2) self.debug("buffer untouched, just pushing forward") return self.srcpad.push(inbuf) self.debug("buffer dropped") return gst.FLOW_OK
def _src_event(self, unused_pad, event): # for the moment we just push it upstream self.debug("event %r" % event) if event.type == gst.EVENT_SEEK: flags = event.parse_seek()[2] self.debug("Handling seek event %r" % flags) if flags & gst.SEEK_FLAG_FLUSH: self.debug("sending flush_start event") self.srcpad.push_event(gst.event_new_flush_start()) self._segment.set_seek(*event.parse_seek()) self.debug("_segment start:%s stop:%s" % (gst.TIME_ARGS( self._segment.start), gst.TIME_ARGS(self._segment.stop))) # create a new initial seek self.debug("pausing task") self.srcpad.pause_task() self.debug("task paused") self._needsegment = True self.debug("Sending FLUS_STOP event") if flags & gst.SEEK_FLAG_FLUSH: self.srcpad.push_event(gst.event_new_flush_stop()) self.debug("Restarting our task") self.srcpad.start_task(self._our_task) self.debug("Returning True") return True return self.sinkpad.push_event(event)
def chainfunc(self, pad, buf, srcpad): self.log("Input %s timestamp: %s, %s" % (srcpad is self.audiosrc and 'audio' or 'video', gst.TIME_ARGS(buf.timestamp), gst.TIME_ARGS(buf.duration))) if not self._sendNewSegment: self._send_new_segment() try: self._lock.acquire() # Discard buffers outside the configured segment if buf.timestamp < self._syncOffset: self.warning("Could not clip buffer to segment") return gst.FLOW_OK if buf.timestamp == gst.CLOCK_TIME_NONE: return gst.FLOW_OK # Get the input stream time of the buffer buf.timestamp -= self._syncOffset # Set the accumulated stream time buf.timestamp += self._syncTimestamp duration = 0 if buf.duration != gst.CLOCK_TIME_NONE: duration = buf.duration self._totalTime = max(buf.timestamp + duration, self._totalTime) self.log( "Output %s timestamp: %s, %s" % (srcpad is self.audiosrc and 'audio' or 'video', gst.TIME_ARGS(buf.timestamp), gst.TIME_ARGS(buf.duration))) finally: self._lock.release() srcpad.push(buf) return gst.FLOW_OK
def timestampDiscont(): prevTs = s["prev-timestamp"] prevDuration = s["prev-duration"] curTs = s["cur-timestamp"] if prevTs == gst.CLOCK_TIME_NONE: self.debug("no previous timestamp") return if prevDuration == gst.CLOCK_TIME_NONE: self.debug("no previous duration") return if curTs == gst.CLOCK_TIME_NONE: self.debug("no current timestamp") return discont = curTs - (prevTs + prevDuration) dSeconds = discont / float(gst.SECOND) self.debug( "we have a discont on eater %s of %.9f s " "between %s and %s ", eater.eaterAlias, dSeconds, gst.TIME_ARGS(prevTs + prevDuration), gst.TIME_ARGS(curTs)) eater.timestampDiscont(dSeconds, float(curTs) / float(gst.SECOND))
def _keyframeTimeValueChanged(self, kf, ptime, value): self.debug("kf.time:%s, ptime:%s, value:%r", gst.TIME_ARGS(kf.time), gst.TIME_ARGS(ptime), value) if kf.time != ptime: self._controller.unset(self._property.name, kf.time) self._controller.set(self._property.name, ptime, value) self.emit("keyframe-moved", kf)
def data_probe(pad, data, section=""): """Callback to use for gst.Pad.add_*_probe. The extra argument will be used to prefix the debug messages """ if section == "": section = "%s:%s" % (pad.get_parent().get_name(), pad.get_name()) if isinstance(data, gst.Buffer): log.debug( "probe", "%s BUFFER timestamp:%s , duration:%s , size:%d , offset:%d , offset_end:%d", section, gst.TIME_ARGS(data.timestamp), gst.TIME_ARGS(data.duration), data.size, data.offset, data.offset_end) if data.flags & gst.BUFFER_FLAG_DELTA_UNIT: log.debug("probe", "%s DELTA_UNIT", section) if data.flags & gst.BUFFER_FLAG_DISCONT: log.debug("probe", "%s DISCONT", section) if data.flags & gst.BUFFER_FLAG_GAP: log.debug("probe", "%s GAP", section) log.debug("probe", "%s flags:%r", section, data.flags) else: log.debug("probe", "%s EVENT %s", section, data.type) if data.type == gst.EVENT_NEWSEGMENT: upd, rat, fmt, start, stop, pos = data.parse_new_segment() log.debug( "probe", "%s Update:%r rate:%f fmt:%s, start:%s, stop:%s, pos:%s", section, upd, rat, fmt, gst.TIME_ARGS(start), gst.TIME_ARGS(stop), gst.TIME_ARGS(pos)) return True
def _our_task(self, something): if self._buffer == None: self.warning("We were started without a buffer, exiting") self.srcpad.pause_task() return #this is where we repeatedly output our buffer self.debug("self:%r, something:%r" % (self, something)) self.debug("needsegment: %r" % self._needsegment) if self._needsegment: self.debug("Need to output a new segment") segment = gst.event_new_new_segment(False, self._segment.rate, self._segment.format, self._segment.start, self._segment.stop, self._segment.start) self.srcpad.push_event(segment) # calculate offset # offset is int(segment.start / outputrate) self._offset = int(self._segment.start * self._outputrate.num / self._outputrate.denom / gst.SECOND) self._needsegment = False self.debug("Newsegment event pushed") # new position position = self._offset * gst.SECOND * self._outputrate.denom / self._outputrate.num if self._segment.stop != -1 and position > self._segment.stop: self.debug( "end of configured segment (position:%s / segment_stop:%s)" % (gst.TIME_ARGS(position), gst.TIME_ARGS(self._segment.stop))) # end of stream/segment handling if self._segment.flags & gst.SEEK_FLAG_SEGMENT: # emit a gst.MESSAGE_SEGMENT_DONE self.post_message( gst.message_new_segment_done(self, gst.FORMAT_TIME, self._segment.stop)) else: self.srcpad.push_event(gst.event_new_eos()) self.last_return = gst.FLOW_WRONG_STATE self.srcpad.pause_task() # we need to update the caps here ! obuf = self._buffer.make_metadata_writable() ok, nstart, nstop = self._segment.clip(gst.FORMAT_TIME, position, position + self._bufferduration) if ok: obuf.timestamp = nstart obuf.duration = nstop - nstart obuf.caps = self._srccaps self.debug("Pushing out buffer %s" % gst.TIME_ARGS(obuf.timestamp)) self.last_return = self.srcpad.push(obuf) self._offset += 1 if self.last_return != gst.FLOW_OK: self.debug("Pausing ourself, last_return : %s" % gst.flow_get_name(self.last_return)) self.srcpad.pause_task()
def _dataprobe(unused_pad, data): if isinstance(data, gst.Buffer): print "Buffer", gst.TIME_ARGS(data.timestamp), gst.TIME_ARGS( data.duration), data.caps.to_string() else: print "Event", data.type if data.type == gst.EVENT_NEWSEGMENT: print data.parse_new_segment() return True
def _sink_chain(self, unused_pad, buf): self.debug("buffer %s %s" % (gst.TIME_ARGS(buf.timestamp), gst.TIME_ARGS(buf.duration))) if self._buffer != None: self.debug( "already have a buffer ! Returning GST_FLOW_WRONG_STATE") return gst.FLOW_WRONG_STATE self._buffer = buf self.srcpad.start_task(self._our_task) return gst.FLOW_WRONG_STATE
def _update_sync_point(self, start, position): # Only update the sync point if we haven't received any buffer # (totalTime == 0) or we received a reset if not self._totalTime and not self._resetReceived: return self._syncTimestamp = self._totalTime if position >= start: self._syncOffset = start + (position - start) else: self._syncOffset = start self._resetReceived = False self.info("Update sync point to % r, offset to %r" % (gst.TIME_ARGS(self._syncTimestamp), (gst.TIME_ARGS(self._syncOffset))))
def switch(self, padname): switch = self.pipeline.get_by_name('s') stop_time = switch.emit('block') newpad = switch.get_static_pad(padname) start_time = newpad.get_property('running-time') gst.warning('stop time = %d' % (stop_time,)) gst.warning('stop time = %s' % (gst.TIME_ARGS(stop_time),)) gst.warning('start time = %d' % (start_time,)) gst.warning('start time = %s' % (gst.TIME_ARGS(start_time),)) gst.warning('switching from %r to %r' % (switch.get_property('active-pad'), padname)) switch.emit('switch', newpad, stop_time, start_time)
def seek(self, position, format=gst.FORMAT_TIME): """ Seeks in the L{Pipeline} to the given position. @param position: Position to seek to @type position: L{long} @param format: The C{Format} of the seek position @type format: C{gst.Format} @raise PipelineError: If seek failed """ if format == gst.FORMAT_TIME: self.debug("position : %s", gst.TIME_ARGS(position)) else: self.debug("position : %d , format:%d", position, format) # FIXME : temporarily deactivate position listener #self._listenToPosition(False) # clamp between [0, duration] if format == gst.FORMAT_TIME: position = max(0, min(position, self.getDuration())) res = self._pipeline.seek(1.0, format, gst.SEEK_FLAG_FLUSH, gst.SEEK_TYPE_SET, position, gst.SEEK_TYPE_NONE, -1) if not res: self.debug("seeking failed") raise PipelineError("seek failed") self.debug("seeking succesfull") self.emit('position', position)
def _append_file(self, location): """ Appends the given file to the composition, along with the proper mixer effect """ self.debug("location:%s" % location) start = self._lastposition if self._lastadded: start += self._levels[self._lastadded][2] start -= self._levels[location][1] gnls = self._new_gnl_source(location, start) self._composition.add(gnls) if self._lastadded: # create the mixer duration = self._levels[self._lastadded][3] - self._levels[ self._lastadded][2] + self._levels[location][1] mixer = self._new_mixer(start, duration) self._composition.add(mixer) self._lastposition = start self._lastadded = location self.debug("lastposition:%s , lastadded:%s" % (gst.TIME_ARGS(self._lastposition), self._lastadded))
def _process_fragment(self, fragment): if not self._stream_setup: sink = self.get_element("sink") pad = sink.get_pad("sink") caps = pad.get_negotiated_caps() name = caps.get_structure(0).get_name() self._setup_stream_type(name) self._fragmentsCount = self._fragmentsCount + 1 # Wait hls-min-window fragments to set the component 'happy' if self._fragmentsCount == self._minWindow: self.info("%d fragments received. Changing mood to 'happy'", self._fragmentsCount) self.setMood(moods.happy) self._ready = True b = fragment.get_property('buffer') index = fragment.get_property('index') duration = fragment.get_property('duration') if index < self._last_index: self.warning( "Found a discontinuity last index is %s but current " "one is %s", self._last_index, index) self.soft_restart() fragName = self.hlsring.addFragment( b.data, index, round(duration / float(gst.SECOND))) self.info('Added fragment "%s", index=%s, duration=%s', fragName, index, gst.TIME_ARGS(duration))
def _startThumbnail(self, timestamp): RandomAccessPreviewer._startThumbnail(self, timestamp) self.log("timestamp : %s", gst.TIME_ARGS(timestamp)) self.videopipeline.seek(1.0, gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE, gst.SEEK_TYPE_SET, timestamp, gst.SEEK_TYPE_NONE, -1)
def probe_cb(pad, buffer): """ Periodically scheduled buffer probe, that ensures that we're currently actually having dataflow through our eater elements. Called from GStreamer threads. @param pad: The gst.Pad srcpad for one eater in this component. @param buffer: A gst.Buffer that has arrived on this pad """ self._last_data_time = time.time() self.logMessage('buffer probe on %s has timestamp %s', self.name, gst.TIME_ARGS(buffer.timestamp)) deferred, probe_id = self._probe_id.pop("id", (None, None)) if probe_id: # This will be None only if detach() has been called. self._pad.remove_buffer_probe(probe_id) reactor.callFromThread(deferred.callback, None) # Data received! Return to happy ASAP: reactor.callFromThread(self.watch_poller.run) self._first = False # let the buffer through return True
def _newTime(self, value, frame=-1): self.info("value:%s, frame:%d", gst.TIME_ARGS(value), frame) self.current_time = value self.current_frame = frame self.timelabel.set_markup("<tt>%s</tt>" % time_to_string(value)) if not self.moving_slider: self.posadjust.set_value(float(value)) return False
def on_message_application(cutter, message, loop): global count s = message.structure which = 'below' if s['above']: which = 'above' print "%s: %s threshold" % (gst.TIME_ARGS(s['timestamp']), which) if s['above']: count += 1 if count > 2: loop.quit()
def _newTime(self, value, frame=-1): self.info("value:%s, frame:%d", gst.TIME_ARGS(value), frame) self.current_time = value self.current_frame = frame self.timecode_entry.setWidgetValue(value, False) if not self.moving_slider: self.posadjust.set_value(float(value)) return False
def do_switch(self, feed=None): if feed == None: feed = self.idealFeed self.clearWarning('temporary-switch-problem') if feed == self.activeFeed: self.debug("already streaming from feed %r", feed) return if feed not in self.logicalFeeds: self.warning("unknown logical feed: %s", feed) return # (pad, switch) pairs = [self.switchPads[alias] for alias in self.logicalFeeds[feed]] stop_times = [e.emit('block') for p, e in pairs] start_times = [p.get_property('running-time') for p, e in pairs] stop_time = max(stop_times) self.debug('stop time = %d', stop_time) self.debug('stop time = %s', gst.TIME_ARGS(stop_time)) if stop_time != gst.CLOCK_TIME_NONE: diff = float(max(stop_times) - min(stop_times)) if diff > gst.SECOND * 10: fmt = N_("When switching to %s, feed timestamps out" " of sync by %us") self.addWarning('large-timestamp-difference', fmt, feed, diff / gst.SECOND, priority=40) start_time = min(start_times) self.debug('start time = %s', gst.TIME_ARGS(start_time)) self.debug('switching from %r to %r', self.activeFeed, feed) for p, e in pairs: self.debug("switching to pad %r", p) e.emit('switch', p, stop_time, start_time) self.activeFeed = feed self.uiState.set("active-eater", feed)
def do_render(self, buf): self.log("buffer %s %d" % (gst.TIME_ARGS(buf.timestamp), len(buf.data))) b = array.array("B") b.fromstring(buf) pixb = cairo.ImageSurface.create_for_data(b, cairo.FORMAT_ARGB32, self.width, self.height, self.width * 4) self.emit('thumbnail', pixb, buf.timestamp) return gst.FLOW_OK
def _videoPadSeekCb(self, pad): try: duration = self.pipeline.query_duration(gst.FORMAT_TIME)[0] except gst.QueryError: duration = 0 self.debug("doing thumbnail seek at %s", gst.TIME_ARGS(duration)) if duration: self.pipeline.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH, duration / 3) pad.set_blocked_async(False, self._videoPadBlockCb)
def _leveller_done_cb(self, l, reason, file): if reason != sources.EOS: gst.debug("Error: %s" % reason) return gst.debug("in: %s, out: %s" % (gst.TIME_ARGS(l.mixin), gst.TIME_ARGS(l.mixout))) gst.debug("rms: %f, %f dB" % (l.rms, l.rmsdB)) # store infos self._levels[file] = (l.rms, l.mixin, l.mixout, l.length) gst.debug("writing level pickle") file = open(self._picklepath, "w") pickle.dump(self._levels, file) file.close() self._check_prerolled() self._scan() # clean up leveller after this handler gobject.timeout_add(0, l.clean)
def splitObject(self, position): start = self.gnl_object.props.start duration = self.gnl_object.props.duration if position <= start or position >= start + duration: raise TrackError("can't split at position %s" % gst.TIME_ARGS(position)) other = self.copy() other.trimObjectStart(position) self.setObjectDuration(position - self.gnl_object.props.start) self.setObjectMediaDuration(position - self.gnl_object.props.start) return other
def _durationChangedCb(self, unused_pipeline, duration): self.debug("duration : %s", gst.TIME_ARGS(duration)) position = self.posadjust.get_value() if duration < position: self.posadjust.set_value(float(duration)) self.posadjust.upper = float(duration) if duration == 0: self._setUiActive(False) else: self._setUiActive(True) if self._initial_seek is not None: seek, self._initial_seek = self._initial_seek, None self.pipeline.seek(seek)
def _seekTimeoutCb(self): self.pending_seek_id = None if self.position != None and self.format != None: position, self.position = self.position, None format, self.format = self.format, None try: self.emit('seek', position, format) except: log.doLog(log.ERROR, None, "seeker", "Error while seeking to position:%s format:%r", (gst.TIME_ARGS(position), format)) # if an exception happened while seeking, properly # reset ourselves return False return False
def _timelineSeekCb(self, ruler, position, format): self.debug("position:%s", gst.TIME_ARGS (position)) if self.viewer.action != self.project.view_action: self.viewer.setPipeline(None) self.viewer.hideSlider() self.viewer.setAction(self.project.view_action) self.viewer.setPipeline(self.project.pipeline) # get the pipeline settings and set the DAR of the viewer sett = self.project.getSettings() self.viewer.setDisplayAspectRatio(float(sett.videopar * sett.videowidth) / float(sett.videoheight)) # everything above only needs to be done if the viewer isn't already # set to the pipeline. self.project.pipeline.pause() try: self.project.pipeline.seek(position, format) except: self.debug("Seeking failed")
def do_render(self, buf): self.log("buffer %s %d" % (gst.TIME_ARGS(buf.timestamp), len(buf.data))) b = array.array("b") b.fromstring(buf) pixb = cairo.ImageSurface.create_for_data( b, # We don't use FORMAT_ARGB32 because Cairo uses premultiplied # alpha, and gstreamer does not. Discarding the alpha channel # is not ideal, but the alternative would be to compute the # conversion in python (slow!). cairo.FORMAT_RGB24, self.width, self.height, self.width * 4) self.emit('thumbnail', pixb, buf.timestamp) return gst.FLOW_OK
def _notify_caps_cb(self, pad, args): self.discovered_cond.acquire() caps = pad.get_negotiated_caps() if not caps: pad.info("no negotiated caps available") self.discovered = True self.discovered_cond.notify() self.discovered_cond.release() return # the caps are fixed # We now get the total length of that stream q = gst.query_new_duration(gst.FORMAT_TIME) pad.info("sending duration query") if pad.get_peer().query(q): format, length = q.parse_duration() if format == gst.FORMAT_TIME: pad.info("got duration (time) : %s" % (gst.TIME_ARGS(length),)) else: pad.info("got duration : %d [format:%d]" % (length, format)) else: length = -1 gst.warning("duration query failed") # We store the caps and length in the proper location if "audio" in caps.to_string(): self.input_samplerate = caps[0]["rate"] if not self.output_samplerate: self.output_samplerate = self.input_samplerate self.input_channels = caps[0]["channels"] if not self.output_channels: self.output_channels = self.input_channels self.input_duration = length / gst.SECOND self.input_totalframes = int( self.input_duration * self.input_samplerate) if "x-raw-float" in caps.to_string(): self.input_width = caps[0]["width"] else: self.input_width = caps[0]["depth"] self.discovered = True self.discovered_cond.notify() self.discovered_cond.release()