librespot: switch from ffmpegx to pulseaudio to stream to Kodi

This commit is contained in:
awiouy 2017-07-15 07:12:52 +02:00
parent 95eb836354
commit 3bff39dd21
8 changed files with 177 additions and 62 deletions

View File

@ -1,3 +1,8 @@
104
- Update to 910974e
- Switch from ffmpegx to pulseaudio to stream to Kodi
- Wait for sound.target
103
- Update system_information_string

View File

@ -19,12 +19,12 @@
PKG_NAME="librespot"
PKG_VERSION="aa86ebf"
PKG_REV="103"
PKG_REV="104"
PKG_ARCH="any"
PKG_LICENSE="MIT"
PKG_SITE="https://github.com/plietar/$PKG_NAME/"
PKG_URL="https://github.com/plietar/$PKG_NAME/archive/$PKG_VERSION.zip"
PKG_DEPENDS_TARGET="toolchain avahi ffmpegx libvorbis pyalsaaudio rust"
PKG_DEPENDS_TARGET="toolchain avahi libvorbis pulseaudio pyalsaaudio rust"
PKG_SECTION="service"
PKG_SHORTDESC="Librespot: play Spotify through LibreELEC using a Spotify app as a remote"
PKG_LONGDESC="Librespot ($PKG_VERSION) plays Spotify through LibreELEC using the open source librespot library using a Spotify app as a remote."
@ -43,7 +43,7 @@ configure_target() {
make_target() {
cd src
$CARGO_BUILD --no-default-features --features "alsa-backend with-avahi"
$CARGO_BUILD --no-default-features --features "alsa-backend pulseaudio-backend with-avahi"
cd "$PKG_BUILD/.$TARGET_NAME"/*/release
$STRIP librespot
}
@ -59,7 +59,6 @@ addon() {
mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID/bin"
cp "$PKG_BUILD/.$TARGET_NAME"/*/release/librespot \
"$(get_build_dir ffmpegx)/.install_pkg/usr/local/bin/ffmpegx" \
"$ADDON_BUILD/$PKG_ADDON_ID/bin"
mkdir -p "$ADDON_BUILD/$PKG_ADDON_ID/lib"

View File

@ -0,0 +1,19 @@
diff --git a/src/audio_backend/pulseaudio.rs b/src/audio_backend/pulseaudio.rs
index 3b9a09b..ce78062 100644
--- a/src/audio_backend/pulseaudio.rs
+++ b/src/audio_backend/pulseaudio.rs
@@ -23,12 +23,13 @@ impl Open for PulseAudioSink {
let name = CString::new("librespot").unwrap();
let description = CString::new("A spoty client library").unwrap();
+ let sink = CString::new("librespot_sink").unwrap();
let s = unsafe {
pa_simple_new(null(), // Use the default server.
name.as_ptr(), // Our application's name.
PA_STREAM_PLAYBACK,
- null(), // Use the default device.
+ sink.as_ptr(), // Our sink.
description.as_ptr(), // Description of our stream.
&ss, // Our sample format.
null(), // Use default channel map

View File

@ -1,22 +0,0 @@
From 697926d2058e8650a9dd559d90a8a130950c417a Mon Sep 17 00:00:00 2001
From: Sasha Hilton <sashahilton00@users.noreply.github.com>
Date: Tue, 27 Jun 2017 12:45:34 +0100
Subject: [PATCH] Update system_information_string
---
src/connection/mod.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/connection/mod.rs b/src/connection/mod.rs
index af134fc..cfb5000 100644
--- a/src/connection/mod.rs
+++ b/src/connection/mod.rs
@@ -41,7 +41,7 @@ pub fn authenticate(transport: Transport, credentials: Credentials, device_id: S
system_info => {
cpu_family: CpuFamily::CPU_UNKNOWN,
os: Os::OS_UNKNOWN,
- system_information_string: "This is not the client you are looking for".to_owned(),
+ system_information_string: "librespot".to_string() + "_" + &(version::short_sha()) + "_" + &(version::short_now()),
device_id: device_id,
},
version_string: version::version_string(),

View File

@ -0,0 +1,79 @@
diff -Naur librespot/src/player.rs librespot-hooks/src/player.rs
--- librespot/src/player.rs 2017-07-09 20:01:31.000000000 +0200
+++ librespot-hooks/src/player.rs 2017-07-17 09:13:26.300852918 +0200
@@ -212,7 +212,7 @@
Some(Err(e)) => panic!("Vorbis error {:?}", e),
None => {
self.sink.stop().unwrap();
- self.run_onstop();
+ info!("onstop 1");
let old_state = mem::replace(&mut self.state, PlayerState::Stopped);
old_state.signal_end_of_track();
@@ -224,6 +224,8 @@
debug!("command={:?}", cmd);
match cmd {
PlayerCommand::Load(track_id, play, position, end_of_track) => {
+ self.run_onstart();
+
if self.state.is_playing() {
self.sink.stop().unwrap();
}
@@ -232,7 +234,7 @@
Some(decoder) => {
if play {
if !self.state.is_playing() {
- self.run_onstart();
+ info!("onstart 1");
}
self.sink.start().unwrap();
@@ -242,7 +244,7 @@
};
} else {
if self.state.is_playing() {
- self.run_onstop();
+ info!("onstop 2");
}
self.state = PlayerState::Paused {
@@ -255,7 +257,7 @@
None => {
end_of_track.complete(());
if self.state.is_playing() {
- self.run_onstop();
+ info!("onstart 3");
}
}
}
@@ -276,7 +278,7 @@
if let PlayerState::Paused { .. } = self.state {
self.state.paused_to_playing();
- self.run_onstart();
+ info!("onstart 2");
self.sink.start().unwrap();
} else {
warn!("Player::play called from invalid state");
@@ -288,17 +290,19 @@
self.state.playing_to_paused();
self.sink.stop().unwrap();
- self.run_onstop();
+ info!("onstop 4");
} else {
warn!("Player::pause called from invalid state");
}
}
PlayerCommand::Stop => {
+ self.run_onstop();
+
match self.state {
PlayerState::Playing { .. } => {
self.sink.stop().unwrap();
- self.run_onstop();
+ info!("onstop 5");
self.state = PlayerState::Stopped;
}
PlayerState::Paused { .. } => {

View File

@ -84,6 +84,9 @@ init_alsa() {
. /etc/profile
oe_setup_addon service.librespot
LS_PORT="6666"
LS_SINK="librespot_sink"
LIBRESPOT="librespot --cache \"$ADDON_HOME/cache\"
--disable-audio-cache \
--name \"Librespot@$HOSTNAME\""
@ -99,14 +102,18 @@ if [ -n "$ls_p" -a -n "$ls_u" ]; then
fi
if [ "$ls_O" == "Kodi" ]; then
LIBRESPOT="$LIBRESPOT --backend pipe \
--onstart=\"kodi-send \
--action=RunAddon(service.librespot)\" \
| ffmpegx -hide_banner -loglevel warning \
-avioflags direct -fflags nobuffer \
-re -ac 2 -ar 44100 -channel_layout stereo -f s16le -i pipe: \
-sdp_file \"$ADDON_HOME/librespot.sdp\" \
-f rtp rtp://127.0.0.1:5555"
if [ -z "$(pactl list short modules | grep sink_name=$LS_SINK)" ]; then
pactl load-module module-null-sink sink_name="$LS_SINK" > /dev/null
fi
pactl suspend-sink "$LS_SINK" 1
if [ -z "$(pactl list short modules | grep source=$LS_SINK.monitor)" ]; then
pactl load-module module-rtp-send source="$LS_SINK.monitor" \
destination_ip=127.0.0.1 port="$LS_PORT" source_ip=127.0.0.1 > /dev/null
fi
LIBRESPOT="$LIBRESPOT \
--backend pulseaudio \
--onstart=\"kodi-send --action=RunAddon(service.librespot,start)\" \
--onstop=\"kodi-send --action=RunAddon(service.librespot,stop)\""
else
init_alsa
if [ -n "$ls_o" ]; then

View File

@ -24,21 +24,20 @@ import xbmc
import xbmcaddon
import xbmcgui
PORT = '6666'
SINK = 'librespot_sink'
ICON = xbmcaddon.Addon().getAddonInfo('icon')
ID = xbmcaddon.Addon().getAddonInfo('id')
NAME = xbmcaddon.Addon().getAddonInfo('name')
PROFILE = xbmcaddon.Addon().getAddonInfo('profile')
STRINGS = xbmcaddon.Addon().getLocalizedString
ITEM = os.path.join(PROFILE, 'librespot.sdp')
LISTITEM = xbmcgui.ListItem(NAME)
LISTITEM.setArt({'thumb': ICON})
def addon():
if len(sys.argv) == 1:
pass
elif sys.argv[1] == 'start':
Player().play()
elif sys.argv[1] == 'stop':
Player().stop()
elif sys.argv[1] == 'wizard':
dialog = xbmcgui.Dialog()
while True:
@ -54,10 +53,6 @@ def addon():
break
def systemctl(command):
subprocess.call(['systemctl', command, ID])
class Monitor(xbmc.Monitor):
def __init__(self, *args, **kwargs):
@ -70,37 +65,69 @@ class Monitor(xbmc.Monitor):
class Player(xbmc.Player):
ITEM = 'rtp://127.0.0.1:{port}'.format(port=PORT)
LISTITEM = xbmcgui.ListItem(NAME)
LISTITEM.setArt({'thumb': xbmcaddon.Addon().getAddonInfo('icon')})
def __init__(self, *args, **kwargs):
super(Player, self).__init__(self)
self.service = Service()
self.sink = Sink()
if self.isPlaying():
self.onPlayBackStarted()
def onPlayBackEnded(self):
if not self.islibrespot:
xbmc.sleep(5000)
if not self.isPlaying():
systemctl('start')
xbmc.sleep(1000)
if not self.isPlaying():
self.service.restart()
def onPlayBackStarted(self):
if self.getPlayingFile() == ITEM:
self.islibrespot = True
else:
self.islibrespot = False
systemctl('stop')
if self.getPlayingFile() != self.ITEM:
self.sink.suspend()
self.service.stop()
def onPlayBackStopped(self):
systemctl('restart')
self.service.restart()
def play(self):
if not self.isPlaying():
super(Player, self).play(ITEM, LISTITEM)
self.sink.unsuspend()
super(Player, self).play(self.ITEM, self.LISTITEM)
def stop(self):
if self.isPlaying():
if self.getPlayingFile() == ITEM:
super(Player, self).stop()
self.sink.suspend()
if self.isPlaying() and self.getPlayingFile() == self.ITEM:
super(Player, self).stop()
else:
systemctl('restart')
self.service.restart()
class Service():
def __init__(self):
self.id = xbmcaddon.Addon().getAddonInfo('id')
def restart(self):
self.systemctl('restart')
def start(self):
self.systemctl('start')
def stop(self):
self.systemctl('stop')
def systemctl(self, command):
subprocess.call(['systemctl', command, self.id])
class Sink():
def suspend(self):
subprocess.call(['pactl', 'suspend-sink', SINK, '1'])
def unsuspend(self):
subprocess.call(['pactl', 'suspend-sink', SINK, '0'])
if __name__ == '__main__':
Monitor().waitForAbort()

View File

@ -1,10 +1,11 @@
[Unit]
Description=librespot
After=network-online.target
Requires=network-online.target
After=network-online.target sound.target
Requires=network-online.target sound.target
[Service]
ExecStart=/bin/sh /storage/.kodi/addons/service.librespot/bin/librespot.start
ExecStopPost=/usr/bin/pactl suspend-sink librespot_sink 1
Restart=on-failure
[Install]