Change LibspotifyBackend from using GStreamer directly to just sending it's data to GStreamerOutput

This commit is contained in:
Stein Magnus Jodal 2010-08-12 01:30:20 +02:00
parent f488b0fe2d
commit 816f877c4b

View File

@ -1,14 +1,9 @@
import datetime as dt
import gobject
import logging
import os
import multiprocessing
import threading
import pygst
pygst.require('0.10')
import gst
from spotify import Link, SpotifyError
from spotify.manager import SpotifySessionManager
from spotify.alsahelper import AlsaController
@ -18,6 +13,7 @@ from mopidy.backends.base import (BaseBackend, BaseCurrentPlaylistController,
BaseLibraryController, BasePlaybackController,
BaseStoredPlaylistsController)
from mopidy.models import Artist, Album, Track, Playlist
from mopidy.process import pickle_connection
logger = logging.getLogger('mopidy.backends.libspotify')
@ -47,7 +43,6 @@ class LibspotifyBackend(BaseBackend):
self.stored_playlists = LibspotifyStoredPlaylistsController(
backend=self)
self.uri_handlers = [u'spotify:', u'http://open.spotify.com/']
self.gstreamer_pipeline = gst.Pipeline("spotify_pipeline")
self.spotify = self._connect()
def _connect(self):
@ -55,7 +50,7 @@ class LibspotifyBackend(BaseBackend):
spotify = LibspotifySessionManager(
settings.SPOTIFY_USERNAME, settings.SPOTIFY_PASSWORD,
core_queue=self.core_queue,
gstreamer_pipeline=self.gstreamer_pipeline)
output_connection=self.output_connection)
spotify.start()
return spotify
@ -97,13 +92,22 @@ class LibspotifyLibraryController(BaseLibraryController):
class LibspotifyPlaybackController(BasePlaybackController):
def _set_output_state(self, state_name):
logger.debug(u'Setting output state to %s ...', state_name)
(my_end, other_end) = multiprocessing.Pipe()
self.backend.output_connection.send({
'command': 'set_state',
'state': state_name,
'reply_to': pickle_connection(other_end),
})
my_end.poll(None)
return my_end.recv()
def _pause(self):
result = self.backend.gstreamer_pipeline.set_state(gst.STATE_PAUSED)
logger.debug('Changed gstreamer state to paused. Result was: %s' % result)
return result == gst.STATE_CHANGE_SUCCESS
return self._set_output_state('PAUSED')
def _play(self, track):
self.backend.gstreamer_pipeline.set_state(gst.STATE_READY)
self._set_output_state('READY')
if self.state == self.PLAYING:
self.stop()
if track.uri is None:
@ -112,24 +116,22 @@ class LibspotifyPlaybackController(BasePlaybackController):
self.backend.spotify.session.load(
Link.from_string(track.uri).as_track())
self.backend.spotify.session.play(1)
self.backend.gstreamer_pipeline.set_state(gst.STATE_PLAYING)
self._set_output_state('PLAYING')
return True
except SpotifyError as e:
logger.warning('Play %s failed: %s', track.uri, e)
return False
def _resume(self):
result = self.backend.gstreamer_pipeline.set_state(gst.STATE_PLAYING)
logger.debug('Changed gstreamer state to playing. Result was: %s' % result)
return result == gst.STATE_CHANGE_SUCCESS
return self._set_output_state('PLAYING')
def _seek(self, time_position):
pass # TODO
def _stop(self):
self.backend.gstreamer_pipeline.set_state(gst.STATE_READY)
result = self._set_output_state('READY')
self.backend.spotify.session.play(0)
return True
return result
class LibspotifyStoredPlaylistsController(BaseStoredPlaylistsController):
@ -202,57 +204,19 @@ class LibspotifyTranslator(object):
tracks=[cls.to_mopidy_track(t) for t in spotify_playlist],
)
class GstreamerMessageBusProcess(threading.Thread):
def __init__(self, core_queue, pipeline):
super(GstreamerMessageBusProcess, self).__init__()
self.core_queue = core_queue
self.bus = pipeline.get_bus()
def run(self):
loop = gobject.MainLoop()
gobject.threads_init()
context = loop.get_context()
while True:
message = self.bus.pop_filtered(gst.MESSAGE_EOS)
if message is not None:
self.core_queue.put({'command': 'end_of_track'})
logger.debug('Got and handled Gstreamer message of type: %s' % message.type)
context.iteration(True)
class LibspotifySessionManager(SpotifySessionManager, threading.Thread):
cache_location = os.path.expanduser(settings.SPOTIFY_LIB_CACHE)
settings_location = os.path.expanduser(settings.SPOTIFY_LIB_CACHE)
appkey_file = os.path.expanduser(settings.SPOTIFY_LIB_APPKEY)
user_agent = 'Mopidy %s' % get_version()
def __init__(self, username, password, core_queue, gstreamer_pipeline):
def __init__(self, username, password, core_queue, output_connection):
SpotifySessionManager.__init__(self, username, password)
threading.Thread.__init__(self)
self.core_queue = core_queue
self.output_connection = output_connection
self.connected = threading.Event()
self.session = None
self.gstreamer_pipeline = gstreamer_pipeline
cap_string = """audio/x-raw-int,
endianness=(int)1234,
channels=(int)2,
width=(int)16,
depth=(int)16,
signed=True,
rate=(int)44100"""
caps = gst.caps_from_string(cap_string)
self.gsrc = gst.element_factory_make("appsrc", "app-source")
self.gsrc.set_property('caps', caps)
self.gsink = gst.element_factory_make("autoaudiosink", "autosink")
self.gstreamer_pipeline.add(self.gsrc, self.gsink)
gst.element_link_many(self.gsrc, self.gsink)
message_process = GstreamerMessageBusProcess(self.core_queue, self.gstreamer_pipeline)
message_process.start()
def run(self):
self.connect()
@ -294,17 +258,21 @@ class LibspotifySessionManager(SpotifySessionManager, threading.Thread):
def music_delivery(self, session, frames, frame_size, num_frames,
sample_type, sample_rate, channels):
"""Callback used by pyspotify"""
cap_string = """audio/x-raw-int,
endianness=(int)1234,
channels=(int)2,
width=(int)16,
depth=(int)16,
signed=True,
rate=(int)44100"""
caps = gst.caps_from_string(cap_string)
b = gst.Buffer(frames)
b.set_caps(caps)
self.gsrc.emit('push-buffer', b)
# TODO Base caps_string on arguments
caps_string = """
audio/x-raw-int,
endianness=(int)1234,
channels=(int)2,
width=(int)16,
depth=(int)16,
signed=True,
rate=(int)44100
"""
self.output_connection.send({
'command': 'deliver_data',
'caps': caps_string,
'data': bytes(frames),
})
def play_token_lost(self, session):
"""Callback used by pyspotify"""
@ -317,9 +285,8 @@ class LibspotifySessionManager(SpotifySessionManager, threading.Thread):
def end_of_track(self, session):
"""Callback used by pyspotify"""
logger.debug('End of track.')
self.gsrc.emit('end-of-stream')
logger.debug('End of stream sent to gstreamer.')
logger.debug('End of data stream.')
self.output_connection.send({'command': 'end_of_data_stream'})
def search(self, query, connection):
"""Search method used by Mopidy backend"""