From 0a96e5dccb3f7177c239f6efefd176ea87850833 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Thu, 15 Nov 2012 22:34:20 +0100 Subject: [PATCH 1/3] Update emit_data to take buffers. Simplify emit data method to take Gstreamer buffers. This allows us to more concisely give it buffers with duration, timestamp and other relevant data set. --- mopidy/audio/actor.py | 12 +++--------- mopidy/backends/spotify/session_manager.py | 9 ++++++++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py index 162e2a05..b422bc67 100644 --- a/mopidy/audio/actor.py +++ b/mopidy/audio/actor.py @@ -225,22 +225,16 @@ class Audio(pykka.ThreadingActor): """ self._playbin.set_property('uri', uri) - def emit_data(self, capabilities, data): + def emit_data(self, buffer_): """ Call this to deliver raw audio data to be played. Note that the uri must be set to ``appsrc://`` for this to work. - :param capabilities: a GStreamer capabilities string - :type capabilities: string - :param data: raw audio data to be played + :param buffer_: buffer to pass to appsrc + :type buffer_: :class:`gst.Buffer` """ - caps = gst.caps_from_string(capabilities) - buffer_ = gst.Buffer(buffer(data)) - buffer_.set_caps(caps) - source = self._playbin.get_property('source') - source.set_property('caps', caps) source.emit('push-buffer', buffer_) def emit_end_of_stream(self): diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index cd3d97db..8032a289 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -1,5 +1,9 @@ from __future__ import unicode_literals +import pygst +pygst.require('0.10') +import gst + import logging import os import threading @@ -108,7 +112,10 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): 'sample_rate': sample_rate, 'channels': channels, } - self.audio.emit_data(capabilites, bytes(frames)) + buffer_ = gst.Buffer(bytes(frames)) + buffer_.set_caps(gst.caps_from_string(capabilites)) + + self.audio.emit_data(buffer_) return num_frames def play_token_lost(self, session): From f2b975cc37c93cf5633f85497d6e4c5ffa7f572a Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Thu, 15 Nov 2012 22:38:27 +0100 Subject: [PATCH 2/3] Update emit_data to return true if data was delivered. This is probably not needed, but for the sake of correctnes it doesn't hurt. --- mopidy/audio/actor.py | 5 ++++- mopidy/backends/spotify/session_manager.py | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py index b422bc67..a17033ed 100644 --- a/mopidy/audio/actor.py +++ b/mopidy/audio/actor.py @@ -231,11 +231,14 @@ class Audio(pykka.ThreadingActor): Note that the uri must be set to ``appsrc://`` for this to work. + Returns true if data was delivered. + :param buffer_: buffer to pass to appsrc :type buffer_: :class:`gst.Buffer` + :rtype: boolean """ source = self._playbin.get_property('source') - source.emit('push-buffer', buffer_) + return source.emit('push-buffer', buffer_) == gst.FLOW_OK def emit_end_of_stream(self): """ diff --git a/mopidy/backends/spotify/session_manager.py b/mopidy/backends/spotify/session_manager.py index 8032a289..998e4d5e 100644 --- a/mopidy/backends/spotify/session_manager.py +++ b/mopidy/backends/spotify/session_manager.py @@ -115,8 +115,10 @@ class SpotifySessionManager(process.BaseThread, PyspotifySessionManager): buffer_ = gst.Buffer(bytes(frames)) buffer_.set_caps(gst.caps_from_string(capabilites)) - self.audio.emit_data(buffer_) - return num_frames + if self.audio.emit_data(buffer_).get(): + return num_frames + else: + return 0 def play_token_lost(self, session): """Callback used by pyspotify""" From d516e9023ab8ee0894341c70c8c6a75a33e9959e Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Thu, 15 Nov 2012 22:49:44 +0100 Subject: [PATCH 3/3] Store active appsrc and refuse data when it is not set. We use the new source flag and the about to finish flags to set and unset the current appsrc. In emit data we now return false if the appsrc is not set. Also note that we need to use b'' for Gstreamer properties as it can't convert unicode to the correct type. I also added the signal disconnect code for about to finish. --- mopidy/audio/actor.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/mopidy/audio/actor.py b/mopidy/audio/actor.py index a17033ed..a23c4c43 100644 --- a/mopidy/audio/actor.py +++ b/mopidy/audio/actor.py @@ -42,8 +42,10 @@ class Audio(pykka.ThreadingActor): self._mixer = None self._mixer_track = None self._software_mixing = False + self._appsrc = None self._notify_source_signal_id = None + self._about_to_finish_id = None self._message_signal_id = None def on_start(self): @@ -67,9 +69,14 @@ class Audio(pykka.ThreadingActor): fakesink = gst.element_factory_make('fakesink') self._playbin.set_property('video-sink', fakesink) + self._about_to_finish_id = self._playbin.connect( + 'about-to-finish', self._on_about_to_finish) self._notify_source_signal_id = self._playbin.connect( 'notify::source', self._on_new_source) + def _on_about_to_finish(self, element): + self._appsrc = None + def _on_new_source(self, element, pad): uri = element.get_property('uri') if not uri or not uri.startswith('appsrc://'): @@ -82,8 +89,13 @@ class Audio(pykka.ThreadingActor): b'rate=(int)44100') source = element.get_property('source') source.set_property('caps', default_caps) + source.set_property('format', b'time') # Gstreamer does not like unicode + + self._appsrc = source def _teardown_playbin(self): + if self._about_to_finish_id: + self._playbin.disconnect(self._about_to_finish_id) if self._notify_source_signal_id: self._playbin.disconnect(self._notify_source_signal_id) self._playbin.set_state(gst.STATE_NULL) @@ -237,8 +249,9 @@ class Audio(pykka.ThreadingActor): :type buffer_: :class:`gst.Buffer` :rtype: boolean """ - source = self._playbin.get_property('source') - return source.emit('push-buffer', buffer_) == gst.FLOW_OK + if not self._appsrc: + return False + return self._appsrc.emit('push-buffer', buffer_) == gst.FLOW_OK def emit_end_of_stream(self): """