def play(self, start = None, end = None, cb = (), play = True, timed = False): # For cb, None means no cb, so use something else for "no change". self.pitch.set_property('tempo', self.speed) self.offset = self.track['files'][self.track['media']][1] * 1000. if cb != (): self.cb = cb if end is None: end = self.endtarget if end is not None and start is not None and end < start: end = None self.endtarget = end if play is None: play = self.playing() if end is not None and end < self.offset + self.media_duration: if self.set_endtarget: self.set_endtarget(end) end -= self.offset elif self.set_endtarget: self.set_endtarget(end) if start is not None and start < self.offset: wait = -1 *(start - self.offset) loop = GLib.MainLoop() start = self.offset if self.timeout != None: GLib.source_remove(self.timeout) #Only do one timeout at a time to prevent wierd re-starting issues self.timeout = None if self.playing(): self.pause(True) self.timeout = GLib.timeout_add(wait, lambda _: self.play(start = start, end=end, cb=cb,play=play, timed = True), None) self.countdown_end = time.time() + wait/1000. return True logging.debug("start {}, offset {}, duration {}, play {}".format(start, self.offset, self.media_duration, play)) if start is not None and start < self.offset + self.media_duration: logging.debug("Actually playing because start is not none") start -= self.offset start /= self.speed if end is not None: end /= self.speed self.pause() self.pipeline.get_state(Gst.CLOCK_TIME_NONE) if end is not None and (start is None or end > start): ret = self.pipeline.seek(1.0, Gst.Format(Gst.Format.TIME), Gst.SeekFlags.ACCURATE | Gst.SeekFlags.FLUSH, Gst.SeekType.SET, start * Gst.MSECOND, Gst.SeekType.SET, end * Gst.MSECOND) if not ret: ret = self.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.KEY_UNIT | Gst.SeekFlags.FLUSH, start * Gst.MSECOND) logging.warning("Simple seeking") #self.playbin.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, seek_time_secs * Gst.SECOND) else: ret = self.pipeline.seek(1.0, Gst.Format(Gst.Format.TIME), Gst.SeekFlags.ACCURATE | Gst.SeekFlags.FLUSH, Gst.SeekType.SET, start * Gst.MSECOND, Gst.SeekType.END, 0) if not ret: ret = self.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.KEY_UNIT|Gst.SeekFlags.ACCURATE | Gst.SeekFlags.FLUSH, start * Gst.MSECOND) logging.warning("Simple seeking") if not ret: logging.warning("Seeking not allowed by gstreamer") if play: self.pause(False) if timed: self.timeout = None self.countdown_end = None return False #Bit wierd, but makes this function possible to be used as a oneshot timer.
def play_audio_from_file(file_path, queue=False): """ Audio media """ if hasattr(play_audio_from_file, 'player') and \ play_audio_from_file.player: if queue: if hasattr(play_audio_from_file, 'queue'): if play_audio_from_file.queue and \ file_path in play_audio_from_file.queue: # if we already have that file in the queue # we'll just update the timer. if hasattr(play_audio_from_file, 'queue_timeout'): time.sleep(0.01) GObject.source_remove( play_audio_from_file.queue_timeout) f = Gst.Format(Gst.Format.TIME) duration = play_audio_from_file.player.query_duration( f)[0] timeout = duration / 1000000000. play_audio_from_file.queue_timeout = \ GObject.timeout_add(int(timeout * 1000), \ play_audio_from_file, file_path) return else: play_audio_from_file.queue = [] time.sleep(0.01) f = Gst.Format(Gst.Format.TIME) duration = play_audio_from_file.player.query_duration(f)[0] timeout = duration / 1000000000. play_audio_from_file.queue_timeout = GObject.timeout_add( \ int(timeout * 1000), play_audio_from_file, file_path) play_audio_from_file.queue.append(file_path) return else: play_audio_from_file.player.set_state(Gst.State.NULL) else: Gst.init(None) play_audio_from_file.player = Gst.parse_launch ( 'filesrc location=%s ! oggdemux ! vorbisdec ! ' \ 'audioconvert ! alsasink' % (file_path)) if not play_audio_from_file.player: _logger.warning('unable to play audio file %s' % (file_path)) else: play_audio_from_file.player.set_state(Gst.State.PLAYING)
def get_duration_seek_ns(self): try: position = self.player.query_duration(Gst.Format(Gst.Format.TIME)) return position[1] except Exception, e: logging.warn("GET query_duration: " + str(e)) return -1
def time(self): """Returns a tuple containing (position, length) where both values are integers in seconds. If no stream is available, returns (0, 0). """ fmt = Gst.Format(Gst.Format.TIME) try: posq = self.player.query_position(fmt) if not posq[0]: raise QueryError("query_position failed") pos = posq[1] / (10**9) lengthq = self.player.query_duration(fmt) if not lengthq[0]: raise QueryError("query_duration failed") length = lengthq[1] / (10**9) self.cached_time = (pos, length) return (pos, length) except QueryError: # Stream not ready. For small gaps of time, for instance # after seeking, the time values are unavailable. For this # reason, we cache recent. if self.playing and self.cached_time: return self.cached_time else: return (0, 0)
def seek_seconds(self, seconds): if not seconds: return logging.info("Start with seconds " + str(seconds)) seek_ns = (float(seconds) + 0.0) * self.NANO_SECONDS logging.info("SEC SEEK SEC " + str(seek_ns)) self.player.seek_simple(Gst.Format(Gst.Format.TIME), Gst.SeekFlags.FLUSH, seek_ns)
def seek(self, percent, offset=0): if not self.bean: return None seek_ns = self.duration_sec * (percent + offset) / 100 * self.NANO_SECONDS if self.bean.start_sec and self.bean.start_sec > 0: seek_ns += float(self.bean.start_sec) * self.NANO_SECONDS self.player.seek_simple(Gst.Format(Gst.Format.TIME), Gst.SeekFlags.FLUSH, seek_ns)
def update(self): self.pipeline.get_state(Gst.CLOCK_TIME_NONE) do_callback = False try: pos = self.pipeline.query_position(Gst.Format(Gst.Format.TIME))[1] / Gst.MSECOND * self.speed + self.offset except: self.end() self.set_pos(pos) # if self.speed <= 1 else pos / self.speed) return True
def get_pos(self): if self.timeout != None: if not self.playing(): return -1000*(self.countdown_end - time.time()) self.pipeline.get_state(Gst.CLOCK_TIME_NONE) try: pos = self.pipeline.query_position(Gst.Format(Gst.Format.TIME))[1] / Gst.MSECOND * self.speed + self.offset except: return 0 return pos #if self.speed <= 1 else pos / self.speed
def seek(self, position): """Seeks to position (in seconds).""" cur_pos, cur_len = self.time() if position > cur_len: self.stop() return fmt = Gst.Format(Gst.Format.TIME) ns = position * 10**9 # convert to nanoseconds self.player.seek_simple(fmt, Gst.SeekFlags.FLUSH, ns) # save new cached time self.cached_time = (position, cur_len)
def get_duration(cls, filename): #Class method so we can check a files duration without instantiating everything cls.prober.set_property('uri', 'file://' + url.pathname2url(os.path.realpath(filename))) cls.probe_pipeline.set_state(Gst.State.PAUSED) cls.probe_pipeline.get_state(Gst.CLOCK_TIME_NONE) if any([bugged in filename for bugged in ["mp3","wav","m4a"]]): time.sleep (0.125) #https://www.ruby-forum.com/topic/1232772 try: ret = cls.probe_pipeline.query_duration(Gst.Format(Gst.Format.TIME))[1] / Gst.MSECOND except: #import traceback #traceback.print_exc() logging.warning('Not using file "%s"' % os.path.realpath(filename)) ret = None cls.probe_pipeline.set_state(Gst.State.NULL) return ret
def build_and_run(pipelinefunc, name, segment=None, **pipelinefunc_kwargs): for key, value in pipelinefunc_kwargs.items(): print("{0} = {1}".format(key, value)) print >> sys.stderr, "=== Running Test %s ===" % name mainloop = GObject.MainLoop() pipeline = Gst.Pipeline(name=name) handler = MultiChannelHandler(mainloop, pipeline, output="./", instrument=None) pipeline = pipelinefunc(pipeline, name, handler, **pipelinefunc_kwargs) if segment is not None: if pipeline.set_state( Gst.State.PAUSED) == Gst.StateChangeReturn.FAILURE: raise RuntimeError("pipeline failed to enter PLAYING state") pipeline.seek(1.0, Gst.Format(Gst.Format.TIME), Gst.SeekFlags.FLUSH, Gst.SeekType.SET, segment[0].ns(), Gst.SeekType.SET, segment[1].ns()) if pipeline.set_state(Gst.State.PLAYING) == Gst.StateChangeReturn.FAILURE: raise RuntimeError("pipeline failed to enter PLAYING state") mainloop.run()
#!/usr/bin/env python import gi gi.require_version('Gst', '1.0') from gi.repository import Gst Gst.init(None) player = Gst.ElementFactory.make("playbin", "player") fakesink = Gst.ElementFactory.make("fakesink", "fakesink") player.set_property("video-sink", fakesink) player.set_property( "uri", "file:///home/cpd/Devel/Python/PyFiddle/Audio/mpthreetest.mp3") format = Gst.Format(Gst.Format.TIME) duration = player.query_duration(format)[0]
def get_duration(self) -> Optional[float]: pos = self.source.query_duration(Gst.Format(Gst.Format.TIME)) if not pos[0]: return None return pos[1] / 1e9
import multiprocessing import os import queue import time import urllib import urllib.request import gi # pylint: disable=import-error gi.require_version('Gst', '1.0') from gi.repository import GLib, Gst # pylint: disable=import-error,wrong-import-position Gst.init(None) _LOGGER = logging.getLogger(__name__) _FORMAT_TIME = Gst.Format(Gst.Format.TIME) _NANOSEC_MULT = 10 ** 9 STATE_IDLE = 'idle' STATE_PLAYING = 'playing' STATE_PAUSED = 'paused' TASK_PLAY = 'play' TASK_PAUSE = 'pause' TASK_STOP = 'stop' TASK_MEDIA = 'media' TASK_SET_POSITION = 'set_position' TASK_SET_VOLUME = 'set_volume' ATTR_STATE = 'state' ATTR_VOLUME = 'volume' ATTR_POSITION = 'position' ATTR_DURATION = 'duration'
def seek_ns(self, ns): if not ns: return logging.info("SEC ns " + str(ns)) self.player.seek_simple(Gst.Format(Gst.Format.TIME), Gst.SeekFlags.FLUSH, ns)
def __init__(self, instance): Gtk.VBox.__init__(self) Loggable.__init__(self) self.log("Init PreviewWidget") self.connect('destroy', self._destroy_cb) self.settings = instance.settings self.preview_cache = {} self.preview_cache_errors = {} self.discoverer = Discoverer.new(Gst.SECOND) #playbin for play pics self.player = Gst.ElementFactory.make("playbin", "preview-player") bus = self.player.get_bus() bus.add_signal_watch() bus.connect('message', self._bus_message_cb) bus.enable_sync_message_emission() bus.connect('sync-message::element', self._sync_message_cb) bus.connect('message::tag', self._tag_found_cb) self.__videosink = self.player.get_property("video-sink") self.__fakesink = Gst.ElementFactory.make("fakesink", "fakesink") #some global variables for preview handling self.is_playing = False self.time_format = Gst.Format(Gst.Format.TIME) self.original_dims = (PREVIEW_WIDTH, PREVIEW_HEIGHT) self.countinuous_seek = False self.slider_being_used = False self.current_selected_uri = "" self.current_preview_type = "" self.description = "" self.tags = {} # Gui elements: # Drawing area for video output self.preview_video = ViewerWidget() self.preview_video.modify_bg(Gtk.StateType.NORMAL, self.preview_video.get_style().black) self.pack_start(self.preview_video, False, True, 0) # An image for images and audio self.preview_image = Gtk.Image() self.preview_image.set_size_request(self.settings.FCpreviewWidth, self.settings.FCpreviewHeight) self.preview_image.show() self.pack_start(self.preview_image, False, True, 0) # Play button self.bbox = Gtk.HBox() self.play_button = Gtk.ToolButton(Gtk.STOCK_MEDIA_PLAY) self.play_button.connect("clicked", self._on_start_stop_clicked_cb) self.bbox.pack_start(self.play_button, False, True, 0) #Scale for position handling self.pos_adj = Gtk.Adjustment() self.seeker = Gtk.Scale.new(Gtk.Orientation.HORIZONTAL, self.pos_adj) self.seeker.connect('button-press-event', self._on_seeker_press_cb) self.seeker.connect('button-release-event', self._on_seeker_press_cb) self.seeker.connect('motion-notify-event', self._on_motion_notify_cb) self.seeker.set_draw_value(False) self.seeker.show() self.bbox.pack_start(self.seeker, True, True, 0) # Zoom buttons self.b_zoom_in = Gtk.ToolButton(Gtk.STOCK_ZOOM_IN) self.b_zoom_in.connect("clicked", self._on_zoom_clicked_cb, 1) self.b_zoom_out = Gtk.ToolButton(Gtk.STOCK_ZOOM_OUT) self.b_zoom_out.connect("clicked", self._on_zoom_clicked_cb, -1) self.bbox.pack_start(self.b_zoom_in, False, True, 0) self.bbox.pack_start(self.b_zoom_out, False, True, 0) self.bbox.show_all() self.pack_start(self.bbox, False, False, 0) # Label for metadata tags self.l_tags = Gtk.Label() self.l_tags.set_justify(Gtk.Justification.LEFT) self.l_tags.set_ellipsize(Pango.EllipsizeMode.END) self.l_tags.show() self.pack_start(self.l_tags, False, False, 0) # Error handling vbox = Gtk.VBox() vbox.set_spacing(SPACING) self.l_error = Gtk.Label(label=_("PiTiVi can not preview this file.")) self.b_details = Gtk.Button(_("More info")) self.b_details.connect('clicked', self._on_b_details_clicked_cb) vbox.pack_start(self.l_error, True, True, 0) vbox.pack_start(self.b_details, False, False, 0) vbox.show() self.pack_start(vbox, False, False, 0)
_ = lambda s: s # may be add gettext later NAME = "Play it Slowly" VERSION = "1.5.1" WEBSITE = "http://29a.ch/playitslowly/" if sys.platform == "win32": CONFIG_PATH = os.path.expanduser("~/playitslowly.json") else: XDG_CONFIG_HOME = os.path.expanduser( os.environ.get("XDG_CONFIG_HOME", "~/.config")) if not os.path.exists(XDG_CONFIG_HOME): os.mkdir(XDG_CONFIG_HOME) CONFIG_PATH = os.path.join(XDG_CONFIG_HOME, "playitslowly.json") TIME_FORMAT = Gst.Format(Gst.Format.TIME) def in_pathlist(filename, paths=os.environ.get("PATH").split(os.pathsep)): """check if an application is somewhere in $PATH""" return any(os.path.exists(os.path.join(path, filename)) for path in paths) class Config(dict): """Very simple json config file""" def __init__(self, path=None): dict.__init__(self) self.path = path def load(self): with open(self.path, encoding="utf-8") as f: