From c7656cdc15e71320fb910c6eaf2ffb4a340b9de2 Mon Sep 17 00:00:00 2001 From: Stein Magnus Jodal Date: Sat, 22 Dec 2012 20:24:58 +0100 Subject: [PATCH] spotify: Replace wall clock timer with GStreamer timer --- mopidy/backends/spotify/playback.py | 93 ++++------------------ mopidy/backends/spotify/session_manager.py | 4 + 2 files changed, 19 insertions(+), 78 deletions(-) diff --git a/mopidy/backends/spotify/playback.py b/mopidy/backends/spotify/playback.py index e4534172..d80ef543 100644 --- a/mopidy/backends/spotify/playback.py +++ b/mopidy/backends/spotify/playback.py @@ -1,113 +1,50 @@ from __future__ import unicode_literals import logging -import time +import functools from spotify import Link, SpotifyError from mopidy.backends import base -from mopidy.core import PlaybackState logger = logging.getLogger('mopidy.backends.spotify') +def seek_data_callback(spotify_backend, time_position): + logger.debug('seek_data_callback(%d) called', time_position) + spotify_backend.playback.on_seek_data(time_position) + + class SpotifyPlaybackProvider(base.BasePlaybackProvider): - def __init__(self, *args, **kwargs): - super(SpotifyPlaybackProvider, self).__init__(*args, **kwargs) - - self._timer = TrackPositionTimer() - - def pause(self): - self._timer.pause() - - return super(SpotifyPlaybackProvider, self).pause() - def play(self, track): if track.uri is None: return False + spotify_backend = self.backend.actor_ref.proxy() + seek_data_callback_bound = functools.partial( + seek_data_callback, spotify_backend) + try: self.backend.spotify.session.load( Link.from_string(track.uri).as_track()) self.backend.spotify.session.play(1) self.audio.prepare_change() - self.audio.set_uri('appsrc://') + self.audio.set_appsrc(seek_data=seek_data_callback_bound) self.audio.start_playback() self.audio.set_metadata(track) - self._timer.play() - return True except SpotifyError as e: logger.info('Playback of %s failed: %s', track.uri, e) return False - def resume(self): - time_position = self.get_time_position() - self._timer.resume() - self.audio.prepare_change() - result = self.seek(time_position) - self.audio.start_playback() - return result - - def seek(self, time_position): - self.backend.spotify.session.seek(time_position) - self._timer.seek(time_position) - return True - def stop(self): self.backend.spotify.session.play(0) - return super(SpotifyPlaybackProvider, self).stop() - def get_time_position(self): - # XXX: The default implementation of get_time_position hangs/times out - # when used with the Spotify backend and GStreamer appsrc. If this can - # be resolved, we no longer need to use a wall clock based time - # position for Spotify playback. - return self._timer.get_time_position() - - -class TrackPositionTimer(object): - """ - Keeps track of time position in a track using the wall clock and playback - events. - - To not introduce a reverse dependency on the playback controller, this - class keeps track of playback state itself. - """ - - def __init__(self): - self._state = PlaybackState.STOPPED - self._accumulated = 0 - self._started = 0 - - def play(self): - self._state = PlaybackState.PLAYING - self._accumulated = 0 - self._started = self._wall_time() - - def pause(self): - self._state = PlaybackState.PAUSED - self._accumulated += self._wall_time() - self._started - - def resume(self): - self._state = PlaybackState.PLAYING - - def seek(self, time_position): - self._started = self._wall_time() - self._accumulated = time_position - - def get_time_position(self): - if self._state == PlaybackState.PLAYING: - time_since_started = self._wall_time() - self._started - return self._accumulated + time_since_started - elif self._state == PlaybackState.PAUSED: - return self._accumulated - elif self._state == PlaybackState.STOPPED: - return 0 - - def _wall_time(self): - return int(time.time() * 1000) + def on_seek_data(self, time_position): + logger.debug('playback.on_seek_data(%d) called', time_position) + self.backend.spotify.next_buffer_timestamp = time_position + self.backend.spotify.session.seek(time_position) diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index f2631406..0eed9939 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -46,6 +46,7 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): self.backend_ref = backend_ref self.connected = threading.Event() + self.next_buffer_timestamp = None self.container_manager = None self.playlist_manager = None @@ -121,6 +122,9 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): } buffer_ = gst.Buffer(bytes(frames)) buffer_.set_caps(gst.caps_from_string(capabilites)) + if self.next_buffer_timestamp is not None: + buffer_.timestamp = self.next_buffer_timestamp * gst.MSECOND + self.next_buffer_timestamp = None if self.audio.emit_data(buffer_).get(): return num_frames