Captures the TV's audio and sends it out over several transports. The
primary one is HyperHDR: RTP/L16 to a host-side loopback device, since
HyperHDR has no network audio input of its own. A second route renders
the spectrum on the TV and sends FlatBuffers images to port 19400
instead, for setups where touching the host's sound config is not an
option.
native/ the service: capture backends (PulseAudio, ALSA, exec,
test tone, all dlopen-based), DSP, and one file per sink
frontend/ D-pad driven UI at a fixed 1920x1080
servicefiles/ native service manifest plus the boot script
host/ RTP receiver and the loopback installer for the HyperHDR
machine
tools/ build/package, asset generation, Homebrew Channel
manifest, on-TV probe
test/ host-side suites: FlatBuffers and RTP verified against
real decoders, the engine end to end, the page in jsdom
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
144 lines
4.3 KiB
Bash
Executable File
144 lines
4.3 KiB
Bash
Executable File
#!/bin/sh
|
|
# Reports what audio a rooted webOS TV actually exposes.
|
|
#
|
|
# Which capture backend works depends on the model and firmware: some sets run
|
|
# PulseAudio with a monitor source, some only offer ALSA, some need an external
|
|
# helper. Run this once on the TV and the answer is usually obvious.
|
|
#
|
|
# On the TV:
|
|
# sh tv-probe.sh
|
|
#
|
|
# From here, over the Homebrew Channel's ssh:
|
|
# ssh -p 9922 root@TV-IP 'sh -s' < tools/tv-probe.sh
|
|
# ares-shell --device tv -r "$(cat tools/tv-probe.sh)"
|
|
#
|
|
# Reads only. Nothing here changes the TV.
|
|
|
|
header() {
|
|
printf '\n=== %s\n' "$1"
|
|
}
|
|
|
|
have() {
|
|
command -v "$1" >/dev/null 2>&1
|
|
}
|
|
|
|
show() {
|
|
# show <label> <file>
|
|
if [ -r "$2" ]; then
|
|
printf -- '--- %s\n' "$1"
|
|
cat "$2"
|
|
fi
|
|
}
|
|
|
|
header "Identity"
|
|
printf 'uid: %s\n' "$(id -u 2>/dev/null)"
|
|
printf 'kernel: %s\n' "$(uname -r 2>/dev/null)"
|
|
printf 'machine: %s\n' "$(uname -m 2>/dev/null)"
|
|
for f in /etc/starfish-release /etc/os-release; do
|
|
show "$f" "$f"
|
|
done
|
|
if [ -r /var/run/nyx/device_info.json ]; then
|
|
printf -- '--- device info\n'
|
|
cat /var/run/nyx/device_info.json
|
|
printf '\n'
|
|
fi
|
|
|
|
header "Sound devices"
|
|
if [ -d /dev/snd ]; then
|
|
ls -l /dev/snd
|
|
else
|
|
echo "no /dev/snd — the ALSA backend cannot work"
|
|
fi
|
|
|
|
header "ALSA"
|
|
show "cards" /proc/asound/cards
|
|
show "pcm" /proc/asound/pcm
|
|
show "modules" /proc/asound/modules
|
|
if [ -d /proc/asound ]; then
|
|
for dir in /proc/asound/card*/pcm*c; do
|
|
[ -d "$dir" ] || continue
|
|
printf -- '--- capture stream %s\n' "$dir"
|
|
[ -r "$dir/info" ] && cat "$dir/info"
|
|
done
|
|
else
|
|
echo "no /proc/asound — no ALSA on this firmware"
|
|
fi
|
|
|
|
header "Tools"
|
|
for tool in arecord aplay amixer pactl parec pacat pulseaudio gst-launch-1.0 ffmpeg luna-send; do
|
|
if have "$tool"; then
|
|
printf '%-16s %s\n' "$tool" "$(command -v "$tool")"
|
|
else
|
|
printf '%-16s -\n' "$tool"
|
|
fi
|
|
done
|
|
|
|
header "Libraries"
|
|
# The service dlopen()s these rather than linking them, so what matters is
|
|
# whether the file exists at all and under which soname.
|
|
for pattern in \
|
|
/usr/lib/libasound.so* /lib/libasound.so* \
|
|
/usr/lib/libpulse.so* /lib/libpulse.so* \
|
|
/usr/lib/libpulse-simple.so* /lib/libpulse-simple.so*
|
|
do
|
|
for lib in $pattern; do
|
|
[ -e "$lib" ] && ls -l "$lib"
|
|
done
|
|
done 2>/dev/null
|
|
|
|
header "PulseAudio"
|
|
ps ax 2>/dev/null | grep -i '[p]ulseaudio' || ps 2>/dev/null | grep -i '[p]ulseaudio' \
|
|
|| echo "no pulseaudio process found"
|
|
printf -- '--- sockets\n'
|
|
for sock in /var/run/pulse/native /run/pulse/native /tmp/pulse-*/native \
|
|
/var/run/pulse/*.socket /dev/socket/pulse/*
|
|
do
|
|
[ -e "$sock" ] && ls -l "$sock"
|
|
done 2>/dev/null
|
|
printf -- '--- environment of the audio daemon\n'
|
|
for pid in $(ps ax 2>/dev/null | grep -i '[a]udiod\|[p]ulseaudio' | awk '{print $1}'); do
|
|
if [ -r "/proc/$pid/environ" ]; then
|
|
printf 'pid %s: ' "$pid"
|
|
tr '\0' '\n' < "/proc/$pid/environ" | grep -i 'PULSE\|XDG_RUNTIME' | tr '\n' ' '
|
|
printf '\n'
|
|
fi
|
|
done
|
|
|
|
if have pactl; then
|
|
printf -- '--- pactl info\n'
|
|
PULSE_SERVER="${PULSE_SERVER:-unix:/var/run/pulse/native}" pactl info 2>&1 | head -20
|
|
printf -- '--- sources (monitors are what to capture)\n'
|
|
PULSE_SERVER="${PULSE_SERVER:-unix:/var/run/pulse/native}" pactl list short sources 2>&1
|
|
fi
|
|
|
|
if have arecord; then
|
|
header "ALSA capture devices (arecord -l)"
|
|
arecord -l 2>&1
|
|
printf -- '--- PCMs (arecord -L)\n'
|
|
arecord -L 2>&1 | head -40
|
|
fi
|
|
|
|
header "What to put in the app"
|
|
cat <<'EOF'
|
|
Look at the output above and set Capture > Backend accordingly:
|
|
|
|
PulseAudio a pulseaudio process and a socket exist, and pactl lists a
|
|
source ending in ".monitor". Use that name as the Device, or
|
|
leave Device blank to take the default monitor.
|
|
|
|
ALSA /proc/asound lists a card with a capture PCM. Use hw:X,Y with
|
|
the card and device numbers from "arecord -l".
|
|
|
|
Command neither works but arecord/parec/gst-launch is present. Set the
|
|
Command to something that writes raw S16LE to stdout, e.g.
|
|
arecord -D hw:0,0 -f S16_LE -r 48000 -c 2 -t raw
|
|
|
|
Test tone nothing at all. Use this to prove the network path works while
|
|
you keep looking.
|
|
|
|
If everything is missing, the audio path on this firmware is locked inside the
|
|
proprietary audio daemon. Every sink depends on captured audio, so the test
|
|
tone is all that will run until a capture route is found — use it to prove the
|
|
network side, then come back to this list.
|
|
EOF
|