spotify: Replace wall clock timer with GStreamer timer

This commit is contained in:
Stein Magnus Jodal 2012-12-22 20:24:58 +01:00
parent 9fa0f5213e
commit c7656cdc15
2 changed files with 19 additions and 78 deletions

View File

@ -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)

View File

@ -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