Beispiel #1
0
    def _setThumbnail(self, time, pixbuf):
        # TODO: is "time" guaranteed to be nanosecond precise?
        # => __tim says: "that's how it should be"
        # => also see gst-plugins-good/tests/icles/gdkpixbufsink-test
        # => Daniel: It is *not* nanosecond precise when we remove the videorate
        #            element from the pipeline
        if time in self.queue:
            self.queue.remove(time)

        self.thumb_cache[time] = pixbuf

        if time in self.thumbs:
            self.thumbs[time].set_from_gdkpixbuf_animated(pixbuf)
        else:
            sorted_times = self.thumbs.keys()
            sorted_times.sort()
            index = bisect.bisect(sorted_times, time)
            time = sorted_times[index]
            thumb = self.thumbs[sorted_times[index]]
            if thumb.has_pixel_data:
                if len(sorted_times) < index + 1:
                    self.warning("timestamp %s does not look like anything we "
                                 "requested" % print_ns(time))
                    return
                # It might actually be the follwoing thumbnail we were
                time = sorted_times[index + 1]
                thumb = self.thumbs[time]
                if thumb.has_pixel_data:
                    self.warning("Surrounding thumbnails are already set "
                                 "for timestamp %s" % print_ns(time))
                    return
            self.thumbs[time].set_from_gdkpixbuf_animated(pixbuf)
Beispiel #2
0
    def simple_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" % print_ns(position))
        else:
            self.debug("position : %d , format:%d" % (position, format))

        # clamp between [0, duration]
        if format == Gst.Format.TIME:
            position = max(0, min(position, self.getDuration()) - 1)

        res = self._pipeline.seek(1.0, format, Gst.SeekFlags.FLUSH,
                                  Gst.SeekType.SET, position,
                                  Gst.SeekType.NONE, -1)
        if not res:
            self.debug("seeking failed")
            raise PipelineError("seek failed")

        self.debug("seeking successful")
        self.emit('position', position)
Beispiel #3
0
    def simple_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" % print_ns(position))
        else:
            self.debug("position : %d , format:%d" % (position, format))

        # clamp between [0, duration]
        if format == Gst.Format.TIME:
            position = max(0, min(position, self.getDuration()) - 1)

        res = self._pipeline.seek(1.0, format, Gst.SeekFlags.FLUSH,
                                  Gst.SeekType.SET, position,
                                  Gst.SeekType.NONE, -1)
        if not res:
            self.debug("seeking failed")
            raise PipelineError("seek failed")

        self.debug("seeking successful")
        self.emit('position', position)
Beispiel #4
0
    def _seekTimeoutCb(self, relative=False):
        self.pending_seek_id = None
        if relative:
            try:
                self.emit('seek-relative', self._time)
            except PipelineError:
                self.error("Error while seeking %s relative", self._time)
                # if an exception happened while seeking, properly
                # reset ourselves
                return False

            self._time = None
        elif self.position is not None and self.format is not None:
            position, self.position = self.position, None
            format, self.format = self.format, None
            try:
                self.emit('seek', position, format)
            except PipelineError as e:
                self.error(
                    "Error while seeking to position:%s format: %r, reason: %s",
                    print_ns(position), format, e)
                # if an exception happened while seeking, properly
                # reset ourselves
                return False
        return False
Beispiel #5
0
    def _seekTimeoutCb(self, relative=False):
        self.pending_seek_id = None

        if relative:
            try:
                self.emit('seek-relative', self._time)
            except PipelineError:
                self.error("Error while seeking %s relative", self._time)
                # if an exception happened while seeking, properly
                # reset ourselves
                return False

            self._time = None
        elif self.position is not None and self.format is not None:
            position, self.position = self.position, None
            format, self.format = self.format, None
            try:
                self.emit('seek', position, format)
            except PipelineError as e:
                self.error("Error while seeking to position:%s format: %r, reason: %s",
                          print_ns(position), format, e)
                # if an exception happened while seeking, properly
                # reset ourselves
                return False

        if self.pending_position:
            self.seek(self.pending_position, on_idle=True)
            self.pending_position = None

        return False
Beispiel #6
0
    def getDuration(self, format=Gst.Format.TIME):
        """
        Get the duration of the C{Pipeline}.
        """
        self.log("format %r" % format)

        dur = self._getDuration(format)
        self.log("Got duration %s" % print_ns(dur))
        if self._duration != dur:
            self.emit("duration-changed", dur)

        self._duration = dur

        return dur
Beispiel #7
0
    def getDuration(self, format=Gst.Format.TIME):
        """
        Get the duration of the C{Pipeline}.
        """
        self.log("format %r" % format)

        dur = self._getDuration(format)
        if dur is None:
            self.error("Invalid duration: None")
        else:
            self.log("Got duration %s" % print_ns(dur))
        if self._duration != dur:
            self.emit("duration-changed", dur)

        self._duration = dur

        return dur
Beispiel #8
0
    def simple_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 self._waiting_for_async_done is True:
            self._next_seek = (position, format)
            self.info("Setting next seek to %s" % (str(self._next_seek)))
            return

        if format == Gst.Format.TIME:
            self.debug("position : %s" % print_ns(position))
        else:
            self.debug("position : %d , format:%d" % (position, format))

        # clamp between [0, duration]
        if format == Gst.Format.TIME:
            position = max(0, min(position, self.getDuration()) - 1)

        res = self._pipeline.seek(1.0, format, Gst.SeekFlags.FLUSH,
                                  Gst.SeekType.SET, position,
                                  Gst.SeekType.NONE, -1)
        self._waiting_for_async_done = True

        if self._timeout_async_id:
            GLib.source_remove(self._timeout_async_id)
            self._timeout_async_id = 0

        self._timeout_async_id = GLib.timeout_add(1000, self._resetWaitingForAsyncDone)

        if not res:
            self.debug("seeking failed")
            raise PipelineError("seek failed")

        self.lastPosition = position

        self.debug("seeking successful")
        self.emit('position', position)
Beispiel #9
0
    def clipTrimPreview(self, tl_obj, position):
        """
        While a clip is being trimmed, show a live preview of it.
        """
        if isinstance(tl_obj, GES.TitleClip) or tl_obj.props.is_image or not hasattr(tl_obj, "get_uri"):
            self.log("%s is an image or has no URI, so not previewing trim" % tl_obj)
            return False

        clip_uri = tl_obj.props.uri
        cur_time = time()
        if not self._tmp_pipeline:
            self.debug("Creating temporary pipeline for clip %s, position %s",
                clip_uri, print_ns(position))

            self._oldTimelinePos = self.pipeline.getPosition()
            self._tmp_pipeline = Gst.ElementFactory.make("playbin", None)
            self._tmp_pipeline.set_property("uri", clip_uri)
            self.setPipeline(SimplePipeline(self._tmp_pipeline))
            self._lastClipTrimTime = cur_time
        if (cur_time - self._lastClipTrimTime) > 0.2:
            # Do not seek more than once every 200 ms (for performance)
            self._tmp_pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, position)
            self._lastClipTrimTime = cur_time
Beispiel #10
0
    def clipTrimPreview(self, tl_obj, position):
        """
        While a clip is being trimmed, show a live preview of it.
        """
        if isinstance(tl_obj, GES.TitleClip) or tl_obj.props.is_image or not hasattr(tl_obj, "get_uri"):
            self.log("%s is an image or has no URI, so not previewing trim" % tl_obj)
            return False

        clip_uri = tl_obj.props.uri
        cur_time = time()
        if not self._tmp_pipeline:
            self.debug("Creating temporary pipeline for clip %s, position %s",
                clip_uri, print_ns(position))

            self._oldTimelinePos = self.pipeline.getPosition()
            self._tmp_pipeline = Gst.ElementFactory.make("playbin", None)
            self._tmp_pipeline.set_property("uri", clip_uri)
            self.setPipeline(SimplePipeline(self._tmp_pipeline, self._tmp_pipeline))
            self._lastClipTrimTime = cur_time
        if (cur_time - self._lastClipTrimTime) > 0.2:
            # Do not seek more than once every 200 ms (for performance)
            self._tmp_pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, position)
            self._lastClipTrimTime = cur_time
Beispiel #11
0
    def getDuration(self, format=Gst.Format.TIME):
        """
        Get the duration of the C{Pipeline}.
        """
        self.log("format %r" % format)
        try:
            res, dur = self._pipeline.query_duration(format)
        except Exception, e:

            self.handleException(e)
            raise PipelineError("Couldn't get duration")

        if not res:
            raise PipelineError("Couldn't get duration")

        self.log("Got duration %s" % print_ns(dur))
        if self._duration != dur:
            self.emit("duration-changed", dur)

        self._duration = dur

        return dur

    def activatePositionListener(self, interval=300):
        """
        Activate the position listener.

        When activated, the Pipeline will emit the 'position' signal at the
        specified interval when it is the PLAYING or PAUSED state.

        @see: L{deactivatePositionListener}
Beispiel #12
0
class SimplePipeline(Signallable, Loggable):
    """
    The Pipeline is only responsible for:
     - State changes
     - Position seeking
     - Position Querying
       - Along with an periodic callback (optional)

    Signals:
     - C{state-change} : The state of the pipeline changed.
     - C{position} : The current position of the pipeline changed.
     - C{eos} : The Pipeline has finished playing.
     - C{error} : An error happened.
    """

    __signals__ = {
        "state-change": ["state"],
        "position": ["position"],
        "duration-changed": ["duration"],
        "eos": [],
        "error": ["message", "details"]
    }

    def __init__(self, pipeline, video_overlay):
        Loggable.__init__(self)
        Signallable.__init__(self)
        self._pipeline = pipeline
        self._bus = self._pipeline.get_bus()
        self._bus.add_signal_watch()
        self._bus.connect("message", self._busMessageCb)
        self._listening = False  # for the position handler
        self._listeningInterval = 300  # default 300ms
        self._listeningSigId = 0
        self._duration = Gst.CLOCK_TIME_NONE
        self.video_overlay = video_overlay

    def release(self):
        """
        Release the L{Pipeline} and all used L{ObjectFactory} and
        L{Action}s.

        Call this method when the L{Pipeline} is no longer used. Forgetting to do
        so will result in memory loss.

        @postcondition: The L{Pipeline} will no longer be usable.
        """
        self.deactivatePositionListener()
        self._bus.disconnect_by_func(self._busMessageCb)
        self._bus.remove_signal_watch()

        self._pipeline.setState(Gst.State.NULL)
        self._bus = None

    def flushSeek(self):
        self.pause()
        try:
            self.seekRelative(0)
        except PipelineError:
            pass

    def setState(self, state):
        """
        Set the L{Pipeline} to the given state.

        @raises PipelineError: If the C{Gst.Pipeline} could not be changed to
        the requested state.
        """
        self.debug("state:%r" % state)
        res = self._pipeline.set_state(state)
        if res == Gst.StateChangeReturn.FAILURE:
            # reset to NULL
            self._pipeline.set_state(Gst.State.NULL)
            raise PipelineError(
                "Failure changing state of the Gst.Pipeline to %r, currently reset to NULL"
                % state)

    def getState(self):
        """
        Query the L{Pipeline} for the current state.

        @see: L{setState}

        This will do an actual query to the underlying GStreamer Pipeline.
        @return: The current state.
        @rtype: C{State}
        """
        change, state, pending = self._pipeline.get_state(0)
        self.debug("change:%r, state:%r, pending:%r" %
                   (change, state, pending))
        return state

    def play(self):
        """
        Sets the L{Pipeline} to PLAYING
        """
        self.setState(Gst.State.PLAYING)

    def pause(self):
        """
        Sets the L{Pipeline} to PAUSED
        """
        self.setState(Gst.State.PAUSED)

        # When the pipeline has been paused we need to update the
        # timeline/playhead position, as the 'position' signal
        # is only emitted every 300ms and the playhead jumps
        # during the playback.
        try:
            self.emit("position", self.getPosition())
        except PipelineError:
            # Getting the position failed
            pass

    def stop(self):
        """
        Sets the L{Pipeline} to READY
        """
        self.setState(Gst.State.READY)

    def togglePlayback(self):
        if self.getState() == Gst.State.PLAYING:
            self.pause()
        else:
            self.play()

    #{ Position and Seeking methods

    def getPosition(self, format=Gst.Format.TIME):
        """
        Get the current position of the L{Pipeline}.

        @param format: The format to return the current position in
        @type format: C{Gst.Format}
        @return: The current position or Gst.CLOCK_TIME_NONE
        @rtype: L{long}
        @raise PipelineError: If the position couldn't be obtained.
        """
        self.log("format %r" % format)
        try:
            res, cur = self._pipeline.query_position(format)
        except Exception, e:
            self.handleException(e)
            raise PipelineError("Couldn't get position")

        if not res:
            raise PipelineError("Couldn't get position")

        self.log("Got position %s" % print_ns(cur))
        return cur