mirror of
https://github.com/morgan9e/UxPlay
synced 2026-04-14 00:04:13 +09:00
Add support for bluetooth LE beacon service discovery
based on remarkable work by @connorh315 on getting this working
This commit is contained in:
237
README.html
237
README.html
@@ -8,6 +8,19 @@ developed at the GitHub site <a href="https://github.com/FDH2/UxPlay"
|
||||
class="uri">https://github.com/FDH2/UxPlay</a> (where ALL user issues
|
||||
should be posted, and latest versions can be found).</strong></h3>
|
||||
<ul>
|
||||
<li><p><strong>NEW on github</strong>: Support for <strong>service
|
||||
discovery using a Bluetooth LE “beacon”</strong> (as an alternative to
|
||||
Bonjour/Rendezvous DNS-SD service discovery). The user must set up a
|
||||
Bluetooth LE “beacon”, (a USB 4.0 or later “dongle” can be used). See
|
||||
instructions below. The beacon runs independently of UxPlay and
|
||||
regularly broadcasts a Bluetooth LE (“Low Energy”) 44 byte packet
|
||||
informing nearby iOS/macOS devices of the local IPv4 network address of
|
||||
the UxPlay server, which they can use to contact it on TCP port 7000.
|
||||
Instructions for manually setting up such a beacon in Linux are <a
|
||||
href="#bluetooth-le-beacon-setup">given below</a>. <strong>It is hoped
|
||||
that users will submit Pull Requests contributing scripts for automating
|
||||
beacon setup on all platforms. (Python may be an appropriate language
|
||||
choice)</strong></p></li>
|
||||
<li><p><strong>NEW on github</strong>: (for Linux/*BSD Desktop
|
||||
Environments using D-Bus). New option <code>-scrsv <n></code>
|
||||
provides screensaver inhibition (e.g., to prevent screensaver function
|
||||
@@ -217,8 +230,10 @@ necessary, it is not necessary that the local network also be of the
|
||||
“.local” mDNS-based type). On Linux and BSD Unix servers, this is
|
||||
usually provided by <a href="https://www.avahi.org">Avahi</a>, through
|
||||
the avahi-daemon service, and is included in most Linux distributions
|
||||
(this service can also be provided by macOS, iOS or Windows
|
||||
servers).</p>
|
||||
(this service can also be provided by macOS, iOS or Windows servers).
|
||||
There is now an alternative Service discovery method, using a Bluetooth
|
||||
LE “beacon” See below for <a
|
||||
href="#bluetooth-le-beacon-setup">instructions</a>.</p>
|
||||
<p>Connections to the UxPlay server by iOS/MacOS clients can be
|
||||
initiated both in <strong>AirPlay Mirror</strong> mode (which streams
|
||||
lossily-compressed AAC audio while mirroring the client screen, or in
|
||||
@@ -549,7 +564,8 @@ comments and ignored.</p>
|
||||
you can specify fullscreen mode with the <code>-fs</code> option, or
|
||||
toggle into and out of fullscreen mode with F11 or (held-down left
|
||||
Alt)+Enter keys. Use Ctrl-C (or close the window) to terminate it when
|
||||
done. If the UxPlay server is not seen by the iOS client’s drop-down
|
||||
done.</p>
|
||||
<p>If the UxPlay server is not seen by the iOS client’s drop-down
|
||||
“Screen Mirroring” panel, check that your DNS-SD server (usually
|
||||
avahi-daemon) is running: do this in a terminal window with
|
||||
<code>systemctl status avahi-daemon</code>. If this shows the
|
||||
@@ -564,6 +580,9 @@ opened: <strong>if a firewall is active, also open UDP port 5353 (for
|
||||
mDNS queries) needed by Avahi</strong>. See <a
|
||||
href="#troubleshooting">Troubleshooting</a> below for help with this or
|
||||
other problems.</p>
|
||||
<p>Note that there is now an alternative Service Discovery method using
|
||||
a Bluetooth LE beacon. See the instructions on <a
|
||||
href="#bluetooth-le-beacon-setup">Bluetooth beacon setup</a>.</p>
|
||||
<ul>
|
||||
<li><p>Unlike an Apple TV, the UxPlay server does not by default require
|
||||
clients to initially “pair” with it using a pin code displayed by the
|
||||
@@ -1405,6 +1424,12 @@ to a file to <em>n</em> or less. To change the name <em>audiodump</em>,
|
||||
use -admp [n] <em>filename</em>. <em>Note that (unlike dumped video) the
|
||||
dumped audio is currently only useful for debugging, as it is not
|
||||
containerized to make it playable with standard audio players.</em></p>
|
||||
<p><strong>-ble <em>filename</em></strong>. Enable Bluetooth beacon
|
||||
Service Discovery. The PID and process name of the UxPlay process is
|
||||
recorded in <em>filename</em>, which must be the full path to a
|
||||
writeable file. (This file is created when UxPlay starts and deleted
|
||||
when it stops.) <strong>See below for beacon setup
|
||||
instructions.</strong></p>
|
||||
<p><strong>-d [n]</strong> Enable debug output; optional argument n=1
|
||||
suppresses audio/video packet data in debug output. Note: this does not
|
||||
show GStreamer error or debug messages. To see GStreamer error and
|
||||
@@ -1412,6 +1437,209 @@ warning messages, set the environment variable GST_DEBUG with “export
|
||||
GST_DEBUG=2” before running uxplay. To see GStreamer information
|
||||
messages, set GST_DEBUG=4; for DEBUG messages, GST_DEBUG=5; increase
|
||||
this to see even more of the GStreamer inner workings.</p>
|
||||
<h1 id="bluetooth-le-beacon-setup">Bluetooth LE beacon setup</h1>
|
||||
<p>When uxplay is started with the option
|
||||
<code>uxplay -ble <path-to-writeable-file></code>, it writes a 20
|
||||
byte data file containing (4 bytes) the process ID (PID) as a uint32_t
|
||||
32-bit unsigned integer, and (16 bytes) up to 15 bytes of the process
|
||||
name (usually “uxplay”) as a null-terminated string, padded with zeroes
|
||||
to fill 16 bytes. The file is deleted if UxPlay is terminated normally
|
||||
(without a segfault), and could be used to determine if an instance of
|
||||
uxplay is running. <strong>This file is provided for possible future use
|
||||
in a script for controlling the beacon, and will not be used
|
||||
here</strong>.</p>
|
||||
<p>You may need to use a cheap USB Bluetooth dongle if your system does
|
||||
not have Bluetooth 4.0 or later, or will not let you use it for LE (Low
|
||||
Energy) transmissions.</p>
|
||||
<p>These instructions are tested on Linux using the Bluez Bluetooth
|
||||
stack. They use the <code>hcitool</code> and <code>hciconfig</code>
|
||||
utilities which directly access the HCI stack, and need elevated
|
||||
privileges (use <code>sudo</code>). These utilities have been declared
|
||||
“deprecated” and “obsolete” by BlueZ developers: on Debian-based Linux
|
||||
<code>sudo apt install bluez</code> still provides <code>hcitool</code>,
|
||||
but on some other Linux distributions, it is split off from the main
|
||||
BlueZ package into an “extra” package with a name like
|
||||
“bluez-deprecated”. If we get the AirPlay beacon to work using the newer
|
||||
<code>bluetoothctl</code> utility, these instructions will be
|
||||
updated.</p>
|
||||
<ul>
|
||||
<li><p><strong>These manual instructions will hopefully be soon
|
||||
superseded by e.g. python scripts that automate beacon control, probably
|
||||
using D-Bus on Linux. Please submit any such scripts you get working for
|
||||
possible packaging together with UxPlay</strong>. Note that the Apple
|
||||
Service Discovery beacon is not a standard “<strong>ibeacon</strong>”,
|
||||
and cannot be set up with unmodified “ibeacon”-specific
|
||||
applications.</p></li>
|
||||
<li><p>For testing Bluetooth beacon Service Discovery on Linux, you will
|
||||
need to suppress the avahi-daemon which provides DNS-SD Service
|
||||
Discovery on UxPlay’s Host system (replace <code>mask</code> and
|
||||
<code>stop</code> below by <code>unmask</code> and <code>start</code> to
|
||||
restore DNS-SD service).;</p></li>
|
||||
</ul>
|
||||
<pre><code>$ sudo systemctl mask avahi-daemon.socket
|
||||
$ sudo systemctl stop avahi-daemon
|
||||
</code></pre>
|
||||
<p>Then verify that uxplay will not start without the
|
||||
<code>-ble <filename></code> option.</p>
|
||||
<p>Before starting, check that you have a Bluetooth device with
|
||||
“<code>hcitool dev</code>”</p>
|
||||
<pre><code>$hcitool dev
|
||||
Devices:
|
||||
hci1 E8:EA:6A:7C:3F:CC
|
||||
hci0 08:BE:AC:40:A9:DC</code></pre>
|
||||
<p>This shows two devices with their MAC addresses. You can use
|
||||
“<code>hciconfig -i</code>” to see which version of Bluetooth they
|
||||
implement: we require Bluetooth v4.0 or later. Choose which to use (we
|
||||
will use hci0), and reset it.</p>
|
||||
<pre><code>$ sudo hciconfig hci0 reset
|
||||
</code></pre>
|
||||
<ul>
|
||||
<li><strong>Step 1.</strong> First reconfigure the Bluetooth device
|
||||
(hci0):</li>
|
||||
</ul>
|
||||
<p><code>hcitool</code> sends HCI commands as a sequence of 1-byte
|
||||
hexadecimal octets. It echoes the length (here <code>plen</code> = 15
|
||||
bytes) and content of the sequence it sends to the Bluetooth HCI stack,
|
||||
and of the 4-byte “HCI Event” response it gets. Only the last byte of
|
||||
the response is important: <code>00</code> means the command succeded
|
||||
(other values are error codes).</p>
|
||||
<pre><code>
|
||||
$ sudo hcitool -i hci0 cmd 0x08 0x0006 0xa0 0x00 0xa0 0x00 0x03 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x07 0x00
|
||||
|
||||
< HCI Command: ogf 0x08, ocf 0x0006, plen 15
|
||||
A0 00 A0 00 03 01 00 00 00 00 00 00 00 07 00
|
||||
> HCI Event: 0x0e plen 4
|
||||
02 06 20 00
|
||||
</code></pre>
|
||||
<p>The above command configures the beacon:
|
||||
“<code>cmd 0x08 0x006</code>” means HCI LE (ogf=0x08) command number 6
|
||||
(ocf=0x0006) of the Blutooth LE stack.</p>
|
||||
<p>The first two message bytes “<code>0xa0 0x00</code>” means a 2-byte
|
||||
“unsigned short” value 0x00a0. (uint16_t integers such as 0xabcd are
|
||||
represented as two bytes “<code>0xcd, 0xab</code>”). This is the minimum
|
||||
interval AdvMin between beacon broadcasts, which are essentially
|
||||
simultaneous on all three advertising channels. The next two entries
|
||||
represent the maximum interval AdvMax, also set to 0x00a0, which means
|
||||
100 msec (200 msec would be 2 * 0x00a0 = 0x0140 or
|
||||
“<code>0x40 0x01</code>”). Setting AdvMin = AdvMax fixes the interval
|
||||
between transmissions. If AdvMin < AdvMax, the timing of each
|
||||
broadcast event relative to the previous one can be chosen flexibly to
|
||||
not overlap with any other task the bluetooth socket is carrying out.
|
||||
The allowed range of these parameters is 0x00a0 = 100 msec <= AdvMin
|
||||
<= AdvMax <= 0x4000 = 10.24 sec.</p>
|
||||
<p>An Apple TV (Gen 3) seems to use a fixed interval of 180 msec =
|
||||
0x0120 (“<code>0x20 0x01</code>”).</p>
|
||||
<p>The sixth byte TxAdd = “<code>0x01</code>” says that a random MAC
|
||||
“advertisement address”” AdvAddr for the Bluetooth device will be sent
|
||||
with the advertisement. If you wish to send the true hardware MAC
|
||||
address of the Bluetooth device, replace this byte by
|
||||
“<code>0x00</code>”.</p>
|
||||
<p><strong>These are the only parameters you might want to
|
||||
vary</strong>. The fifth byte 0x03 is the Advertising PDU type
|
||||
“ADV_NONCONN_IND” (a beacon that transmits without accepting
|
||||
connections) and the fourteenth byte 0x07 is a flag 0000 0111 that says
|
||||
to use all three Bluetooth LE advertising channels.</p>
|
||||
<ul>
|
||||
<li><strong>Step 2.</strong> (<strong>Optional: skip this if you changed
|
||||
byte 6 of the initial configuration message from</strong>
|
||||
“<code>0x01</code>” <strong>to</strong> “<code>0x00</code>”.) Use HCI LE
|
||||
command 5 (ocf=0x0005) to set private “advertising address” AdvAddr,
|
||||
which substitutes for the public MAC address of the Bluetooth
|
||||
device.</li>
|
||||
</ul>
|
||||
<p>This uses six random bytes r1,..,r6 and enters them as
|
||||
<code>r1 , r2 , r3, r4, r5, r0 = (r6 | 0x03)</code>, where the 6th
|
||||
byte has been masked with 0x03 = 00000011 so its last two bits are on,
|
||||
and the value r0 is restricted to 64 values “<code>0xrs</code>” where
|
||||
the second hexadecimal digit <code>s</code> is one of {3, 7, b, f},
|
||||
which indicates a “static random” private address that is guaranteed to
|
||||
not change between device reboots. Note that Apple TV’s use random
|
||||
private addresses without applying a mask to r6 to distinguish between
|
||||
different types.</p>
|
||||
<pre><code>
|
||||
$sudo hcitool -i hci0 cmd 0x08 0x0005 0x52 0xaa 0xaa 0x3a 0xb4 0x2f
|
||||
< HCI Command: ogf 0x08, ocf 0x0005, plen 6
|
||||
52 AA AA 3A B4 2F
|
||||
> HCI Event: 0x0e plen 4
|
||||
02 05 20 00 </code></pre>
|
||||
<p>On a Bluetooth packet sniffer with wireshark, this address displays
|
||||
as: <strong>Advertising Address: 2f:b4:3a:aa:aa:52</strong></p>
|
||||
<ul>
|
||||
<li><strong>Step 3.</strong> Now provide the advertising message, with
|
||||
HCI LE command 8 (ocf=0x0008):</li>
|
||||
</ul>
|
||||
<p>This sends a 32 byte message to the HCI LE stack, where the first
|
||||
byte is the length (here 0x0c = 12 bytes) of the significant part of the
|
||||
following 31 bytes: 12 significant bytes, padded with 19 zeros to a
|
||||
total message length of 32 bytes. (<code>hcitool</code> requires a
|
||||
message padded to the full 32 bytes, but only sends the significant
|
||||
bytes to the Bluetooth LE stack.)</p>
|
||||
<pre><code>$ sudo hcitool -i hci0 cmd 0x08 0x0008 0x0c 0x0b 0xff 0x4c 0x00 0x09 0x06 0x03 0x30 0xc0 0xa8 0x01 0xfd 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
< HCI Command: ogf 0x08, ocf 0x0008, plen 32
|
||||
0C 0B FF 4C 00 09 06 03 30 C0 A8 01 FD 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00
|
||||
> HCI Event: 0x0e plen 4
|
||||
01 08 20 00 </code></pre>
|
||||
<p>The only parts of this message that you must change are the four
|
||||
bytes 10,11,12,13, of the IPv4 address, here
|
||||
“<code>0xc0 0xa8 0x01 0xfd</code>”, (decimal 192 168 1 253, an IPv4
|
||||
address 192.168.1.253) which should be an IPv4 address at which the
|
||||
UxPlay server can receive requests from iOS/macOS clients at TCP port
|
||||
7000. You need to find what IPv4 address will work on the computer that
|
||||
hosts UxPlay (use <code>ifconfig</code>), convert each of the four
|
||||
numbers from decimal to hexadecimal, and replace bytes 13-16 of the
|
||||
message by them.</p>
|
||||
<ul>
|
||||
<li><strong>Step 4.</strong> Start advertising by the beacon with
|
||||
Bluetooth LE command 10 (ocf = 0x000a) and 1-byte message
|
||||
“<code>0x01</code>” = “on”.</li>
|
||||
</ul>
|
||||
<pre><code>$ sudo hcitool -i hci0 cmd 0x08 0x000a 0x01
|
||||
< HCI Command: ogf 0x08, ocf 0x000a, plen 1
|
||||
01
|
||||
> HCI Event: 0x0e plen 4
|
||||
02 0A 20 00 </code></pre>
|
||||
<p>(To stop advertising, use this command to send the 1-byte message
|
||||
“<code>0x00</code>” = “off”.)</p>
|
||||
<p>For creating a higher-level script, it might be useful to know that
|
||||
the length 0C = 12 bytes advertisement sent in step 3 has a single
|
||||
“Advertising Protocol Data Unit” (PDU):</p>
|
||||
<ul>
|
||||
<li>0B FF 4C 00 09 06 03 30 C0 A8 01 FD: length 0B = 11 bytes,
|
||||
consisting of: FF ( type = manufacturer-specific) 4C 00 (manufacturer
|
||||
code = 0x004c, Apple ) manufacturer data 09 06 03 30 C0 A8 01 FD</li>
|
||||
</ul>
|
||||
<p>The manufacturer data defined by Apple consists of a single Apple
|
||||
data unit: 09 (Apple type = AirPlay), 06 (Apple data length 6 bytes)
|
||||
Apple data 03 30 XX XX XX XX, broken down into 03 (flags: 0000 0011) 30
|
||||
(a seed) XX XX XX XX (IPv4 network address, written as four hexadecimal
|
||||
octets in standard order). (Apple TV’s use a random private “AdvAddr”
|
||||
address as described above, and periodically update it at about 20 min
|
||||
intervals, each time increasing the seed by 1.)</p>
|
||||
<p>Apple TV’s also insert a type-1 (“Flags”) 2-byte PDU
|
||||
“<code>02 01 1A</code>” before the manufacturer-specific PDU, increasing
|
||||
the significant length of the message to 0xf = 15 bytes. It turns out
|
||||
that the “Flags” PDU is “optional” for advertisements like beacons that
|
||||
do not allow client connections: in our tests on v4.0 and later dongles,
|
||||
Service Discovery still worked fine after dropping the “Flags” PDU.</p>
|
||||
<p>Both Linux and Windows have high-level interfaces that support users
|
||||
sending Advertising PDU’s, but restricted to type 0xff
|
||||
“manufacturer-specific-data” only, without any “Flags”. These should be
|
||||
used for automating beacon setup, and are: (Linux) Bluez <a
|
||||
href="https://github.com/bluez/bluez/blob/master/test/example-advertisement">LEAdvertisingManager1</a>
|
||||
and (Windows 10/11) <a
|
||||
href="https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementpublisher">BluetoothLEAdvertisementPublisherClass</a>
|
||||
(with an <a
|
||||
href="https://github.com/MicrosoftDocs/windows-dev-docs/blob/docs/uwp/devices-sensors/ble-beacon.md">example</a>).</p>
|
||||
<p><strong>We don’t know if these instructions can be modified to
|
||||
advertise IPv6 addresses: if you know of any verified support for
|
||||
Bluetooth LE IPv6 Service Discovery in newer AppleTV models, please let
|
||||
us know. Simply replacing the 4-byte IPv4 address with a 16-byte IPv6
|
||||
address (and adjusting the lengths at bytes 1, 5 and 10) does not seem
|
||||
to work, although perhaps we did not find the right value for byte 11
|
||||
(“Apple Flags”). If Apple’s Bluetooth LE Service Discovery has IPv6
|
||||
support, we need to examine the beacon advertisement packet for IPv6
|
||||
addresses with a Bluetooth sniffer.</strong></p>
|
||||
<h1 id="troubleshooting">Troubleshooting</h1>
|
||||
<p>Note: <code>uxplay</code> is run from a terminal command line, and
|
||||
informational messages are written to the terminal.</p>
|
||||
@@ -1743,7 +1971,8 @@ what version UxPlay claims to be.</p>
|
||||
<h1 id="changelog">Changelog</h1>
|
||||
<p>xxxx 2025-08-11 Render Audio cover-art inside UxPlay with -ca option
|
||||
(no file specified). (D-Bus based) option -scrsv <n> to inhibit
|
||||
screensaver while UxPlay is running (Linux/*BSD only).</p>
|
||||
screensaver while UxPlay is running (Linux/*BSD only). Add support for
|
||||
Service Discovery using a Bluetooth LE beacon.</p>
|
||||
<p>1.72.2 2025-07-07 Fix bug (typo) in DNS_SD advertisement introduced
|
||||
with -pw option. Update llhttp to v 9.3.0</p>
|
||||
<p>1.72.1 2025-06-06 minor update: fix regression in -reg option; add
|
||||
|
||||
205
README.md
205
README.md
@@ -2,6 +2,14 @@
|
||||
|
||||
### **Now developed at the GitHub site <https://github.com/FDH2/UxPlay> (where ALL user issues should be posted, and latest versions can be found).**
|
||||
|
||||
- **NEW on github**: Support for **service discovery using a Bluetooth LE "beacon"** (as an alternative to Bonjour/Rendezvous DNS-SD
|
||||
service discovery). The user must set up a Bluetooth LE "beacon", (a USB 4.0 or later "dongle" can be used). See instructions
|
||||
below. The beacon runs independently of UxPlay and regularly broadcasts a Bluetooth LE ("Low Energy") 44 byte packet informing nearby iOS/macOS devices of
|
||||
the local IPv4 network address of the UxPlay server, which they can use to contact it on TCP port 7000.
|
||||
Instructions for manually setting up such a beacon in Linux are [given below](#bluetooth-le-beacon-setup).
|
||||
__It is hoped that users will submit Pull Requests contributing scripts for automating beacon setup on all platforms.
|
||||
(Python may be an appropriate language choice)__
|
||||
|
||||
- **NEW on github**: (for Linux/*BSD Desktop Environments using D-Bus). New option `-scrsv <n>` provides screensaver inhibition (e.g., to
|
||||
prevent screensaver function while watching mirrored videos without keyboard or mouse
|
||||
activity): n = 0 (off) n=1 (on during video activity) n=2 (always on while UxPlay is running).
|
||||
@@ -201,7 +209,10 @@ necessary that the local network also be of the ".local" mDNS-based
|
||||
type). On Linux and BSD Unix servers, this is usually provided by
|
||||
[Avahi](https://www.avahi.org), through the avahi-daemon service, and is
|
||||
included in most Linux distributions (this service can also be provided
|
||||
by macOS, iOS or Windows servers).
|
||||
by macOS, iOS or Windows servers). There is now an alternative Service
|
||||
discovery method, using a Bluetooth LE "beacon" See below
|
||||
for [instructions](#bluetooth-le-beacon-setup).
|
||||
|
||||
|
||||
Connections to the UxPlay server by iOS/MacOS clients can be initiated
|
||||
both in **AirPlay Mirror** mode (which streams lossily-compressed AAC
|
||||
@@ -532,7 +543,9 @@ as comments and ignored.
|
||||
**Run uxplay in a terminal window**. On some systems, you can specify
|
||||
fullscreen mode with the `-fs` option, or toggle into and out of
|
||||
fullscreen mode with F11 or (held-down left Alt)+Enter keys. Use Ctrl-C
|
||||
(or close the window) to terminate it when done. If the UxPlay server is
|
||||
(or close the window) to terminate it when done.
|
||||
|
||||
If the UxPlay server is
|
||||
not seen by the iOS client's drop-down "Screen Mirroring" panel, check
|
||||
that your DNS-SD server (usually avahi-daemon) is running: do this in a
|
||||
terminal window with `systemctl status avahi-daemon`. If this shows the
|
||||
@@ -547,6 +560,10 @@ opened: **if a firewall is active, also open UDP port 5353 (for mDNS
|
||||
queries) needed by Avahi**. See [Troubleshooting](#troubleshooting)
|
||||
below for help with this or other problems.
|
||||
|
||||
Note that there is now an
|
||||
alternative Service Discovery method using a Bluetooth LE beacon.
|
||||
See the instructions on [Bluetooth beacon setup](#bluetooth-le-beacon-setup).
|
||||
|
||||
- Unlike an Apple TV, the UxPlay server does not by default require
|
||||
clients to initially "pair" with it using a pin code displayed by
|
||||
the server (after which the client "trusts" the server, and does not
|
||||
@@ -1424,6 +1441,12 @@ that (unlike dumped video) the dumped audio is currently only useful for
|
||||
debugging, as it is not containerized to make it playable with standard
|
||||
audio players.*
|
||||
|
||||
**-ble *filename***. Enable Bluetooth beacon Service Discovery.
|
||||
The PID and process name of the UxPlay process is recorded in
|
||||
*filename*, which must be the full path to a writeable file. (This file is created
|
||||
when UxPlay starts and deleted when it stops.) __See below for beacon setup
|
||||
instructions.__
|
||||
|
||||
**-d \[n\]** Enable debug output; optional argument n=1 suppresses audio/video
|
||||
packet data in debug output.
|
||||
Note: this does not show GStreamer error or
|
||||
@@ -1433,8 +1456,181 @@ uxplay. To see GStreamer information messages, set GST_DEBUG=4; for
|
||||
DEBUG messages, GST_DEBUG=5; increase this to see even more of the
|
||||
GStreamer inner workings.
|
||||
|
||||
# Troubleshooting
|
||||
# Bluetooth LE beacon setup
|
||||
|
||||
When uxplay is started with the option `uxplay -ble <path-to-writeable-file>`, it writes a 20 byte data file
|
||||
containing (4 bytes) the process ID (PID) as a uint32_t 32-bit unsigned integer, and
|
||||
(16 bytes) up to 15 bytes of the process name (usually "uxplay") as a null-terminated string,
|
||||
padded with zeroes to fill 16 bytes. The file is deleted if UxPlay is terminated normally (without a segfault),
|
||||
and could be used to determine if an instance of uxplay is running. **This file is provided for possible future use
|
||||
in a script for controlling the beacon, and will not be used here**.
|
||||
|
||||
You may need to use a cheap USB Bluetooth dongle if your system does not have Bluetooth 4.0 or later,
|
||||
or will not let you use it for LE (Low Energy) transmissions.
|
||||
|
||||
|
||||
These instructions are tested on Linux using the Bluez Bluetooth stack. They use the `hcitool` and ``hciconfig``
|
||||
utilities which directly access the HCI stack, and need elevated privileges (use `sudo`). These utilities
|
||||
have been declared "deprecated" and "obsolete" by BlueZ developers: on Debian-based Linux `sudo apt install bluez`
|
||||
still provides `hcitool`, but on some other Linux distributions, it is split off from the main BlueZ package into an "extra" package
|
||||
with a name like "bluez-deprecated". If we get the AirPlay beacon to work using the newer `bluetoothctl` utility, these instructions will be
|
||||
updated.
|
||||
|
||||
* **These manual instructions will hopefully be soon superseded by e.g. python scripts that automate beacon control, probably using D-Bus on Linux. Please
|
||||
submit any such scripts you get working for possible packaging together with UxPlay**. Note that the Apple Service Discovery beacon is
|
||||
not a standard "**ibeacon**", and cannot be set up with unmodified "ibeacon"-specific applications.
|
||||
|
||||
* For testing Bluetooth beacon Service Discovery on Linux, you will need to suppress the avahi-daemon which
|
||||
provides DNS-SD Service Discovery on UxPlay's Host system (replace `mask` and ``stop``
|
||||
below by `unmask` and ``start`` to restore DNS-SD service).;
|
||||
|
||||
```
|
||||
$ sudo systemctl mask avahi-daemon.socket
|
||||
$ sudo systemctl stop avahi-daemon
|
||||
|
||||
```
|
||||
Then verify that uxplay will not start without the `-ble <filename>` option.
|
||||
|
||||
|
||||
|
||||
Before starting, check that you have a Bluetooth device with "`hcitool dev`"
|
||||
|
||||
```
|
||||
$hcitool dev
|
||||
Devices:
|
||||
hci1 E8:EA:6A:7C:3F:CC
|
||||
hci0 08:BE:AC:40:A9:DC
|
||||
```
|
||||
|
||||
This shows two devices with their MAC addresses. You can use "`hciconfig -i`" to see which version of Bluetooth they
|
||||
implement: we require Bluetooth v4.0 or later. Choose which to use (we will use hci0), and reset it.
|
||||
|
||||
```
|
||||
$ sudo hciconfig hci0 reset
|
||||
|
||||
```
|
||||
|
||||
* **Step 1.** First reconfigure the Bluetooth device (hci0):
|
||||
|
||||
`hcitool` sends HCI commands as a sequence of 1-byte hexadecimal octets. It echoes the length (here `plen` = 15 bytes) and content
|
||||
of the sequence it sends to the Bluetooth HCI stack, and of the 4-byte "HCI Event" response it gets.
|
||||
Only the last byte of the response is important: `00` means the command succeded (other values are error codes).
|
||||
|
||||
```
|
||||
|
||||
$ sudo hcitool -i hci0 cmd 0x08 0x0006 0xa0 0x00 0xa0 0x00 0x03 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x07 0x00
|
||||
|
||||
< HCI Command: ogf 0x08, ocf 0x0006, plen 15
|
||||
A0 00 A0 00 03 01 00 00 00 00 00 00 00 07 00
|
||||
> HCI Event: 0x0e plen 4
|
||||
02 06 20 00
|
||||
|
||||
```
|
||||
|
||||
The above command configures the beacon: "`cmd 0x08 0x006`" means HCI LE (ogf=0x08) command number 6 (ocf=0x0006) of
|
||||
the Blutooth LE stack.
|
||||
|
||||
The first two message bytes "`0xa0 0x00`" means a 2-byte "unsigned short" value 0x00a0. (uint16_t integers such as 0xabcd are represented as two bytes "`0xcd, 0xab`"). This is the minimum interval AdvMin between
|
||||
beacon broadcasts, which are essentially simultaneous on all three advertising channels. The next two entries represent the maximum interval AdvMax, also set to 0x00a0,
|
||||
which means 100 msec (200 msec would be 2 * 0x00a0 = 0x0140 or "`0x40 0x01`"). Setting AdvMin = AdvMax fixes the interval between transmissions.
|
||||
If AdvMin < AdvMax, the timing of each broadcast event relative to the previous one can be chosen flexibly to not overlap with any other task the bluetooth socket is carrying out.
|
||||
The allowed range of these parameters is 0x00a0 = 100 msec <= AdvMin <= AdvMax <= 0x4000 = 10.24 sec.
|
||||
|
||||
An Apple TV (Gen 3) seems to use a fixed interval of 180 msec = 0x0120 ("`0x20 0x01`").
|
||||
|
||||
The sixth byte TxAdd = "`0x01`" says that a random MAC "advertisement address"" AdvAddr for the Bluetooth device will be sent with the advertisement. If you wish to send the true
|
||||
hardware MAC address of the Bluetooth device, replace this byte by "`0x00`".
|
||||
|
||||
|
||||
__These are the only parameters you might want to vary__. The fifth byte 0x03 is the Advertising PDU type "ADV_NONCONN_IND" (a beacon that transmits without accepting connections)
|
||||
and the fourteenth byte 0x07 is a flag 0000 0111 that says to use all three Bluetooth LE advertising channels.
|
||||
|
||||
|
||||
|
||||
* **Step 2.** (**Optional: skip this if you changed byte 6 of the initial configuration message from** "``0x01``" **to** "`0x00`".)
|
||||
Use HCI LE command 5 (ocf=0x0005) to
|
||||
set private "advertising address" AdvAddr, which substitutes for the public MAC address of the Bluetooth device.
|
||||
|
||||
This uses six random bytes r1,..,r6 and enters them as
|
||||
` r1 , r2 , r3, r4, r5, r0 = (r6 | 0x03)`, where the 6th byte has been masked with 0x03 = 00000011 so its last two bits are on,
|
||||
and the value r0 is restricted to 64 values "`0xrs`" where the second
|
||||
hexadecimal digit ``s`` is one of {3, 7, b, f}, which indicates a "static random" private address that is guaranteed to not
|
||||
change between device reboots. Note that Apple TV's use random private addresses without applying a mask to r6 to distinguish
|
||||
between different types.
|
||||
|
||||
|
||||
```
|
||||
|
||||
$sudo hcitool -i hci0 cmd 0x08 0x0005 0x52 0xaa 0xaa 0x3a 0xb4 0x2f
|
||||
< HCI Command: ogf 0x08, ocf 0x0005, plen 6
|
||||
52 AA AA 3A B4 2F
|
||||
> HCI Event: 0x0e plen 4
|
||||
02 05 20 00
|
||||
```
|
||||
|
||||
On a Bluetooth packet sniffer with wireshark, this address displays as: **Advertising Address: 2f:b4:3a:aa:aa:52**
|
||||
|
||||
* **Step 3.** Now provide the advertising message, with HCI LE command 8 (ocf=0x0008):
|
||||
|
||||
|
||||
This sends a 32 byte message to the HCI LE stack, where the first byte is the length (here 0x0c = 12 bytes) of the significant part of
|
||||
the following 31 bytes: 12 significant bytes, padded with 19 zeros to a total message length of 32 bytes. (`hcitool` requires a message padded to the full
|
||||
32 bytes, but only sends the significant bytes to the Bluetooth LE stack.)
|
||||
|
||||
```
|
||||
$ sudo hcitool -i hci0 cmd 0x08 0x0008 0x0c 0x0b 0xff 0x4c 0x00 0x09 0x06 0x03 0x30 0xc0 0xa8 0x01 0xfd 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
< HCI Command: ogf 0x08, ocf 0x0008, plen 32
|
||||
0C 0B FF 4C 00 09 06 03 30 C0 A8 01 FD 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00
|
||||
> HCI Event: 0x0e plen 4
|
||||
01 08 20 00
|
||||
```
|
||||
|
||||
The only parts of this message that you must change are the four bytes 10,11,12,13, of the IPv4 address, here "` 0xc0 0xa8 0x01 0xfd `", (decimal 192 168 1 253, an IPv4 address 192.168.1.253)
|
||||
which should be an IPv4 address at which the UxPlay server can receive requests from iOS/macOS clients at TCP port 7000. You need to find what IPv4 address will work
|
||||
on the computer that hosts UxPlay (use `ifconfig`), convert each of the four numbers from decimal to hexadecimal, and replace bytes 13-16 of the message by them.
|
||||
|
||||
|
||||
* **Step 4.** Start advertising by the beacon with Bluetooth LE command 10 (ocf = 0x000a) and 1-byte message "`0x01`" = "on".
|
||||
|
||||
```
|
||||
$ sudo hcitool -i hci0 cmd 0x08 0x000a 0x01
|
||||
< HCI Command: ogf 0x08, ocf 0x000a, plen 1
|
||||
01
|
||||
> HCI Event: 0x0e plen 4
|
||||
02 0A 20 00
|
||||
```
|
||||
|
||||
(To stop advertising, use this command to send the 1-byte message "`0x00`" = "off".)
|
||||
|
||||
|
||||
|
||||
For creating a higher-level script, it might be useful to know that the length 0C = 12 bytes advertisement sent in step 3 has a single "Advertising Protocol Data Unit" (PDU):
|
||||
|
||||
* 0B FF 4C 00 09 06 03 30 C0 A8 01 FD: length 0B = 11 bytes, consisting of: FF ( type = manufacturer-specific) 4C 00 (manufacturer code = 0x004c, Apple ) manufacturer data 09 06 03 30 C0 A8 01 FD
|
||||
|
||||
The manufacturer data defined by Apple consists of a single Apple data unit: 09 (Apple type = AirPlay), 06 (Apple data length 6 bytes) Apple data 03 30 XX XX XX XX, broken down into 03 (flags: 0000 0011) 30 (a seed)
|
||||
XX XX XX XX (IPv4 network address, written as four hexadecimal octets in standard order). (Apple TV's use a random private "AdvAddr" address as described above, and periodically update it at about 20 min intervals, each time
|
||||
increasing the seed by 1.)
|
||||
|
||||
Apple TV's also insert a type-1 ("Flags") 2-byte PDU "`02 01 1A`" before the manufacturer-specific PDU, increasing the significant length of the message to 0xf = 15 bytes.
|
||||
It turns out that the "Flags" PDU is "optional" for advertisements like beacons that do not allow client connections:
|
||||
in our tests on v4.0 and later dongles, Service Discovery still worked fine after dropping the "Flags" PDU.
|
||||
|
||||
Both Linux and Windows have high-level interfaces that support users sending Advertising PDU's, but restricted to
|
||||
type 0xff "manufacturer-specific-data" only, without any "Flags". These should be used for automating beacon setup, and
|
||||
are: (Linux) Bluez [LEAdvertisingManager1](https://github.com/bluez/bluez/blob/master/test/example-advertisement)
|
||||
and (Windows 10/11) [BluetoothLEAdvertisementPublisherClass](https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementpublisher)
|
||||
(with an [example](https://github.com/MicrosoftDocs/windows-dev-docs/blob/docs/uwp/devices-sensors/ble-beacon.md)).
|
||||
|
||||
|
||||
**We don't know if these instructions can be modified to advertise IPv6 addresses: if you know of any verified support for Bluetooth LE IPv6 Service Discovery in newer AppleTV models,
|
||||
please let us know. Simply replacing the 4-byte IPv4 address with a 16-byte IPv6 address (and adjusting the lengths at bytes 1, 5 and 10) does not seem to work, although perhaps
|
||||
we did not find the right value for byte 11 ("Apple Flags"). If Apple's Bluetooth LE Service Discovery has IPv6 support, we need to examine the beacon advertisement packet
|
||||
for IPv6 addresses with a Bluetooth sniffer.**
|
||||
|
||||
|
||||
# Troubleshooting
|
||||
Note: `uxplay` is run from a terminal command line, and informational
|
||||
messages are written to the terminal.
|
||||
|
||||
@@ -1776,7 +1972,8 @@ what version UxPlay claims to be.
|
||||
# Changelog
|
||||
xxxx 2025-08-11 Render Audio cover-art inside UxPlay with -ca option (no file
|
||||
specified). (D-Bus based) option -scrsv <n> to inhibit screensaver while UxPlay
|
||||
is running (Linux/*BSD only).
|
||||
is running (Linux/*BSD only). Add support for Service Discovery using a
|
||||
Bluetooth LE beacon.
|
||||
|
||||
1.72.2 2025-07-07 Fix bug (typo) in DNS_SD advertisement introduced with -pw
|
||||
option. Update llhttp to v 9.3.0
|
||||
|
||||
262
README.txt
262
README.txt
@@ -2,6 +2,20 @@
|
||||
|
||||
### **Now developed at the GitHub site <https://github.com/FDH2/UxPlay> (where ALL user issues should be posted, and latest versions can be found).**
|
||||
|
||||
- **NEW on github**: Support for **service discovery using a Bluetooth
|
||||
LE "beacon"** (as an alternative to Bonjour/Rendezvous DNS-SD
|
||||
service discovery). The user must set up a Bluetooth LE "beacon", (a
|
||||
USB 4.0 or later "dongle" can be used). See instructions below. The
|
||||
beacon runs independently of UxPlay and regularly broadcasts a
|
||||
Bluetooth LE ("Low Energy") 44 byte packet informing nearby
|
||||
iOS/macOS devices of the local IPv4 network address of the UxPlay
|
||||
server, which they can use to contact it on TCP port 7000.
|
||||
Instructions for manually setting up such a beacon in Linux are
|
||||
[given below](#bluetooth-le-beacon-setup). **It is hoped that users
|
||||
will submit Pull Requests contributing scripts for automating beacon
|
||||
setup on all platforms. (Python may be an appropriate language
|
||||
choice)**
|
||||
|
||||
- **NEW on github**: (for Linux/\*BSD Desktop Environments using
|
||||
D-Bus). New option `-scrsv <n>` provides screensaver inhibition
|
||||
(e.g., to prevent screensaver function while watching mirrored
|
||||
@@ -219,7 +233,9 @@ necessary that the local network also be of the ".local" mDNS-based
|
||||
type). On Linux and BSD Unix servers, this is usually provided by
|
||||
[Avahi](https://www.avahi.org), through the avahi-daemon service, and is
|
||||
included in most Linux distributions (this service can also be provided
|
||||
by macOS, iOS or Windows servers).
|
||||
by macOS, iOS or Windows servers). There is now an alternative Service
|
||||
discovery method, using a Bluetooth LE "beacon" See below for
|
||||
[instructions](#bluetooth-le-beacon-setup).
|
||||
|
||||
Connections to the UxPlay server by iOS/MacOS clients can be initiated
|
||||
both in **AirPlay Mirror** mode (which streams lossily-compressed AAC
|
||||
@@ -550,11 +566,13 @@ as comments and ignored.
|
||||
**Run uxplay in a terminal window**. On some systems, you can specify
|
||||
fullscreen mode with the `-fs` option, or toggle into and out of
|
||||
fullscreen mode with F11 or (held-down left Alt)+Enter keys. Use Ctrl-C
|
||||
(or close the window) to terminate it when done. If the UxPlay server is
|
||||
not seen by the iOS client's drop-down "Screen Mirroring" panel, check
|
||||
that your DNS-SD server (usually avahi-daemon) is running: do this in a
|
||||
terminal window with `systemctl status avahi-daemon`. If this shows the
|
||||
avahi-daemon is not running, control it with
|
||||
(or close the window) to terminate it when done.
|
||||
|
||||
If the UxPlay server is not seen by the iOS client's drop-down "Screen
|
||||
Mirroring" panel, check that your DNS-SD server (usually avahi-daemon)
|
||||
is running: do this in a terminal window with
|
||||
`systemctl status avahi-daemon`. If this shows the avahi-daemon is not
|
||||
running, control it with
|
||||
`sudo systemctl [start,stop,enable,disable] avahi-daemon` (on
|
||||
non-systemd systems, such as \*BSD, use
|
||||
`sudo service avahi-daemon [status, start, stop, restart, ...]`). If
|
||||
@@ -565,6 +583,10 @@ opened: **if a firewall is active, also open UDP port 5353 (for mDNS
|
||||
queries) needed by Avahi**. See [Troubleshooting](#troubleshooting)
|
||||
below for help with this or other problems.
|
||||
|
||||
Note that there is now an alternative Service Discovery method using a
|
||||
Bluetooth LE beacon. See the instructions on [Bluetooth beacon
|
||||
setup](#bluetooth-le-beacon-setup).
|
||||
|
||||
- Unlike an Apple TV, the UxPlay server does not by default require
|
||||
clients to initially "pair" with it using a pin code displayed by
|
||||
the server (after which the client "trusts" the server, and does not
|
||||
@@ -1443,6 +1465,12 @@ that (unlike dumped video) the dumped audio is currently only useful for
|
||||
debugging, as it is not containerized to make it playable with standard
|
||||
audio players.*
|
||||
|
||||
**-ble *filename***. Enable Bluetooth beacon Service Discovery. The PID
|
||||
and process name of the UxPlay process is recorded in *filename*, which
|
||||
must be the full path to a writeable file. (This file is created when
|
||||
UxPlay starts and deleted when it stops.) **See below for beacon setup
|
||||
instructions.**
|
||||
|
||||
**-d \[n\]** Enable debug output; optional argument n=1 suppresses
|
||||
audio/video packet data in debug output. Note: this does not show
|
||||
GStreamer error or debug messages. To see GStreamer error and warning
|
||||
@@ -1451,6 +1479,225 @@ GST_DEBUG=2" before running uxplay. To see GStreamer information
|
||||
messages, set GST_DEBUG=4; for DEBUG messages, GST_DEBUG=5; increase
|
||||
this to see even more of the GStreamer inner workings.
|
||||
|
||||
# Bluetooth LE beacon setup
|
||||
|
||||
When uxplay is started with the option
|
||||
`uxplay -ble <path-to-writeable-file>`, it writes a 20 byte data file
|
||||
containing (4 bytes) the process ID (PID) as a uint32_t 32-bit unsigned
|
||||
integer, and (16 bytes) up to 15 bytes of the process name (usually
|
||||
"uxplay") as a null-terminated string, padded with zeroes to fill 16
|
||||
bytes. The file is deleted if UxPlay is terminated normally (without a
|
||||
segfault), and could be used to determine if an instance of uxplay is
|
||||
running. **This file is provided for possible future use in a script for
|
||||
controlling the beacon, and will not be used here**.
|
||||
|
||||
You may need to use a cheap USB Bluetooth dongle if your system does not
|
||||
have Bluetooth 4.0 or later, or will not let you use it for LE (Low
|
||||
Energy) transmissions.
|
||||
|
||||
These instructions are tested on Linux using the Bluez Bluetooth stack.
|
||||
They use the `hcitool` and `hciconfig` utilities which directly access
|
||||
the HCI stack, and need elevated privileges (use `sudo`). These
|
||||
utilities have been declared "deprecated" and "obsolete" by BlueZ
|
||||
developers: on Debian-based Linux `sudo apt install bluez` still
|
||||
provides `hcitool`, but on some other Linux distributions, it is split
|
||||
off from the main BlueZ package into an "extra" package with a name like
|
||||
"bluez-deprecated". If we get the AirPlay beacon to work using the newer
|
||||
`bluetoothctl` utility, these instructions will be updated.
|
||||
|
||||
- **These manual instructions will hopefully be soon superseded by
|
||||
e.g. python scripts that automate beacon control, probably using
|
||||
D-Bus on Linux. Please submit any such scripts you get working for
|
||||
possible packaging together with UxPlay**. Note that the Apple
|
||||
Service Discovery beacon is not a standard "**ibeacon**", and cannot
|
||||
be set up with unmodified "ibeacon"-specific applications.
|
||||
|
||||
- For testing Bluetooth beacon Service Discovery on Linux, you will
|
||||
need to suppress the avahi-daemon which provides DNS-SD Service
|
||||
Discovery on UxPlay's Host system (replace `mask` and `stop` below
|
||||
by `unmask` and `start` to restore DNS-SD service).;
|
||||
|
||||
```{=html}
|
||||
<!-- -->
|
||||
```
|
||||
$ sudo systemctl mask avahi-daemon.socket
|
||||
$ sudo systemctl stop avahi-daemon
|
||||
|
||||
Then verify that uxplay will not start without the `-ble <filename>`
|
||||
option.
|
||||
|
||||
Before starting, check that you have a Bluetooth device with
|
||||
"`hcitool dev`"
|
||||
|
||||
$hcitool dev
|
||||
Devices:
|
||||
hci1 E8:EA:6A:7C:3F:CC
|
||||
hci0 08:BE:AC:40:A9:DC
|
||||
|
||||
This shows two devices with their MAC addresses. You can use
|
||||
"`hciconfig -i`" to see which version of Bluetooth they implement: we
|
||||
require Bluetooth v4.0 or later. Choose which to use (we will use hci0),
|
||||
and reset it.
|
||||
|
||||
$ sudo hciconfig hci0 reset
|
||||
|
||||
- **Step 1.** First reconfigure the Bluetooth device (hci0):
|
||||
|
||||
`hcitool` sends HCI commands as a sequence of 1-byte hexadecimal octets.
|
||||
It echoes the length (here `plen` = 15 bytes) and content of the
|
||||
sequence it sends to the Bluetooth HCI stack, and of the 4-byte "HCI
|
||||
Event" response it gets. Only the last byte of the response is
|
||||
important: `00` means the command succeded (other values are error
|
||||
codes).
|
||||
|
||||
|
||||
$ sudo hcitool -i hci0 cmd 0x08 0x0006 0xa0 0x00 0xa0 0x00 0x03 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x07 0x00
|
||||
|
||||
< HCI Command: ogf 0x08, ocf 0x0006, plen 15
|
||||
A0 00 A0 00 03 01 00 00 00 00 00 00 00 07 00
|
||||
> HCI Event: 0x0e plen 4
|
||||
02 06 20 00
|
||||
|
||||
The above command configures the beacon: "`cmd 0x08 0x006`" means HCI LE
|
||||
(ogf=0x08) command number 6 (ocf=0x0006) of the Blutooth LE stack.
|
||||
|
||||
The first two message bytes "`0xa0 0x00`" means a 2-byte "unsigned
|
||||
short" value 0x00a0. (uint16_t integers such as 0xabcd are represented
|
||||
as two bytes "`0xcd, 0xab`"). This is the minimum interval AdvMin
|
||||
between beacon broadcasts, which are essentially simultaneous on all
|
||||
three advertising channels. The next two entries represent the maximum
|
||||
interval AdvMax, also set to 0x00a0, which means 100 msec (200 msec
|
||||
would be 2 \* 0x00a0 = 0x0140 or "`0x40 0x01`"). Setting AdvMin = AdvMax
|
||||
fixes the interval between transmissions. If AdvMin \< AdvMax, the
|
||||
timing of each broadcast event relative to the previous one can be
|
||||
chosen flexibly to not overlap with any other task the bluetooth socket
|
||||
is carrying out. The allowed range of these parameters is 0x00a0 = 100
|
||||
msec \<= AdvMin \<= AdvMax \<= 0x4000 = 10.24 sec.
|
||||
|
||||
An Apple TV (Gen 3) seems to use a fixed interval of 180 msec = 0x0120
|
||||
("`0x20 0x01`").
|
||||
|
||||
The sixth byte TxAdd = "`0x01`" says that a random MAC "advertisement
|
||||
address"" AdvAddr for the Bluetooth device will be sent with the
|
||||
advertisement. If you wish to send the true hardware MAC address of the
|
||||
Bluetooth device, replace this byte by "`0x00`".
|
||||
|
||||
**These are the only parameters you might want to vary**. The fifth byte
|
||||
0x03 is the Advertising PDU type "ADV_NONCONN_IND" (a beacon that
|
||||
transmits without accepting connections) and the fourteenth byte 0x07 is
|
||||
a flag 0000 0111 that says to use all three Bluetooth LE advertising
|
||||
channels.
|
||||
|
||||
- **Step 2.** (**Optional: skip this if you changed byte 6 of the
|
||||
initial configuration message from** "`0x01`" **to** "`0x00`".) Use
|
||||
HCI LE command 5 (ocf=0x0005) to set private "advertising address"
|
||||
AdvAddr, which substitutes for the public MAC address of the
|
||||
Bluetooth device.
|
||||
|
||||
This uses six random bytes r1,..,r6 and enters them as
|
||||
`r1 , r2 , r3, r4, r5, r0 = (r6 | 0x03)`, where the 6th byte has been
|
||||
masked with 0x03 = 00000011 so its last two bits are on, and the value
|
||||
r0 is restricted to 64 values "`0xrs`" where the second hexadecimal
|
||||
digit `s` is one of {3, 7, b, f}, which indicates a "static random"
|
||||
private address that is guaranteed to not change between device reboots.
|
||||
Note that Apple TV's use random private addresses without applying a
|
||||
mask to r6 to distinguish between different types.
|
||||
|
||||
|
||||
$sudo hcitool -i hci0 cmd 0x08 0x0005 0x52 0xaa 0xaa 0x3a 0xb4 0x2f
|
||||
< HCI Command: ogf 0x08, ocf 0x0005, plen 6
|
||||
52 AA AA 3A B4 2F
|
||||
> HCI Event: 0x0e plen 4
|
||||
02 05 20 00
|
||||
|
||||
On a Bluetooth packet sniffer with wireshark, this address displays as:
|
||||
**Advertising Address: 2f:b4:3a:aa:aa:52**
|
||||
|
||||
- **Step 3.** Now provide the advertising message, with HCI LE command
|
||||
8 (ocf=0x0008):
|
||||
|
||||
This sends a 32 byte message to the HCI LE stack, where the first byte
|
||||
is the length (here 0x0c = 12 bytes) of the significant part of the
|
||||
following 31 bytes: 12 significant bytes, padded with 19 zeros to a
|
||||
total message length of 32 bytes. (`hcitool` requires a message padded
|
||||
to the full 32 bytes, but only sends the significant bytes to the
|
||||
Bluetooth LE stack.)
|
||||
|
||||
$ sudo hcitool -i hci0 cmd 0x08 0x0008 0x0c 0x0b 0xff 0x4c 0x00 0x09 0x06 0x03 0x30 0xc0 0xa8 0x01 0xfd 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
|
||||
< HCI Command: ogf 0x08, ocf 0x0008, plen 32
|
||||
0C 0B FF 4C 00 09 06 03 30 C0 A8 01 FD 00 00 00 00 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00
|
||||
> HCI Event: 0x0e plen 4
|
||||
01 08 20 00
|
||||
|
||||
The only parts of this message that you must change are the four bytes
|
||||
10,11,12,13, of the IPv4 address, here "`0xc0 0xa8 0x01 0xfd`", (decimal
|
||||
192 168 1 253, an IPv4 address 192.168.1.253) which should be an IPv4
|
||||
address at which the UxPlay server can receive requests from iOS/macOS
|
||||
clients at TCP port 7000. You need to find what IPv4 address will work
|
||||
on the computer that hosts UxPlay (use `ifconfig`), convert each of the
|
||||
four numbers from decimal to hexadecimal, and replace bytes 13-16 of the
|
||||
message by them.
|
||||
|
||||
- **Step 4.** Start advertising by the beacon with Bluetooth LE
|
||||
command 10 (ocf = 0x000a) and 1-byte message "`0x01`" = "on".
|
||||
|
||||
```{=html}
|
||||
<!-- -->
|
||||
```
|
||||
$ sudo hcitool -i hci0 cmd 0x08 0x000a 0x01
|
||||
< HCI Command: ogf 0x08, ocf 0x000a, plen 1
|
||||
01
|
||||
> HCI Event: 0x0e plen 4
|
||||
02 0A 20 00
|
||||
|
||||
(To stop advertising, use this command to send the 1-byte message
|
||||
"`0x00`" = "off".)
|
||||
|
||||
For creating a higher-level script, it might be useful to know that the
|
||||
length 0C = 12 bytes advertisement sent in step 3 has a single
|
||||
"Advertising Protocol Data Unit" (PDU):
|
||||
|
||||
- 0B FF 4C 00 09 06 03 30 C0 A8 01 FD: length 0B = 11 bytes,
|
||||
consisting of: FF ( type = manufacturer-specific) 4C 00
|
||||
(manufacturer code = 0x004c, Apple ) manufacturer data 09 06 03 30
|
||||
C0 A8 01 FD
|
||||
|
||||
The manufacturer data defined by Apple consists of a single Apple data
|
||||
unit: 09 (Apple type = AirPlay), 06 (Apple data length 6 bytes) Apple
|
||||
data 03 30 XX XX XX XX, broken down into 03 (flags: 0000 0011) 30 (a
|
||||
seed) XX XX XX XX (IPv4 network address, written as four hexadecimal
|
||||
octets in standard order). (Apple TV's use a random private "AdvAddr"
|
||||
address as described above, and periodically update it at about 20 min
|
||||
intervals, each time increasing the seed by 1.)
|
||||
|
||||
Apple TV's also insert a type-1 ("Flags") 2-byte PDU "`02 01 1A`" before
|
||||
the manufacturer-specific PDU, increasing the significant length of the
|
||||
message to 0xf = 15 bytes. It turns out that the "Flags" PDU is
|
||||
"optional" for advertisements like beacons that do not allow client
|
||||
connections: in our tests on v4.0 and later dongles, Service Discovery
|
||||
still worked fine after dropping the "Flags" PDU.
|
||||
|
||||
Both Linux and Windows have high-level interfaces that support users
|
||||
sending Advertising PDU's, but restricted to type 0xff
|
||||
"manufacturer-specific-data" only, without any "Flags". These should be
|
||||
used for automating beacon setup, and are: (Linux) Bluez
|
||||
[LEAdvertisingManager1](https://github.com/bluez/bluez/blob/master/test/example-advertisement)
|
||||
and (Windows 10/11)
|
||||
[BluetoothLEAdvertisementPublisherClass](https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementpublisher)
|
||||
(with an
|
||||
[example](https://github.com/MicrosoftDocs/windows-dev-docs/blob/docs/uwp/devices-sensors/ble-beacon.md)).
|
||||
|
||||
**We don't know if these instructions can be modified to advertise IPv6
|
||||
addresses: if you know of any verified support for Bluetooth LE IPv6
|
||||
Service Discovery in newer AppleTV models, please let us know. Simply
|
||||
replacing the 4-byte IPv4 address with a 16-byte IPv6 address (and
|
||||
adjusting the lengths at bytes 1, 5 and 10) does not seem to work,
|
||||
although perhaps we did not find the right value for byte 11 ("Apple
|
||||
Flags"). If Apple's Bluetooth LE Service Discovery has IPv6 support, we
|
||||
need to examine the beacon advertisement packet for IPv6 addresses with
|
||||
a Bluetooth sniffer.**
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
Note: `uxplay` is run from a terminal command line, and informational
|
||||
@@ -1795,7 +2042,8 @@ what version UxPlay claims to be.
|
||||
|
||||
xxxx 2025-08-11 Render Audio cover-art inside UxPlay with -ca option (no
|
||||
file specified). (D-Bus based) option -scrsv `<n>`{=html} to inhibit
|
||||
screensaver while UxPlay is running (Linux/\*BSD only).
|
||||
screensaver while UxPlay is running (Linux/\*BSD only). Add support for
|
||||
Service Discovery using a Bluetooth LE beacon.
|
||||
|
||||
1.72.2 2025-07-07 Fix bug (typo) in DNS_SD advertisement introduced with
|
||||
-pw option. Update llhttp to v 9.3.0
|
||||
|
||||
23
lib/raop.c
23
lib/raop.c
@@ -188,26 +188,35 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
|
||||
All requests arriving here have been parsed by llhttp to obtain
|
||||
method | url | protocol (RTSP/1.0 or HTTP/1.1)
|
||||
|
||||
There are three types of connections supplying these requests:
|
||||
There are four types of connections supplying these requests:
|
||||
Connections from the AirPlay client:
|
||||
(1) type RAOP connections with CSeq seqence header, and no X-Apple-Session-ID header
|
||||
(2) type AIRPLAY connection with an X-Apple-Sequence-ID header and no Cseq header
|
||||
Connections from localhost:
|
||||
(3) type HLS internal connections from the local HLS server (gstreamer) at localhost with neither
|
||||
of these headers, but a Host: localhost:[port] header. method = GET.
|
||||
(4) a special RAOP connection trigggered by a Bluetooth LE beacon: Protocol RTSP/1.0, method: GET
|
||||
url /info?txtAirPlay?txtRAOP, and no headers including CSeq
|
||||
*/
|
||||
|
||||
const char *method = http_request_get_method(request);
|
||||
const char *url = http_request_get_url(request);
|
||||
const char *protocol = http_request_get_protocol(request);
|
||||
|
||||
if (!method || !url) {
|
||||
if (!method || !url || !protocol) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* this rejects messages from _airplay._tcp for video streaming protocol unless bool raop->hls_support is true*/
|
||||
/* ¨idenitfy if request is a response to a BLE beaconn */
|
||||
const char *cseq = http_request_get_header(request, "CSeq");
|
||||
const char *protocol = http_request_get_protocol(request);
|
||||
if (!cseq && !conn->raop->hls_support) {
|
||||
bool ble = false;
|
||||
if (!strcmp(protocol,"RTSP/1.0") && !cseq && (strstr(url, "txtAirPlay") || strstr(url, "txtRAOP") )) {
|
||||
logger_log(conn->raop->logger, LOGGER_INFO, "response to Bluetooth LE beacon advertisement received)");
|
||||
ble = true;
|
||||
}
|
||||
|
||||
/* this rejects messages from _airplay._tcp for video streaming protocol unless bool raop->hls_support is true*/
|
||||
if (!cseq && !conn->raop->hls_support && !ble) {
|
||||
logger_log(conn->raop->logger, LOGGER_INFO, "ignoring AirPlay video streaming request (use option -hls to activate HLS support)");
|
||||
return;
|
||||
}
|
||||
@@ -217,7 +226,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
|
||||
hls_request = (host && !cseq && !client_session_id);
|
||||
|
||||
if (conn->connection_type == CONNECTION_TYPE_UNKNOWN) {
|
||||
if (cseq) {
|
||||
if (cseq || ble) {
|
||||
if (httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_RAOP)) {
|
||||
char ipaddr[40];
|
||||
utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, ipaddr, (int) (sizeof(ipaddr)));
|
||||
@@ -367,7 +376,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
|
||||
handler = &raop_handler_audiomode;
|
||||
}
|
||||
} else if (!strcmp(method, "GET")) {
|
||||
if (!strcmp(url, "/info")) {
|
||||
if (strstr(url, "/info")) {
|
||||
handler = &raop_handler_info;
|
||||
}
|
||||
} else if (!strcmp(method, "OPTIONS")) {
|
||||
|
||||
2
uxplay.1
2
uxplay.1
@@ -184,6 +184,8 @@ UxPlay 1.72: An open\-source AirPlay mirroring (+ audio streaming) server:
|
||||
audio packets are dumped. "aud"= unknown format.
|
||||
.PP
|
||||
.TP
|
||||
\fB\-ble\fI fn\fR For BluetoothLE beacon: write PID to file fn ("off" to cancel)
|
||||
.TP
|
||||
\fB\-d [n]\fR Enable debug logging; optional: n=1 to skip normal packet data.
|
||||
.TP
|
||||
\fB\-v\fR Displays version information
|
||||
|
||||
94
uxplay.cpp
94
uxplay.cpp
@@ -189,6 +189,7 @@ static uint32_t rtptime_end = 0;
|
||||
static uint32_t rtptime_coverart_expired = 0;
|
||||
static std::string artist;
|
||||
static std::string coverart_artist;
|
||||
static std::string ble_filename = "";
|
||||
|
||||
//Support for D-Bus-based screensaver inhibition (org.freedesktop.ScreenSaver)
|
||||
static unsigned int scrsv;
|
||||
@@ -353,6 +354,18 @@ static size_t write_metadata(const char *filename, const char *text) {
|
||||
return count;
|
||||
}
|
||||
|
||||
static int write_bledata( const uint32_t *pid, const char *process_name, const char *filename) {
|
||||
char name[16] { 0 };
|
||||
size_t len = strlen(process_name);
|
||||
memcpy (name, process_name, (len > 15 ? 15 :len));
|
||||
FILE *fp = fopen(filename, "wb");
|
||||
size_t count = fwrite(pid, sizeof (uint32_t), 1, fp);
|
||||
count *= sizeof(uint32_t);
|
||||
count += fwrite(name, 1, sizeof(name), fp);
|
||||
fclose(fp);
|
||||
return (int) count;
|
||||
}
|
||||
|
||||
static char *create_pin_display(char *pin_str, int margin, int gap) {
|
||||
char *ptr;
|
||||
char num[2] = { 0 };
|
||||
@@ -905,6 +918,7 @@ static void print_info (char *name) {
|
||||
printf(" =1,2,..; fn=\"audiodump\"; change with \"-admp [n] filename\".\n");
|
||||
printf(" x increases when audio format changes. If n is given, <= n\n");
|
||||
printf(" audio packets are dumped. \"aud\"= unknown format.\n");
|
||||
printf("-ble fn For BluetoothLE beacon: write PID to file fn (\"off\" to cancel)\n");
|
||||
printf("-d [n] Enable debug logging; optional: n=1 to skip normal packet data\n");
|
||||
printf("-v Displays version information\n");
|
||||
printf("-h Displays this help\n");
|
||||
@@ -1357,6 +1371,21 @@ static void parse_arguments (int argc, char *argv[]) {
|
||||
fprintf(stderr,"option -md must be followed by a filename for metadata text output\n");
|
||||
exit(1);
|
||||
}
|
||||
} else if (arg == "-ble" ) {
|
||||
if (option_has_value(i, argc, arg, argv[i+1])) {
|
||||
ble_filename.erase();
|
||||
i++;
|
||||
if (strlen(argv[i]) != 3 || strncmp(argv[i], "off", 3)) {
|
||||
ble_filename.append(argv[i]);
|
||||
if (!file_has_write_access(argv[i])) {
|
||||
fprintf(stderr, "%s cannot be written to:\noption \"-ble<fn>\" must be to a file with write access\n", argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,"option -ble must be followed by a filename for PID data or by \"off\"\n");
|
||||
exit(1);
|
||||
}
|
||||
} else if (arg == "-bt709") {
|
||||
bt709_fix = true;
|
||||
} else if (arg == "-srgb") {
|
||||
@@ -1678,26 +1707,37 @@ static int register_dnssd() {
|
||||
int dnssd_error;
|
||||
uint64_t features;
|
||||
|
||||
if ((dnssd_error = dnssd_register_raop(dnssd, raop_port))) {
|
||||
if (dnssd_error == -65537) {
|
||||
LOGE("No DNS-SD Server found (DNSServiceRegister call returned kDNSServiceErr_Unknown)");
|
||||
} else if (dnssd_error == -65548) {
|
||||
LOGE("DNSServiceRegister call returned kDNSServiceErr_NameConflict");
|
||||
LOGI("Is another instance of %s running with the same DeviceID (MAC address) or using same network ports?",
|
||||
DEFAULT_NAME);
|
||||
LOGI("Use options -m ... and -p ... to allow multiple instances of %s to run concurrently", DEFAULT_NAME);
|
||||
dnssd_error = dnssd_register_raop(dnssd, raop_port);
|
||||
if (dnssd_error) {
|
||||
if (ble_filename.empty()) {
|
||||
if (dnssd_error == -65537) {
|
||||
LOGE("No DNS-SD Server found (DNSServiceRegister call returned kDNSServiceErr_Unknown)");
|
||||
} else if (dnssd_error == -65548) {
|
||||
LOGE("DNSServiceRegister call returned kDNSServiceErr_NameConflict");
|
||||
LOGI("Is another instance of %s running with the same DeviceID (MAC address) or using same network ports?",
|
||||
DEFAULT_NAME);
|
||||
LOGI("Use options -m ... and -p ... to allow multiple instances of %s to run concurrently", DEFAULT_NAME);
|
||||
} else {
|
||||
LOGE("dnssd_register_raop failed with error code %d\n"
|
||||
"mDNS Error codes are in range FFFE FF00 (-65792) to FFFE FFFF (-65537) "
|
||||
"(see Apple's dns_sd.h)", dnssd_error);
|
||||
}
|
||||
return -3;
|
||||
} else {
|
||||
LOGE("dnssd_register_raop failed with error code %d\n"
|
||||
"mDNS Error codes are in range FFFE FF00 (-65792) to FFFE FFFF (-65537) "
|
||||
"(see Apple's dns_sd.h)", dnssd_error);
|
||||
LOGI("dnssd_register_raop failed: ignoring because Bluetooth LE service discovery may be available");
|
||||
}
|
||||
return -3;
|
||||
}
|
||||
if ((dnssd_error = dnssd_register_airplay(dnssd, airplay_port))) {
|
||||
LOGE("dnssd_register_airplay failed with error code %d\n"
|
||||
"mDNS Error codes are in range FFFE FF00 (-65792) to FFFE FFFF (-65537) "
|
||||
"(see Apple's dns_sd.h)", dnssd_error);
|
||||
return -4;
|
||||
|
||||
dnssd_error = dnssd_register_airplay(dnssd, airplay_port);
|
||||
if (dnssd_error) {
|
||||
if (ble_filename.empty()) {
|
||||
LOGE("dnssd_register_airplay failed with error code %d\n"
|
||||
"mDNS Error codes are in range FFFE FF00 (-65792) to FFFE FFFF (-65537) "
|
||||
"(see Apple's dns_sd.h)", dnssd_error);
|
||||
return -4;
|
||||
} else {
|
||||
LOGI("dnssd_register_airplay failed: ignoring because Bluetooth LE service discovery may be available");
|
||||
}
|
||||
}
|
||||
|
||||
LOGD("register_dnssd: advertised AirPlay service with \"Features\" code = 0x%llX",
|
||||
@@ -2818,6 +2858,19 @@ int main (int argc, char *argv[]) {
|
||||
write_metadata(metadata_filename.c_str(), "no data\n");
|
||||
}
|
||||
|
||||
#define PID_MAX 4194304 // 2^22
|
||||
if (ble_filename.length()) {
|
||||
#ifdef _WIN_32
|
||||
DWORD pid = GetCurrentProcessId();
|
||||
g_assert(pid <= PID_MAX);
|
||||
#else
|
||||
pid_t pid = getpid();
|
||||
g_assert (pid <= PID_MAX && pid >= 0);
|
||||
#endif
|
||||
write_bledata((uint32_t *) &pid, argv[0], ble_filename.c_str());
|
||||
LOGI("Bluetooth LE beacon-based service discovery is possible: PID data written to %s", ble_filename.c_str());
|
||||
}
|
||||
|
||||
/* set default resolutions for h264 or h265*/
|
||||
if (!display[0] && !display[1]) {
|
||||
if (h265_support) {
|
||||
@@ -2895,10 +2948,13 @@ int main (int argc, char *argv[]) {
|
||||
fclose(video_dumpfile);
|
||||
}
|
||||
if (coverart_filename.length()) {
|
||||
remove (coverart_filename.c_str());
|
||||
remove (coverart_filename.c_str());
|
||||
}
|
||||
if (metadata_filename.length()) {
|
||||
remove (metadata_filename.c_str());
|
||||
remove (metadata_filename.c_str());
|
||||
}
|
||||
if (ble_filename.length()) {
|
||||
remove (ble_filename.c_str());
|
||||
}
|
||||
#ifdef DBUS
|
||||
if (dbus_connection) {
|
||||
|
||||
Reference in New Issue
Block a user