Skip to content

RPi Steamlink Support #4029

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv)

. $(get_pkg_directory libX11)/package.mk

PKG_NAME="steamlink-libX11"
PKG_LONGDESC="libX11 for steamlink-rpi"
PKG_URL=""
PKG_DEPENDS_TARGET+=" libX11"
PKG_BUILD_FLAGS+=" -sysroot"

unpack() {
mkdir -p ${PKG_BUILD}
tar --strip-components=1 -xf ${SOURCES}/${PKG_NAME:10}/${PKG_NAME:10}-${PKG_VERSION}.tar.bz2 -C ${PKG_BUILD}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
diff -Naur libX11-1.6.0/nls/Makefile.am libX11-1.6.0.patch/nls/Makefile.am
--- libX11-1.6.0/nls/Makefile.am 2013-06-04 04:21:16.000000000 +0200
+++ libX11-1.6.0.patch/nls/Makefile.am 2013-06-04 17:07:11.962522739 +0200
@@ -36,10 +36,10 @@
< locale.dir.l1 > locale.dir.l2
cat locale.dir.l2 locale.dir.l1 > locale.dir

-if HAVE_PERL
-LOG_COMPILER = $(PERL)
-TESTS = compose-check.pl
-endif HAVE_PERL
+# if HAVE_PERL
+# LOG_COMPILER = $(PERL)
+# TESTS = compose-check.pl
+# endif HAVE_PERL


# Per-locale data files
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv)

. $(get_pkg_directory libXext)/package.mk

PKG_NAME="steamlink-libXext"
PKG_LONGDESC="libXext for steamlink-rpi"
PKG_URL=""
PKG_DEPENDS_TARGET+=" libXext"
PKG_BUILD_FLAGS+=" -sysroot"

unpack() {
mkdir -p ${PKG_BUILD}
tar --strip-components=1 -xf ${SOURCES}/${PKG_NAME:10}/${PKG_NAME:10}-${PKG_VERSION}.tar.bz2 -C ${PKG_BUILD}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv)

. $(get_pkg_directory libjpeg-turbo)/package.mk

PKG_NAME="steamlink-libjpeg-turbo"
PKG_LONGDESC="libjpeg-turbo for steamlink"
PKG_URL=""
PKG_DEPENDS_UNPACK+=" libjpeg-turbo"
PKG_BUILD_FLAGS+=" -sysroot"

PKG_CMAKE_OPTS_TARGET+=" -DENABLE_STATIC=OFF \
-DENABLE_SHARED=ON \
-DWITH_JPEG8=OFF"

unpack() {
mkdir -p ${PKG_BUILD}
tar --strip-components=1 -xf ${SOURCES}/${PKG_NAME:10}/${PKG_NAME:10}-${PKG_VERSION}.tar.gz -C ${PKG_BUILD}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv)

. $(get_pkg_directory libpng)/package.mk

PKG_NAME="steamlink-libpng"
PKG_LONGDESC="libpng for steamlink-rpi"
PKG_URL=""
PKG_DEPENDS_TARGET+=" libpng"
PKG_BUILD_FLAGS+=" -sysroot"

PKG_CONFIGURE_OPTS_TARGET+=" --disable-static \
--enable-shared"

unpack() {
mkdir -p ${PKG_BUILD}
tar --strip-components=1 -xf ${SOURCES}/${PKG_NAME:10}/${PKG_NAME:10}-${PKG_VERSION}.tar.xz -C ${PKG_BUILD}
}

post_makeinstall_target() {
:
}
2 changes: 2 additions & 0 deletions packages/addons/script/steamlink-rpi/changelog.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
100
- Initial Release
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions packages/addons/script/steamlink-rpi/package.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv)

PKG_NAME="steamlink-rpi"
PKG_VERSION="1.0"
PKG_REV="100"
PKG_ARCH="arm"
PKG_ADDON_PROJECTS="RPi2"
PKG_LICENSE="custom"
PKG_SITE="https://support.steampowered.com/kb_article.php?ref=6153-IFGH-6589"
PKG_DEPENDS_TARGET="toolchain steamlink-libjpeg-turbo steamlink-libpng steamlink-libXext steamlink-libX11"
PKG_SECTION="script"
PKG_SHORTDESC="Steam Link App for Raspberry Pi"
PKG_LONGDESC="Installs the Steam Link App for Raspberry Pi 3 or newer from Valve for use in streaming from Steam clients. Addon is not associated with Valve. Steam and the Steam logo are trademarks and/or registered trademarks of Valve Corporation in the U.S. and/or other countries."
PKG_TOOLCHAIN="manual"

PKG_IS_ADDON="yes"
PKG_ADDON_NAME="steamlink-rpi"
PKG_ADDON_TYPE="xbmc.python.script"
PKG_ADDON_PROVIDES="executable"

PKG_STEAMLINK_VERSION="1.1.64.162"
PKG_STEAMLINK_HASH="1ea1c41802bd6cc3efdba7ba258b9d25d5c07cdd1d0ffe9f2d47597588a09155"

addon() {
# Add needed libraries
mkdir -p ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs

# libpng
cp -L $(get_install_dir steamlink-libpng)/usr/lib/libpng16.so.16 ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs/

# libjpeg-turbo
cp -L $(get_install_dir steamlink-libjpeg-turbo)/usr/lib/libjpeg.so.62 ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs/

# libXext
cp -L $(get_install_dir steamlink-libXext)/usr/lib/libXext.so.6 ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs/

# libX11
cp -L $(get_install_dir steamlink-libX11)/usr/lib/libX11.so.6 ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs/
cp -L $(get_install_dir steamlink-libX11)/usr/lib/libX11-xcb.so.1 ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs/
}

post_install_addon() {
# Add steamlink version to download to addon
sed -e "s/@STEAMLINK_VERSION@/${PKG_STEAMLINK_VERSION}/" \
-e "s/@STEAMLINK_HASH@/${PKG_STEAMLINK_HASH}/" \
-i ${ADDON_BUILD}/${PKG_ADDON_ID}/default.py
}
46 changes: 46 additions & 0 deletions packages/addons/script/steamlink-rpi/source/bin/steamlink-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/sh

# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv)

. /etc/profile
oe_setup_addon script.steamlink-rpi

# Steamlink not ready; abort
if [ ! -f ${ADDON_DIR}/prep.ok ]; then
if [ -f /tmp/steamlink.watchdog ]; then
rm /tmp/steamlink.watchdog
fi
exit 0
fi

# Adapt to meet steamlink requirements

# mount an overlay for udev rules and reload udev rules
if [ $(grep -c "/lib/udev/rules.d" /proc/mounts) -eq 0 ]; then
if [ ! -d ${ADDON_DIR}/steamlink/.overlay ]; then
mkdir -p ${ADDON_DIR}/steamlink/.overlay
fi
mount -t overlay overlay -o lowerdir=/lib/udev/rules.d,upperdir=${ADDON_DIR}/steamlink/udev/rules.d/,workdir=${ADDON_DIR}/steamlink/.overlay /lib/udev/rules.d
udevadm trigger
fi

# use alsa for steamlink; make sure a sound card is present
if [ ! -d /proc/asound ]; then
dtparam audio=on
fi

# Launch steamlink
# xxx: shutdown kodi or controller input goes to kodi too
systemctl stop kodi
# xxx: shutdown pulseaudio or no sound
systemctl stop pulseaudio
${ADDON_DIR}/steamlink/steamlink.sh
sleep 2
# Cleanup
umount /lib/udev/rules.d
rm /tmp/steamlink.watchdog
systemctl start pulseaudio
systemctl start kodi

exit 0
132 changes: 132 additions & 0 deletions packages/addons/script/steamlink-rpi/source/default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv)

import os
import tarfile
import subprocess
import xbmcaddon
import xbmcgui
from hashlib import sha256
from pathlib import Path
from shutil import rmtree
from sys import exit
from tempfile import TemporaryDirectory
from time import sleep
from urllib.request import urlretrieve


STEAMLINK_VERSION = "@STEAMLINK_VERSION@"
STEAMLINK_HASH = "@STEAMLINK_HASH@"
STEAMLINK_TARBALL_NAME = f"steamlink-rpi3-{STEAMLINK_VERSION}.tar.gz"
STEAMLINK_URL = f"http://media.steampowered.com/steamlink/rpi/{STEAMLINK_TARBALL_NAME}"
ADDON_DIR = xbmcaddon.Addon().getAddonInfo("path")
PROGRESS_BAR = xbmcgui.DialogProgress()


def GetSHA256Hash(file_name):
""" Get sha256sum of file_name in 8kb chunks """
with open(file_name,"rb") as file:
SHA256HASH = sha256()
while True:
data_block = file.read(8192)
if not data_block:
break
SHA256HASH.update(data_block)
return SHA256HASH.hexdigest()

def GetRPiProcessor():
""" Use vcgencmd to obtain cpu identifier as int """
VC_CMD_OUTPUT=subprocess.check_output(["vcgencmd", "otp_dump"], encoding="utf-8")

for line in VC_CMD_OUTPUT.splitlines():
if line[0:3] == "30:":
PROCESSOR=line.split(":")[1] # entire processor id
return int(PROCESSOR[4:5]) # only cpu id

return 0

def OutputFileContents(file):
""" Read everything in file """
with open(file) as data:
return data.read()

def ProgressBarReport(chunk_count, chunk_size, total_size):
""" Use urlretrieve's reporthook to report progress """
if total_size != -1:
progress_percentage = int(chunk_count * chunk_size / total_size * 100)
PROGRESS_BAR.update(progress_percentage)
else:
PROGRESS_BAR.update(0, "Filesize Unknown")

def DownloadSteamlink():
""" Download Steam Link for RPi """
with TemporaryDirectory() as temp_dir:
STEAMLINK_TEMP_PATH = os.path.join(temp_dir, STEAMLINK_TARBALL_NAME)

PROGRESS_BAR.create("Steam Link", f"Downloading Steam Link Version: {STEAMLINK_VERSION}...")
urlretrieve(STEAMLINK_URL, STEAMLINK_TEMP_PATH, ProgressBarReport)

if tarfile.is_tarfile(STEAMLINK_TEMP_PATH):
DOWNLOAD_HASH = GetSHA256Hash(STEAMLINK_TEMP_PATH)
if STEAMLINK_HASH == DOWNLOAD_HASH:
PROGRESS_BAR.update(100, f"Extracting Steam Link Version {STEAMLINK_VERSION}...")
STEAMLINK_TARBALL = tarfile.open(STEAMLINK_TEMP_PATH)
STEAMLINK_TARBALL.extractall(path=f"{ADDON_DIR}/")
PROGRESS_BAR.close()
else:
PROGRESS_BAR.update(0, "Download Error: bad file hash. Try again later.")
sleep(5)
PROGRESS_BAR.close()
exit(1)
else:
PROGRESS_BAR.update(0, "Download Error: bad download or missing file")
sleep(5)
PROGRESS_BAR.close()
exit(1)

def PrepareSteamlink():
""" System preparation before launching Steam Link """

# Disable Steam Link's cpu check
if not os.path.isfile(f"{ADDON_DIR}/steamlink/.ignore_cpuinfo"):
Path(f"{ADDON_DIR}/steamlink/.ignore_cpuinfo").touch()

# Add system libraries to bundled
for file in os.listdir(f"{ADDON_DIR}/system-libs/"):
os.symlink(f"{ADDON_DIR}/system-libs/{file}", f"{ADDON_DIR}/steamlink/lib/{file}")

# systemd setup
if not os.path.isfile(f"{str(Path.home())}/.config/system.d/steamlink-rpi.watchdog.service"):
os.symlink(f"{ADDON_DIR}/system.d/steamlink-rpi.watchdog.service", f"{str(Path.home())}/.config/system.d/steamlink-rpi.watchdog.service")
subprocess.run(["systemctl", "enable", f"{str(Path.home())}/.config/system.d/steamlink-rpi.watchdog.service"])

# Finalize
Path(f"{ADDON_DIR}/prep.ok").touch()

def StartSteamlink():
# Check if running on RPi3 or higher
if not os.path.isfile(f"{ADDON_DIR}/steamlink/.ignore_cpuinfo") and GetRPiProcessor() < 2:
xbmcgui.Dialog.notification("Steam Link", "Steam Link will not run on this hardware. Aborting...", xbmcgui.NOTIFICATION_INFO, 5000)
exit(1)

# Check if addon wants to update Steam Link
if os.path.isfile(f"{ADDON_DIR}/steamlink/version.txt"):
STEAMLINK_INSTALLED_VERSION = OutputFileContents(f"{ADDON_DIR}/steamlink/version.txt").rstrip()

# Update Steamlink handling
if STEAMLINK_VERSION != STEAMLINK_INSTALLED_VERSION:
rmtree(f"{ADDON_DIR}/steamlink/")
os.remove(f"{ADDON_DIR}/prep.ok")

# Download Steam Link if not present
if not os.path.isfile(f"{ADDON_DIR}/prep.ok"):
DownloadSteamlink()
PrepareSteamlink()

# Start Steamlink
xbmcgui.Dialog().notification("Steam Link", "Starting Steam Link", xbmcgui.NOTIFICATION_INFO, 3000)
Path("/tmp/steamlink.watchdog").touch()
subprocess.run(["systemctl", "start", "steamlink-rpi.watchdog.service"])


StartSteamlink()
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[Unit]
Description="Watchdog to start Steamlink"
After=graphical.target
ConditionPathExists=/tmp/steamlink.watchdog

[Service]
Type=oneshot
ExecStart=/storage/.kodi/addons/script.steamlink-rpi/bin/steamlink-start.sh

[Install]
WantedBy=graphical.target