mirror of
https://github.com/morgan9e/UxPlay
synced 2026-04-14 00:04:13 +09:00
Merge pull request #72 from FDH2/master
ongoing pull requests from development branch UxPlay 1.43
This commit is contained in:
50
README.html
50
README.html
@@ -1,29 +1,30 @@
|
||||
<h1 id="uxplay-1.43-airplayairplay-mirror-server-for-linux-macos-and-unix.">UxPlay 1.43: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.</h1>
|
||||
<p>This project is a GPLv3 unix AirPlay2 server which now also works on macOS. Its main use is to act like an AppleTV for screen-mirroring (with audio) of iOS/macOS clients (iPads, iPhones, MacBooks) in a window on the server display (with the possibility of sharing that window on screen-sharing applications such as Zoom) on a host running Linux, macOS, or other unix, using Apple’s AirPlay Mirror protocol first available in iOS 5. (Details of what is known about the AirPlay2 protocol can be found <a href="https://github.com/SteeBono/airplayreceiver/wiki/AirPlay2-Protocol">here</a> and <a href="https://emanuelecozzi.net/docs/airplay2">here</a>). <strong>Note that Apple DRM (as in Apple TV app content) cannot be decrypted by UxPlay.</strong></p>
|
||||
<p>The UxPlay server and its client must be on the same local area network, on which a <strong>Bonjour/Zeroconf mDNS/DNS-SD server</strong> is also running (only DNS-SD “Service Discovery” service is 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>
|
||||
<p><em>Since v1.38, UxPlay now also supports the Airplay audio-only protocol as well as AirPlay Mirror protocol, and (when the client screen is not being mirrored) can play Apple Lossless (ALAC) audio streamed from the client without video (the accompanying cover-art and metadata is not displayed). The initial connection to the client can be either AirPlay audio or Airplay Mirror mode. An Airplay Mirror connection (with “Advanced Audio Coding” AAC-ELD lossy-compression audio) switches to ALAC if the mirrow window is closed and an AirPlay audio connection is started, and back again to AAC if a new Airplay Mirror connection is made</em>.</p>
|
||||
<p>UxPlay is based on https://github.com/FD-/RPiPlay, with GStreamer integration from https://github.com/antimof/UxPlay. (UxPlay only uses GStreamer, and does not contain the alternative Raspberry-Pi-specific audio and video renderers also found in RPiPlay.) Tested on a number of systems, including Ubuntu 20.04, Linux Mint 20.2, OpenSUSE 15.3, macOS 10.15, FreeBSD 13.0.</p>
|
||||
<h1 id="uxplay-1.44-airplayairplay-mirror-server-for-linux-macos-and-unix.">UxPlay 1.44: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.</h1>
|
||||
<p>This project is a GPLv3 open source unix AirPlay2 Mirror server for Linux, macOS, and *BSD. It is now hosted at the github site <a href="https://github.com/FDH2/UxPlay">https://github.com/FDH2/UxPlay</a> (where development and user-assistance now takes place), although it initially was developed by <a href="http://github.com/antimof/Uxplay">antimof</a> using code from <a href="https://github.com/FD-/RPiPlay">RPiPlay</a>, which in turn derives from <a href="https://github.com/KqsMea8/AirplayServer">AirplayServer</a>, <a href="https://github.com/juhovh/shairplay">shairplay</a>, and <a href="https://github.com/EstebanKubata/playfair">playfair</a>. (The antimof site is mainly inactive, but periodically posts updates pulled from the <a href="https://github.com/FDH2/UxPlay">main UxPlay site</a>).</p>
|
||||
<p>Its main use is to act like an AppleTV for screen-mirroring (with audio) of iOS/iPadOS/macOS clients (iPhones, iPads, MacBooks) in a window on the server display (with the possibility of sharing that window on screen-sharing applications such as Zoom) on a host running Linux, macOS, or other unix. UxPlay supports a “legacy” form of Apple’s AirPlay Mirror protocol introduced in iOS 12; client devices running iOS/iPadOS 12 or later are supported, as is a (nonfree) Windows-based AirPlay-client software emulator, AirMyPC. Older (32-bit) client devices that can only run iOS 9.3 or iOS 10.3 are currently partially supported by UxPlay: reports indicate that screen-mirroring video works, audio is a work in progess. (Details of what is publically known about Apple’s AirPlay2 protocol can be found <a href="https://github.com/SteeBono/airplayreceiver/wiki/AirPlay2-Protocol">here</a> and <a href="https://emanuelecozzi.net/docs/airplay2">here</a>).</p>
|
||||
<p>The UxPlay server and its client must be on the same local area network, on which a <strong>Bonjour/Zeroconf mDNS/DNS-SD server</strong> is also running (only DNS-SD “Service Discovery” service is strictly 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>
|
||||
<p>Connections to the UxPlay server by iOS/MacOS clients can be initiated both in AirPlay Mirror mode (which streams lossily-compressed AAC audio while mirroring the client screen, or in the alternative AirPlay Audio mode which streams Apple Lossless (ALAC) audio without screen mirroring (the accompanying metadata and cover art in this mode is not displayed). <em>Switching between these two modes during an active connection is possible: in Mirror mode, close the mirror window and start an Audio mode connection, switch back by initiating a Mirror mode connection.</em> <strong>Note that Apple DRM (as in Apple TV app content on the client) cannot be decrypted by UxPlay, and (unlike with a true AppleTV), the client cannot run a http connection on the server instead of streaming content from one on the client.</strong></p>
|
||||
<p>UxPlay uses GStreamer Plugins for rendering audio and video, and does not offer the alternative Raspberry-Pi-specific audio and video renderers available in <a href="https://github.com/FD-/RPiPlay">RPiPlay</a>. It is tested on a number of systems, including (among others) Debian 11.2, Ubuntu 20.04 and 21.10, Linux Mint 20.2, OpenSUSE 15.3, macOS 10.15.7, FreeBSD 13.0.</p>
|
||||
<p>Using Gstreamer means that video and audio are supported “out of the box”, using a choice of plugins. Gstreamer decoding is plugin agnostic, and uses accelerated decoders if available. For Intel integrated graphics, the VAAPI plugin is preferable, (but don’t use it with nVidia).</p>
|
||||
<h3 id="note-to-packagers-openssl-3.0.0-solves-gpl-v3-license-issues.">Note to packagers: OpenSSL-3.0.0 solves GPL v3 license issues.</h3>
|
||||
<p>Some Linux distributions such as Debian do not allow distribution of compiled GPL code linked to OpenSLL-1.1.1 because its “dual OpenSSL/SSLeay” license has some incompatibilites with GPL, unless all code authors have explicitly given an “exception” to allow such linking (the historical origins of UxPlay make this impossible to obtain). Other distributions treat OpenSSL as a “System Library” which the GPL allows linking to.</p>
|
||||
<p>Some Linux distributions such as Debian do not allow distribution of compiled GPL code linked to OpenSSL-1.1.1 because its “dual OpenSSL/SSLeay” license has some incompatibilites with GPL, unless all code authors have explicitly given an “exception” to allow such linking (the historical origins of UxPlay make this impossible to obtain). Other distributions treat OpenSSL as a “System Library” which the GPL allows linking to.</p>
|
||||
<p>For “GPL-strict” distributions, UxPlay can be built using OpenSSL- 3.0.0, which has a new <a href="https://www.openssl.org/blog/blog/2021/09/07/OpenSSL3.Final/">GPLv3-compatible license</a>.</p>
|
||||
<h1 id="getting-uxplay">Getting UxPlay:</h1>
|
||||
<p>Either download and unzip <a href="https://github.com/FDH2/UxPlay/archive/refs/heads/master.zip">UxPlay-master.zip</a>, or (if git is installed): “git clone https://github.com/FDH2/UxPlay”.</p>
|
||||
<p>*This is also a pull request on the original site https://github.com/antimof/UxPlay ; but is unlikely to ever get committed into the codebase there, as that project appears to be inactive.</p>
|
||||
<p>*This is also a pull request on the original site https://github.com/antimof/UxPlay ; that original project is inactive, but the pull request with changes up to 2021-12-10 were recently merged with the antimof tree (thank you antimof!).</p>
|
||||
<h2 id="building-uxplay-on-linux-or-bsd">Building UxPlay on Linux (or *BSD):</h2>
|
||||
<p>(Instructions for Debian/Ubuntu; adapt these for other Linuxes; for macOS, see below).</p>
|
||||
<p>You need a C/C++ compiler (e.g. g++) with the standard development libraries installed.</p>
|
||||
<p>Make sure that cmake>=3.4.1 and pkg-config are also installed: “sudo apt-get install cmake pkg-config”. In a terminal window, change directories to the source directory of the downloaded source code (“UxPlay-master” for zipfile downloads, “UxPlay” for “git clone” downloads), then do</p>
|
||||
<ol type="1">
|
||||
<li><code>sudo apt-get install libssl-dev libplist-dev libavahi-compat-libdnssd-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-libav gstreamer1.0-plugins-bad</code></li>
|
||||
<li><code>sudo apt-get install gstreamer1.0-vaapi</code> (For Intel graphics, but not nVidia graphics)</li>
|
||||
<li><code>sudo apt-get install gstreamer1.0-vaapi</code> (For hardware-accelerated Intel graphics, but not nVidia graphics)</li>
|
||||
<li><code>sudo apt-get install libx11-dev</code> (for the “ZOOMFIX” X11_display name fix for screen-sharing with e.g., ZOOM)</li>
|
||||
<li><code>cmake .</code> (or “<code>cmake -DZOOMFIX=ON .</code>” to get a screen-sharing fix to make X11 mirror display windows visible to screen-sharing applications such as Zoom, see below).</li>
|
||||
<li><code>make</code></li>
|
||||
<li><code>sudo make install</code> (you can afterwards uninstall with <code>sudo make uninstall</code> in the same directory in which this was run)</li>
|
||||
</ol>
|
||||
<p><em>If you intend to modify the code, use a separate “build” directory: replace</em> “<code>cmake [ ] .</code>” <em>by</em> “<code>mkdir build ; cd build ; cmake [ ] ..</code>”; <em>you can then clean the build directory with</em> “<code>rm -rf build/*</code>” <em>(run from within the UxPlay source directory) without affecting the source directories which contain your modifications</em>.</p>
|
||||
<p>The above script installs the executable file “<code>uxplay</code>” to <code>/usr/local/bin</code>, (and installs a manpage to <code>/usr/local/man/man1</code> and README files to <code>/usr/local/share/doc/uxplay</code>). It can also be found in the build directory after the build processs. Run uxplay in a terminal window.</p>
|
||||
<p>The above script installs the executable file “<code>uxplay</code>” to <code>/usr/local/bin</code>, (and installs a manpage to somewhere like <code>/usr/local/share/man/man1</code> and README files to somewhere like <code>/usr/local/share/doc/uxplay</code>). It can also be found in the build directory after the build processs. Run uxplay in a terminal window.</p>
|
||||
<p><strong>Note libplist-dev (version 2.0 or greater) is a new dependency (the original antimof version UxPlay-1.2 supplied it). Older Linux distributions may only supply libplist 1.x, which is too old. [Installing libplist-dev (with libplist3) from ubuntu 18.04 solves this problem on ubuntu 16.04.]</strong> If you cannot find a libplist-2.x package that installs on your older distribution, you can get it at <a href="https://github.com/libimobiledevice/libplist">https://github.com/libimobiledevice/libplist</a> and build it from source. _(You will need build tools autoconf, automake, libtool, and may need to also install some libpython*-dev package). By default, libplist installs in /usr/local/lib. If this is not in the library path (as in ubuntu), create a file /etc/ld.so.conf.d/libplist.conf containing the text “/usr/local/lib”, and run “sudo ldconfig” to permanently add /usr/local/lib to the library path._</p>
|
||||
<p><strong>Red Hat, Fedora, CentOS:</strong> (sudo yum install) openssl-devel libplist-devel avahi-compat-libdns_sd-devel (+libX11-devel for ZOOMFIX). The required GStreamer packages are: gstreamer1-devel gstreamer1-plugins-base-devel gstreamer1-libav gstreamer1-plugins-bad-free ( + gstreamer1-vaapi for intel graphics).</p>
|
||||
<p><strong>OpenSUSE:</strong> (sudo zypper install) libopenssl-devel libplist-devel avahi-compat-mDNSResponder-devel (+ libX11-devel for ZOOMFIX). The required GStreamer packages are: gstreamer-devel gstreamer-plugins-base-devel gstreamer-plugins-libav gstreamer-plugins-bad (+ gstreamer-plugins-vaapi for Intel graphics).</p>
|
||||
@@ -35,7 +36,7 @@
|
||||
<p>First get the latest macOS release of GStreamer-1.0 from <a href="https://gstreamer.freedesktop.org/download/">https://gstreamer.freedesktop.org/download/</a>. Install both the macOS runtime and development installer packages. Assuming that the latest release is 1.18.5 they are <code>gstreamer-1.0-1.18.5-x86_64.pkg</code> and <code>gstreamer-1.0-devel-1.18.5-x86_64.pkg</code>. Click on them to install (they install to /Library/FrameWorks/GStreamer.framework). It is recommended you use GStreamer.framework rather than install Gstreamer with Homebrew or MacPorts (see later).</p>
|
||||
<p>Next install OpenSSL and libplist: these can be built from source (see below) but it is easier to get them using MacPorts “sudo port install openssl libplist-devel” or Homebrew “brew install openssl libplist”. Only the static forms of the two libraries will used for the macOS build, so they do not need to remain installed after you have built uxplay: if you don’t have MacPorts or Homebrew installed, you can just install one of these package-managers before building uxplay, and uninstall it afterwards if you do not want to keep it. Unfortunately, Fink’s openssl11-dev package currently doesn’t supply the static (libcrypto.a) form of the needed OpenSLL library libcrypto, and its libplist1 package is too old.</p>
|
||||
<p>Finally, build and install uxplay (without ZOOMFIX): open a terminal and change into the UxPlay source directory (“UxPlay-master” for zipfile downloads, “UxPlay” for “git clone” downloads) and build/install with “cmake . ; make ; sudo make install” (same as for Linux).</p>
|
||||
<p>The macOS build uses OpenGL, not X11, to create the mirror display window. This has some “quirks”: the window title is “OpenGL renderer” instead of the Airplay server name, but it is visible to screen-sharing apps (e.g., Zoom). The option -t <em>timeout</em> cannot be used because if the GStreamer pipeline is destroyed while the OpenGL window is still open, and uxplay is left running, a segfault occurs (this is an issue with the glimagesink GStreamer OpenGL plugin, not UxPlay). Also, the resolution settings “-s wxh” do not affect the (small) initial mirror window size, but the window can be expanded using the mouse or trackpad.</p>
|
||||
<p>On the macOS build, autovideosink uses OpenGL, not X11, to create the mirror display window (equivalent to “-vs glimagesink”; “-vs osxvideosink” can also be used). The window title does not show the Airplay server name, but it is visible to screen-sharing apps (e.g., Zoom). On macOS, The option -t <em>timeout</em> cannot be used because if the GStreamer pipeline is destroyed while the mirror window is still open, a segfault occurs (this is an issue with the GStreamer plugins, not UxPlay). Also, the resolution settings “-s wxh” do not affect the (small) initial OpenGL mirror window size, but the window can be expanded using the mouse or trackpad. In contrast, a window created with “-vs osxvideosink” is initially big, but has the wrong aspect ratio (stretched image); in this case the aspect ratio changes when the window width is changed by dragging its side.</p>
|
||||
<p><strong><em>Building OpenSSL and libplist from source on macOS</em></strong></p>
|
||||
<p>If you have have the standard GNU toolset (autoconf, automake, libtool, etc.) installed, you can also download and compile the source code for these libraries from <a href="https://www.openssl.org/source/">https://www.openssl.org/source/</a>, <a href="https://github.com/libimobiledevice/libplist">https://github.com/libimobiledevice/libplist</a>. Install the downloaded openssl by opening a terminal in your Downloads directory, and unpacking the source distribution openssl-3.0.0.tar.gz , (“tar -xvzf openssl-3.0.0.tar.gz ; cd openssl-3.0.0”). Then build/install with “./config ; make ; sudo make install_dev” and clean up after building uxplay with “sudo make uninstall” in the same directory. Similarly, for libplist, download the source as a zipfile from github as <a href="https://github.com/libimobiledevice/libplist/archive/refs/heads/master.zip">libplist-master.zip</a>, then unpack (“unzip libplist-master.zip ; cd libplist-master”), build/install (“./autogen.sh ; make ; sudo make install”) and clean up after uxplay is built with “sudo make uninstall” in the same directory.</p>
|
||||
<p><strong><em>Other ways (Homebrew, MacPorts) to install GStreamer on macOS (not recommended):</em></strong></p>
|
||||
@@ -45,18 +46,26 @@
|
||||
<li><p>with MacPorts: “sudo port install gstreamer1-gst-plugins-base gstreamer1-gst-plugins-good gstreamer1-gst-plugins-bad gstreamer1-gst-libav”. The MacPorts GStreamer is built to use X11, so must be run from an XQuartz terminal, can use ZOOMFIX, and needs option “-vs ximagesink”. On an older unibody MacBook Pro, the default resolution wxh = 1920x1080 was too large for the non-retina display, but using option “-s 800x600” worked; However, the GStreamer pipeline is fragile against attempts to change the X11 window size, or to rotations that switch a connected client between portrait and landscape mode while uxplay is running. Using the MacPorts X11 GStreamer is only viable if the image size is left unchanged from the initial “-s wxh” setting (also use the iPad/iPhone setting that locks the screen orientation against switching between portrait and landscape mode as the device is rotated).</p></li>
|
||||
</ol>
|
||||
<h1 id="troubleshooting"><strong>Troubleshooting:</strong></h1>
|
||||
<p>If uxplay starts, but stalls after “Initialized server socket(s)” appears, it is probably because a firewall is blocking access to the server on which it is running. If possible, either turn off the firewall to see if that is the problem, or get three consecutive network ports, starting at port n, all three in the range 1024-65535, opened for both tcp and udp, and use “uxplay -p n” (or open UDP 6000, 6001, 6011 TCP 7000,7001,7100 and use “uxplay -p”).</p>
|
||||
<p>Stalling after “Initialize server socket(s)”, with the server showing as available on the client iPad/iPhone, is almost certainly a firewall problem: one user was unaware that <em>two</em> firewalls (ufw and firewalld) were both active on their system.</p>
|
||||
<p>Stalling this way, but <em>without</em> the server showing as available on the client, probably means that your network <strong>does not have a running Bonjour/zeroconf DNS-SD server.</strong> On Linux, make sure Avahi is installed, and start the avahi-daemon service (your distribution will document how to do this). Some systems may instead use the mdnsd daemon as an alternative to provide DNS-SD service. (FreeBSD offers both alternatives, but only Avahi was tested: one of the steps needed for getting Avahi running on this system is to edit /usr/local/etc/avahi/avahi-daemon.conf to uncomment a line for airplay support.)</p>
|
||||
<p>For other problems after a connection is made, use “uxplay -d” (debug log option) to see what is happening. <strong>Such problems are usually due to a GStreamer plugin that doesn’t work on your system</strong>: (by default, GStreamer uses an algorithm to guess what is the “best” plugin to use on your system). If you use an nVidia graphics card, make sure that the gstreamer1.0-vaapi plugin for Intel graphics is <em>NOT</em> installed (<strong>uninstall it</strong> if it is installed!). (You can test for this by explicitly choosing the GStreamer videosink with option “-vs ximagesink” or “-vs xvimagesink”, to see if this fixes the problem, or “-vs vaapisink” to see if this reproduces the problem.)</p>
|
||||
<p>There are some reports of GStreamer problems with Intel graphics. One user (on Debian) solved this with “sudo apt install intel-media-va-driver-non-free”. This is a driver for 8’th (or later) generation "*-lake" Intel chips, that seems to be related to VAAPI accelerated graphics.</p>
|
||||
<p>Note: <code>uxplay</code> is run from a terminal command line, and informational messages are written to the terminal.</p>
|
||||
<h3 id="uxplay-starts-but-stalls-after-initialized-server-sockets-appears-without-any-server-name-showing-on-the-client.">1. uxplay starts, but stalls after “Initialized server socket(s)” appears, <em>without any server name showing on the client</em>.</h3>
|
||||
<p>Stalling this way, with <em>no</em> server name showing <em>on the client</em> as available, probably means that your network <strong>does not have a running Bonjour/zeroconf DNS-SD server.</strong> On Linux, make sure Avahi is installed, and start the avahi-daemon service on the system running uxplay (your distribution will document how to do this). Some systems may instead use the mdnsd daemon as an alternative to provide DNS-SD service. <em>(FreeBSD offers both alternatives, but only Avahi was tested: one of the steps needed for getting Avahi running on a FreeBSD system is to edit <code>/usr/local/etc/avahi/avahi-daemon.conf</code> to uncomment a line for airplay support.</em>)</p>
|
||||
<h3 id="uxplay-starts-but-stalls-after-initialized-server-sockets-appears-with-the-server-name-showing-on-the-client-but-the-client-fails-to-connect-when-the-uxplay-server-is-selected.">2. uxplay starts, but stalls after “Initialized server socket(s)” appears, <em>with the server name showing on the client</em> (but the client fails to connect when the UxPlay server is selected).</h3>
|
||||
<p>This shows that a <em>dns_sd</em> service is working, but a firewall on the server is probably blocking the connection request from the client. (One user who insisted that the firewall had been turned off turned out to have had <em>two</em> active firewalls (<em>firewalld</em> and <em>ufw</em>) <em>both</em> running on the server!) If possible, either turn off the firewall to see if that is the problem, or get three consecutive network ports, starting at port n, all three in the range 1024-65535, opened for both tcp and udp, and use “uxplay -p n” (or open UDP 6000, 6001, 6011 TCP 7000,7001,7100 and use “uxplay -p”).</p>
|
||||
<h3 id="problems-after-the-client-server-connection-has-been-made">3. Problems <em>after</em> the client-server connection has been made:</h3>
|
||||
<p>For such problems, use “uxplay -d” (debug log option) to see what is happening: it will show how far the connection process gets before the failure occurs.</p>
|
||||
<p><strong>Most such problems are due to a GStreamer plugin that doesn’t work on your system</strong>: (by default, GStreamer uses an algorithm to guess what is the “best” plugin to use on your system). A common case is that the GStreamer VAAPI plugin (for hardware-accelerated intel graphics) is being used on a system with nVidia graphics, If you use an nVidia graphics card, make sure that the gstreamer1.0-vaapi plugin for Intel graphics is <em>NOT</em> installed (<strong>uninstall it</strong> if it is installed!). (You can test for this by explicitly choosing the GStreamer videosink with option “-vs ximagesink” or “-vs xvimagesink”, to see if this fixes the problem, or “-vs vaapisink” to see if this reproduces the problem.)</p>
|
||||
<p>There are some reports of other GStreamer problems with hardware-accelerated Intel graphics. One user (on Debian) solved this with “sudo apt install intel-media-va-driver-non-free”. This is a driver for 8’th (or later) generation "*-lake" Intel chips, that seems to be related to VAAPI accelerated graphics.</p>
|
||||
<p>You can try to fix audio problems by using the “-as <em>audiosink</em>” option to choose the GStreamer audiosink , rather than have autoaudiosink pick one for you. The command “gst_inspect-1.0 | grep Sink | grep Audio” " will show you which audiosinks are available on your system. (Replace “Audio” by “Video” to see videosinks). Some possible audiosinks are pulsesink, alsasink, osssink, oss4sink, and osxaudiosink (macOS).</p>
|
||||
<p>If you ran cmake with “-DZOOMFIX=ON”, check if the problem is still there without ZOOMFIX. ZOOMFIX is only applied to the default videosink choice (“autovideosink”) and the two X11 videosinks “ximagesink” and “xvimagesink”. ZOOMFIX is only designed for these last two; if autovideosink chooses a different videosink, ZOOMFIX is now ignored. If you are using the X11 windowing system (standard on Linux), and have trouble with screen-sharing on Zoom, use ZOOMFIX and “-vs xvimagesink” (or “-vs ximagesink” if the previous choice doesn’t work).</p>
|
||||
<p>As other videosink choices are not affected by ZOOMFIX, they may or may not be visible to screen-sharing apps. Cairo-based windows created on Linux with “-vs gtksink” are visible to screen-sharing aps without ZOOMFIX; windows on macOS created by “-vs glimagesink” (default choice) and “-vs osximagesink” are also visible.</p>
|
||||
<p>The “OpenGL renderer” window created on Linux by “-vs glimagesink” sometimes does not close properly when its “close” button is clicked. (this is a GStreamer issue). You may need to terminate uxplay with Ctrl-C to close a “zombie” OpenGl window.</p>
|
||||
<p><strong>GStreamer issues:</strong> To troubleshoot GStreamer execute “export GST_DEBUG=2” to set the GStreamer debug-level environment-variable in the terminal where you will run uxplay, so that you see warning and error messages; (replace “2” by “4” to see much (much) more of what is happening inside GStreamer). Run “gst-inspect-1.0” to see which GStreamer plugins are installed on your system.</p>
|
||||
<h3 id="gstreamer-issues-missing-plugins-etc.">4. GStreamer issues (missing plugins, etc.):</h3>
|
||||
<p>To troubleshoot GStreamer execute “export GST_DEBUG=2” to set the GStreamer debug-level environment-variable in the terminal where you will run uxplay, so that you see warning and error messages; (replace “2” by “4” to see much (much) more of what is happening inside GStreamer). Run “gst-inspect-1.0” to see which GStreamer plugins are installed on your system.</p>
|
||||
<p>Some extra GStreamer packages for special plugins may need to be installed (or reinstalled: a user using a Wayland display system as an alternative to X11 reported that after reinstalling Lubuntu 18.4, UxPlay would not work until gstreamer1.0-x was installed, presumably for Wayland’s X11-compatibility mode). Different distributions may break up GStreamer 1.x into packages in different ways; the packages listed above in the build instructions should bring in other required GStreamer packages as dependencies, but will not install all possible plugins.</p>
|
||||
<p><strong>Use with non-Apple clients</strong>: one user tried to use UxPlay with an <em>airmypc</em> client (a non-free commercial Windows application that can mirror a Windows screen on an Apple TV using AirPlay mirror protocol). While <em>airmypc</em> can mirror to a true AppleTV and some other AirPlay receivers, UxPlay appears to correctly pair with this client, but then fails to decrypt both the audio and video streams. Possibly a different variant of the AirPlay encryption/decryption protocol not supported by UxPlay is used by this client. Without further information, there is no obvious fix. UxPlay reports itself to clients as “model appleTV2,1, sourceVersion 220.68”.</p>
|
||||
<h3 id="failure-to-decrypt-all-video-and-audio-streams-from-a-particular-older-client">5. Failure to decrypt ALL video and audio streams from a particular (older) client:</h3>
|
||||
<p>This triggers an error message, and will be due to use of an incorrect protocol for getting the AES decryption key from the client.</p>
|
||||
<p>Modern Apple clients use a more-encrypted protocol than older ones. Which protocol is used by UxPlay depends on the client <em>User-Agent</em> string (reported by the client and now shown in the terminal output). iOS 9 and 10 clients only use iTunes FairPlay encryption on the AES decryption key they send to the server. Somewhere around iOS sourceVersion 330 (part of the User-Agent string) Apple started to further encrypt it by a sha-512 hash with a “shared secret” created during the Server-Client pairing process. The sourceVersion 330 above which the extra decryption step is carried out is set in lib/global.h if you need to change it. (This applies only to audio decryption; the AES key used for video decryption has had this extra encryption since iOS 9).</p>
|
||||
<p>The third-party non-free Windows software <em>AirMyPC</em> (a commercial AirPlay emulator) uses an unhashed AES key for both audio and video encryption. <em>AirMyPC</em> has a distinctive <em>User-Agent</em> string, which is detected using two other settings in lib/global.h that can be adjusted if necessary. These settings might be useful if other AirPlay-emulators need support. Uxplay declares itself to be an AppleTV2,1 with sourceVersion 220.68; this can also be changed in global.h.</p>
|
||||
<h1 id="usage"><strong>Usage:</strong></h1>
|
||||
<p>Options:</p>
|
||||
<p><strong>-n server_name</strong> (Default: UxPlay); server_name@_hostname_ will be the name that appears offering AirPlay services to your iPad, iPhone etc, where <em>hostname</em> is the name of the server running uxplay. This will also now be the name shown above the mirror display (X11) window.</p>
|
||||
@@ -75,8 +84,10 @@
|
||||
<p><strong>-vs 0</strong> suppresses display of streamed video, but plays streamed audio. (The client’s screen is still mirrored at a reduced rate of 1 frame per second, but is not rendered or displayed.) This feature (which streams audio in AAC audio format) is now probably unneeded, as UxPlay can now stream superior-quality Apple Lossless audio without video in Airplay non-mirror mode.</p>
|
||||
<p><strong>-as <em>audiosink</em></strong> chooses the GStreamer audiosink, instead of letting autoaudiosink pick it for you. Some audiosink choices are: pulsesink, alsasink, osssink, oss4sink, and osxaudiosink (for macOS). Using quotes “…” might allow some parameters to be included with the audiosink name. (Some choices of audiosink might not work on your system.)</p>
|
||||
<p><strong>-as 0</strong> (or just <strong>-a</strong>) suppresses playing of streamed audio, but displays streamed video.</p>
|
||||
<p><strong>-t <em>timeout</em></strong> will cause the server to relaunch (without stopping uxplay) if no connections have been present during the previous <em>timeout</em> seconds. (You may wish to use this because the Server may not be visible to new Clients that were inactive when the Server was launched, and an idle Bonjour registration also eventually becomes unavailable for new connections.) The timer only starts once a Client has first made a mirror connection and then has disconnected with “Stop Mirrroring”. <em>This option should <strong>not</strong> be used if the display window is an OpenGL window on macOS, as such an OpenGL window created by GStreamer does not terminate correctly (it causes a segfault) if it is still open when the GStreamer pipeline is closed.</em></p>
|
||||
<p><strong>-t <em>timeout</em></strong> will cause the server to relaunch (without stopping uxplay) if no connections have been present during the previous <em>timeout</em> seconds. You may wish to use this if the Server is not visible to new Clients that were inactive when the Server was launched, and an idle Bonjour registration eventually becomes unavailable for new connections (this is a workaround for what may be due to a problem with your dns-sd or Avahi setup).<br />
|
||||
<em>This option should <strong>not</strong> be used on macOS, as a window created by GStreamer does not terminate correctly (it causes a segfault) if it is still open when the GStreamer pipeline is closed.</em></p>
|
||||
<h1 id="changelog">ChangeLog</h1>
|
||||
<p>1.44 2021-12-13 Omit hash of aeskey with ecdh_secret if sourceVersion <= 280.33 (this supports AirMyPC); internal rearrangement of where this hash is done. Replace decodebin by h264-specific elements in the GStreamer video pipeline. Fully report initial communications between client and server in -d debug mode.</p>
|
||||
<p>1.43 2021-12-07 Various internal changes, such as tests for successful decryption, uniform treatment of informational/debug messages, etc., updated README.</p>
|
||||
<p>1.42 2021-11-20 Fix MAC detection to work with modern Linux interface naming practices, MacOS and *BSD.</p>
|
||||
<p>1.41 2021-11-11 Further cleanups of multiple audio format support (internal changes, separated RAOP and GStreamer audio/video startup)</p>
|
||||
@@ -103,6 +114,7 @@
|
||||
<li><p>UxPlay now builds on macOS.</p></li>
|
||||
<li><p>The hostname of the server running uxplay is now appended to the AirPlay server name, which is now displayed as <em>name</em>@hostname, where <em>name</em> is “UxPlay”, (or whatever is set with the <strong>-n</strong> option).</p></li>
|
||||
<li><p>Added support for audio-only streaming with original (non-Mirror) AirPlay protocol, with Apple Lossless (ALAC) audio.</p></li>
|
||||
<li><p>Added suppport for the older AirPlay protocol used by third-party Windows-based AirPlay mirror emulators such as AirMyPC.</p></li>
|
||||
</ol>
|
||||
<h1 id="disclaimer">Disclaimer</h1>
|
||||
<p>All the resources in this repository are written using only freely available information from the internet. The code and related resources are meant for educational purposes only. It is the responsibility of the user to make sure all local laws are adhered to.</p>
|
||||
@@ -127,7 +139,7 @@
|
||||
<p>When the Apple TV 2nd generation was introduced in 2010, it received support for the AirTunes protocol. However, because this device allowed playback of visual content, the protocol was extended and renamed AirPlay. It was now possible to stream photo slideshows and videos. Shortly after the release of the Apple TV 2nd generation, AirPlay support for iOS was included in the iOS 4.2 update. It seems like at that point, the audio stream was still actually using the same AirTunes 2 protocol as described above. The video and photo streams were added as a whole new protocol based on HTTP, pretty much independent from the audio stream. Soon, the first curious developers began to <a href="https://web.archive.org/web/20101211213705/http://www.tuaw.com/2010/12/08/dear-aunt-tuaw-can-i-airplay-to-my-mac/">investigate how it worked</a>. Their conclusion was that visual content is streamed unencrypted.</p>
|
||||
<p>In April 2011, a talented hacker <a href="http://www.macrumors.com/2011/04/11/apple-airplay-private-key-exposed-opening-door-to-airport-express-emulators/">extracted the AirPlay private key</a> from an AirPort Express. This meant that finally, third-party developers were able to also build AirPlay receiver (server) programs.</p>
|
||||
<p>For iOS 5, released in 2011, Apple added a new protocol to the AirPlay suite: AirPlay mirroring. <a href="https://www.aorensoftware.com/blog/2011/08/20/exploring-airplay-mirroring-internals/">Initial investigators</a> found this new protocol used encryption in order to protect the transferred video data.</p>
|
||||
<p>By 2012, most of AirPlay’s protocols had been reverse-engineered and <a href="https://nto.github.io/AirPlay.html">documented</a>. At this point, audio still used the AirTunes 2 protocol from around 2008, video, photos and mirroring still used their respective protocols in an unmodified form, so you could still speak of AirPlay 1 (building upon AirTunes 2). The Airplay server running on the Apple TV reported as version 130. The setup of AirPlay mirroring used the xml format, in particular a stream.xml file. Additionally, it seems like the actual audio data is using the ALAC codec for audio-only (AirTunes 2) streaming and AAC for mirror audio. At least these different formats were used in <a href="https://github.com/espes/Slave-in-the-Magic-Mirror/issues/12#issuecomment-372380451">later iOS versions</a>.</p>
|
||||
<p>By 2012, most of AirPlay’s protocols had been reverse-engineered and <a href="https://nto.github.io/AirPlay.html">documented</a> (see also <a href="https://openairplay.github.io/airplay-spec">updated version</a>). At this point, audio still used the AirTunes 2 protocol from around 2008, video, photos and mirroring still used their respective protocols in an unmodified form, so you could still speak of AirPlay 1 (building upon AirTunes 2). The Airplay server running on the Apple TV reported as version 130. The setup of AirPlay mirroring used the xml format, in particular a stream.xml file. Additionally, it seems like the actual audio data is using the ALAC codec for audio-only (AirTunes 2) streaming and AAC for mirror audio. At least these different formats were used in <a href="https://github.com/espes/Slave-in-the-Magic-Mirror/issues/12#issuecomment-372380451">later iOS versions</a>.</p>
|
||||
<p>Sometime before iOS 9, the protocol for mirroring was slightly modified: Instead of the “stream.xml” API endpoint, the same information could also be querried in binary plist form, just by changing the API endpoint to “stream”, without any extension. I wasn’t able to figure out which of these was actually used by what specific client / server versions.</p>
|
||||
<p>For iOS 9, Apple made <a href="https://9to5mac.com/2015/09/11/apple-ios-9-airplay-improvements-screen-mirroring/">considerable changes</a> to the AirPlay protocol in 2015, including audio and mirroring. Apparently, the audio protocol was only slightly modified, and a <a href="https://github.com/juhovh/shairplay/issues/43">minor change</a> restored compatibility. For mirroring, an <a href="https://github.com/juhovh/shairplay/issues/43#issuecomment-142115959">additional pairing phase</a> was added to the connection establishment procedure, consisting of pair-setup and pair-verify calls. Seemingly, these were added in order to simplify usage with devices that are connected frequently. Pair-setup is used only the first time an iOS device connects to an AirPlay receiver. The generated cryptographic binding can be used for pair-verify in later sessions. Additionally, the stream / stream.xml endpoint was replaced with the info endpoint (only available as binary plist AFAICT). As of iOS 12, the protocol introduced with iOS 9 was still supported with only slight modifications, albeit as a legacy mode. While iOS 9 used two SETUP calls (one for general connection and mirroring video, and one for audio), iOS 12 legacy mode uses 3 SETUP calls (one for general connection (timing and events), one for mirroring video, one for audio).</p>
|
||||
<p>The release of tvOS 10.2 broke many third-party AirPlay sender (client) programs in 2017. The reason was that it was now mandatory to perform device verification via a pin in order to stream content to an Apple TV. The functionality had been in the protocol before, but was not mandatory. Some discussion about the new scheme can be found <a href="https://github.com/postlund/pyatv/issues/79">here</a>. A full specification of the pairing and authentication protocol was made available on <a href="https://htmlpreview.github.io/?https://github.com/philippe44/RAOP-Player/blob/master/doc/auth_protocol.html">GitHub</a>. At that point, tvOS 10.2 reported as AirTunes/320.20.</p>
|
||||
|
||||
176
README.md
176
README.md
@@ -1,33 +1,50 @@
|
||||
# UxPlay 1.44: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.
|
||||
|
||||
# UxPlay 1.43: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.
|
||||
This project is a GPLv3 open source unix AirPlay2 Mirror server for Linux, macOS, and \*BSD.
|
||||
It is now hosted at the
|
||||
github site [https://github.com/FDH2/UxPlay](https://github.com/FDH2/UxPlay) (where development and user-assistance now takes place), although it initially was developed by
|
||||
[antimof](http://github.com/antimof/Uxplay) using code
|
||||
from [RPiPlay](https://github.com/FD-/RPiPlay), which in turn derives from
|
||||
[AirplayServer](https://github.com/KqsMea8/AirplayServer),
|
||||
[shairplay](https://github.com/juhovh/shairplay), and [playfair](https://github.com/EstebanKubata/playfair). (The antimof site is
|
||||
mainly inactive, but periodically posts updates pulled from the [main UxPlay site](https://github.com/FDH2/UxPlay)).
|
||||
|
||||
This project is a GPLv3 unix AirPlay2 server which now also works on macOS.
|
||||
Its main use is to act like an AppleTV for screen-mirroring (with audio) of iOS/macOS clients
|
||||
(iPads, iPhones, MacBooks) in a window on the server display (with the possibility of
|
||||
Its main use is to act like an AppleTV for screen-mirroring (with audio) of iOS/iPadOS/macOS clients
|
||||
(iPhones, iPads, MacBooks) in a window
|
||||
on the server display (with the possibility of
|
||||
sharing that window on screen-sharing applications such as Zoom)
|
||||
on a host running Linux, macOS, or other unix, using Apple's AirPlay Mirror protocol first available in iOS 5.
|
||||
(Details of what is known about the AirPlay2 protocol can be found [here](https://github.com/SteeBono/airplayreceiver/wiki/AirPlay2-Protocol) and
|
||||
[here](https://emanuelecozzi.net/docs/airplay2)). **Note that Apple DRM (as in Apple TV app content) cannot be decrypted by UxPlay.**
|
||||
on a host running Linux, macOS, or other unix. UxPlay supports a "legacy" form of Apple's AirPlay Mirror protocol introduced
|
||||
in iOS 12; client devices running iOS/iPadOS 12 or later are supported, as is a (nonfree) Windows-based
|
||||
AirPlay-client software emulator, AirMyPC. Older (32-bit) client devices that can only run iOS 9.3 or iOS 10.3 are
|
||||
currently partially supported by UxPlay: reports indicate that
|
||||
screen-mirroring video works, audio is a work in progess.
|
||||
(Details of what is publically known about Apple's AirPlay2 protocol can be found
|
||||
[here](https://github.com/SteeBono/airplayreceiver/wiki/AirPlay2-Protocol) and
|
||||
[here](https://emanuelecozzi.net/docs/airplay2)).
|
||||
|
||||
The UxPlay server and its client must be on the same local area network,
|
||||
on which a **Bonjour/Zeroconf mDNS/DNS-SD server** is also running
|
||||
(only DNS-SD "Service Discovery" service is necessary, it is not necessary that the local network also be of the ".local" mDNS-based type).
|
||||
(only DNS-SD "Service Discovery" service is strictly 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 [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).
|
||||
|
||||
_Since v1.38, UxPlay now also supports the Airplay audio-only protocol as well as AirPlay Mirror protocol, and
|
||||
(when the client screen is not being mirrored)
|
||||
can play Apple Lossless (ALAC) audio streamed from the client without video (the accompanying cover-art and metadata is not displayed).
|
||||
The initial connection to the client can be either AirPlay audio or Airplay Mirror mode. An Airplay Mirror connection (with "Advanced Audio Coding" AAC-ELD lossy-compression audio)
|
||||
switches to ALAC if the mirrow window is closed and an AirPlay audio connection is started, and back again to AAC if a new
|
||||
Airplay Mirror connection is made_.
|
||||
Connections to the UxPlay server by
|
||||
iOS/MacOS clients can be initiated both in AirPlay Mirror mode (which streams
|
||||
lossily-compressed AAC audio while mirroring the client screen,
|
||||
or in the alternative AirPlay Audio mode which streams
|
||||
Apple Lossless (ALAC) audio without screen mirroring (the accompanying metadata and cover art in
|
||||
this mode is not displayed). _Switching between these two modes during an active connection is
|
||||
possible: in Mirror mode, close the mirror window and start an Audio mode connection,
|
||||
switch back by initiating a Mirror mode connection._ **Note that Apple DRM
|
||||
(as in Apple TV app content on the client) cannot be decrypted by UxPlay,
|
||||
and (unlike with a true AppleTV), the client cannot run a http connection on the server
|
||||
instead of streaming content from one on the client.**
|
||||
|
||||
UxPlay is based on https://github.com/FD-/RPiPlay, with GStreamer integration from
|
||||
https://github.com/antimof/UxPlay.
|
||||
(UxPlay only uses GStreamer, and does not contain the alternative Raspberry-Pi-specific
|
||||
audio and video renderers also found in RPiPlay.)
|
||||
Tested on a number of systems, including Ubuntu 20.04, Linux Mint 20.2, OpenSUSE 15.3, macOS 10.15, FreeBSD 13.0.
|
||||
UxPlay uses GStreamer Plugins for rendering audio and video,
|
||||
and does not offer the alternative Raspberry-Pi-specific
|
||||
audio and video renderers available in [RPiPlay](https://github.com/FD-/RPiPlay).
|
||||
It is tested on a number of systems, including (among others) Debian 11.2, Ubuntu 20.04 and 21.10, Linux Mint 20.2, OpenSUSE 15.3, macOS 10.15.7, FreeBSD 13.0.
|
||||
|
||||
Using Gstreamer means that video and audio are supported "out of the box", using a choice of plugins.
|
||||
Gstreamer decoding is plugin agnostic, and uses accelerated decoders if
|
||||
@@ -36,7 +53,7 @@ available. For Intel integrated graphics, the VAAPI plugin is preferable, (but d
|
||||
### Note to packagers: OpenSSL-3.0.0 solves GPL v3 license issues.
|
||||
|
||||
Some Linux distributions such as Debian do not allow distribution of compiled
|
||||
GPL code linked to OpenSLL-1.1.1 because its "dual OpenSSL/SSLeay" license
|
||||
GPL code linked to OpenSSL-1.1.1 because its "dual OpenSSL/SSLeay" license
|
||||
has some incompatibilites with GPL, unless all code authors have explicitly given an "exception" to allow
|
||||
such linking (the historical origins of UxPlay make this impossible to obtain). Other distributions
|
||||
treat OpenSSL as a "System Library" which the GPL allows linking to.
|
||||
@@ -50,9 +67,10 @@ Either download and unzip [UxPlay-master.zip](https://github.com/FDH2/UxPlay/arc
|
||||
or (if git is installed): "git clone https://github.com/FDH2/UxPlay".
|
||||
|
||||
*This is also a pull request on the
|
||||
original site https://github.com/antimof/UxPlay ; but is unlikely to ever
|
||||
get committed into the codebase there, as that
|
||||
project appears to be inactive.
|
||||
original site https://github.com/antimof/UxPlay ; that original
|
||||
project is inactive, but the pull request with
|
||||
changes up to 2021-12-10
|
||||
were recently merged with the antimof tree (thank you antimof!).
|
||||
|
||||
## Building UxPlay on Linux (or \*BSD):
|
||||
|
||||
@@ -65,7 +83,7 @@ In a terminal window, change directories to the source directory of the
|
||||
downloaded source code ("UxPlay-master" for zipfile downloads, "UxPlay" for "git clone" downloads), then do
|
||||
|
||||
1. `sudo apt-get install libssl-dev libplist-dev libavahi-compat-libdnssd-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-libav gstreamer1.0-plugins-bad`
|
||||
2. `sudo apt-get install gstreamer1.0-vaapi` (For Intel graphics, but not nVidia graphics)
|
||||
2. `sudo apt-get install gstreamer1.0-vaapi` (For hardware-accelerated Intel graphics, but not nVidia graphics)
|
||||
3. `sudo apt-get install libx11-dev` (for the "ZOOMFIX" X11_display name fix for screen-sharing with e.g., ZOOM)
|
||||
4. `cmake .` (or "`cmake -DZOOMFIX=ON .`" to get a screen-sharing fix to
|
||||
make X11 mirror display windows visible to screen-sharing applications such as
|
||||
@@ -76,8 +94,8 @@ Zoom, see below).
|
||||
_If you intend to modify the code, use a separate "build" directory: replace_ "```cmake [ ] . ```" _by_ "```mkdir build ; cd build ; cmake [ ] ..```"; _you can then clean
|
||||
the build directory with_ "```rm -rf build/* ```" _(run from within the UxPlay source directory) without affecting the source directories which contain your modifications_.
|
||||
|
||||
The above script installs the executable file "`uxplay`" to `/usr/local/bin`, (and installs a manpage to `/usr/local/man/man1` and README
|
||||
files to `/usr/local/share/doc/uxplay`).
|
||||
The above script installs the executable file "`uxplay`" to `/usr/local/bin`, (and installs a manpage to somewhere like `/usr/local/share/man/man1` and README
|
||||
files to somewhere like `/usr/local/share/doc/uxplay`).
|
||||
It can also be found in the build directory after the build
|
||||
processs. Run uxplay in a terminal window.
|
||||
|
||||
@@ -139,15 +157,16 @@ Finally, build and install uxplay (without ZOOMFIX): open a terminal and change
|
||||
("UxPlay-master" for zipfile downloads, "UxPlay" for "git clone" downloads) and build/install with
|
||||
"cmake . ; make ; sudo make install " (same as for Linux).
|
||||
|
||||
The macOS build uses OpenGL, not X11, to create the mirror display window. This has some "quirks":
|
||||
the window title is "OpenGL renderer" instead of the Airplay server name, but it is visible to
|
||||
screen-sharing apps (e.g., Zoom). The option -t _timeout_
|
||||
cannot be used because if the GStreamer pipeline is destroyed while the OpenGL window is still open,
|
||||
and uxplay is left running, a segfault occurs (this is an issue with the glimagesink GStreamer OpenGL
|
||||
plugin, not UxPlay).
|
||||
Also, the resolution settings "-s wxh" do not affect
|
||||
the (small) initial mirror window size, but the window can be expanded using the mouse or trackpad.
|
||||
|
||||
On the macOS build, autovideosink uses OpenGL, not X11, to create the mirror display window (equivalent to
|
||||
"-vs glimagesink"; "-vs osxvideosink" can also be used).
|
||||
The window title does not show the Airplay server name, but it is visible to
|
||||
screen-sharing apps (e.g., Zoom). On macOS, The option -t _timeout_
|
||||
cannot be used because if the GStreamer pipeline is destroyed while the mirror window is still open,
|
||||
a segfault occurs (this is an issue with the GStreamer plugins, not UxPlay).
|
||||
Also, the resolution settings "-s wxh" do not affect
|
||||
the (small) initial OpenGL mirror window size, but the window can be expanded using the mouse or trackpad.
|
||||
In contrast, a window created with "-vs osxvideosink" is initially big, but has the wrong aspect ratio (stretched image);
|
||||
in this case the aspect ratio changes when the window width is changed by dragging its side.
|
||||
|
||||
***Building OpenSSL and libplist from source on macOS***
|
||||
|
||||
@@ -184,30 +203,37 @@ as the device is rotated).
|
||||
|
||||
# **Troubleshooting:**
|
||||
|
||||
If uxplay starts, but stalls after "Initialized server socket(s)" appears,
|
||||
it is probably because a firewall is blocking
|
||||
access to the server on which it is running. If possible, either turn off the firewall
|
||||
Note: ```uxplay``` is run from a terminal command line, and informational messages are written to the terminal.
|
||||
|
||||
### 1. uxplay starts, but stalls after "Initialized server socket(s)" appears, *without any server name showing on the client*.
|
||||
|
||||
Stalling this way, with _no_ server name showing _on the client_ as available,
|
||||
probably means that your network **does not have a running Bonjour/zeroconf DNS-SD server.**
|
||||
On Linux, make sure Avahi is installed,
|
||||
and start the avahi-daemon service on the system running uxplay (your distribution will document how to do this).
|
||||
Some systems may instead use the mdnsd daemon as an alternative to provide DNS-SD service.
|
||||
_(FreeBSD offers both alternatives, but only Avahi was tested: one of the steps needed for
|
||||
getting Avahi running on a FreeBSD system is to edit ```/usr/local/etc/avahi/avahi-daemon.conf``` to
|
||||
uncomment a line for airplay support._)
|
||||
|
||||
### 2. uxplay starts, but stalls after "Initialized server socket(s)" appears, *with the server name showing on the client* (but the client fails to connect when the UxPlay server is selected).
|
||||
|
||||
This shows that a *dns_sd* service is working, but a firewall on the server is probably blocking the connection request from the client.
|
||||
(One user who insisted that the firewall had been turned off turned out to have had _two_ active firewalls (*firewalld* and *ufw*)
|
||||
_both_ running on the server!) If possible, either turn off the firewall
|
||||
to see if that is the problem, or get three consecutive network ports,
|
||||
starting at port n, all three in the range 1024-65535, opened for both tcp and udp, and use "uxplay -p n"
|
||||
(or open UDP 6000, 6001, 6011 TCP 7000,7001,7100 and use "uxplay -p").
|
||||
|
||||
Stalling after "Initialize server socket(s)", with the server showing as available on the client iPad/iPhone,
|
||||
is almost certainly a firewall problem: one user was unaware that
|
||||
_two_ firewalls (ufw and firewalld) were both active on their system.
|
||||
### 3. Problems _after_ the client-server connection has been made:
|
||||
|
||||
Stalling this way, but _without_ the server showing as available on the client,
|
||||
probably means that your network **does not have a running Bonjour/zeroconf DNS-SD server.**
|
||||
On Linux, make sure Avahi is installed,
|
||||
and start the avahi-daemon service (your distribution will document how to do this).
|
||||
Some systems may instead use the mdnsd daemon as an alternative to provide DNS-SD service.
|
||||
(FreeBSD offers both alternatives, but only Avahi was tested: one of the steps needed for
|
||||
getting Avahi running on this system is to edit /usr/local/etc/avahi/avahi-daemon.conf to
|
||||
uncomment a line for airplay support.)
|
||||
For such problems, use "uxplay -d " (debug log option) to see what is happening: it will show how far the connection process gets before
|
||||
the failure occurs.
|
||||
|
||||
For other problems after a connection is made, use "uxplay -d " (debug log option) to see what is happening.
|
||||
**Such problems are usually due to a GStreamer plugin that doesn't work on your system**: (by default,
|
||||
**Most such problems are due to a GStreamer plugin that doesn't work on your system**: (by default,
|
||||
GStreamer uses an algorithm to guess what is the "best"
|
||||
plugin to use on your system).
|
||||
plugin to use on your system). A common case is that the GStreamer VAAPI plugin
|
||||
(for hardware-accelerated intel graphics) is being used on a system with nVidia graphics,
|
||||
If you use an
|
||||
nVidia graphics card, make sure that the gstreamer1.0-vaapi
|
||||
plugin for Intel graphics is *NOT* installed (**uninstall it** if it is installed!).
|
||||
@@ -215,7 +241,7 @@ plugin for Intel graphics is *NOT* installed (**uninstall it** if it is installe
|
||||
"-vs ximagesink" or "-vs xvimagesink", to see if this fixes the problem, or "-vs vaapisink" to see if this
|
||||
reproduces the problem.)
|
||||
|
||||
There are some reports of GStreamer problems with Intel graphics. One user
|
||||
There are some reports of other GStreamer problems with hardware-accelerated Intel graphics. One user
|
||||
(on Debian) solved this with "sudo apt install intel-media-va-driver-non-free". This is a driver for 8'th (or later) generation
|
||||
"*-lake" Intel chips, that seems to be related to VAAPI accelerated graphics.
|
||||
|
||||
@@ -238,7 +264,9 @@ Cairo-based windows created on Linux with "-vs gtksink" are visible to screen-sh
|
||||
The "OpenGL renderer" window created on Linux by "-vs glimagesink" sometimes does not close properly when its "close" button is clicked.
|
||||
(this is a GStreamer issue). You may need to terminate uxplay with Ctrl-C to close a "zombie" OpenGl window.
|
||||
|
||||
__GStreamer issues:__ To troubleshoot GStreamer execute "export GST_DEBUG=2"
|
||||
### 4. GStreamer issues (missing plugins, etc.):
|
||||
|
||||
To troubleshoot GStreamer execute "export GST_DEBUG=2"
|
||||
to set the GStreamer debug-level environment-variable in the terminal
|
||||
where you will run uxplay, so that you see warning and error messages;
|
||||
(replace "2" by "4" to see much (much) more of what is happening inside
|
||||
@@ -250,12 +278,20 @@ reported that after reinstalling Lubuntu 18.4, UxPlay would not work until gstr
|
||||
Different distributions may break up GStreamer 1.x into packages in different ways; the packages listed above in the build instructions should bring in
|
||||
other required GStreamer packages as dependencies, but will not install all possible plugins.
|
||||
|
||||
**Use with non-Apple clients**: one user tried to use UxPlay with an *airmypc* client (a non-free commercial
|
||||
Windows application that can mirror a Windows screen on an Apple TV using AirPlay mirror protocol). While *airmypc*
|
||||
can mirror to a true AppleTV and some other AirPlay receivers, UxPlay appears to correctly pair with this client, but
|
||||
then fails to decrypt both the audio and video streams. Possibly a different variant of the AirPlay encryption/decryption
|
||||
protocol not supported by UxPlay is used by this client. Without further information, there is no obvious fix.
|
||||
UxPlay reports itself to clients as "model appleTV2,1, sourceVersion 220.68".
|
||||
### 5. Failure to decrypt ALL video and audio streams from a particular (older) client:
|
||||
|
||||
This triggers an error message, and will be due to use of an incorrect protocol for getting the AES decryption key from the client.
|
||||
|
||||
Modern Apple clients use a more-encrypted protocol than older ones.
|
||||
Which protocol is used by UxPlay depends on the client _User-Agent_ string (reported by the client and now shown in the terminal output).
|
||||
iOS 9 and 10 clients only use iTunes FairPlay encryption on the AES decryption key they send to the server.
|
||||
Somewhere around iOS sourceVersion 330 (part of the User-Agent string) Apple started to further encrypt it by a sha-512 hash with a "shared secret" created
|
||||
during the Server-Client pairing process. The sourceVersion 330 above which the extra decryption step is carried out is set in lib/global.h if you need to
|
||||
change it. (This applies only to audio decryption; the AES key used for video decryption has had this extra encryption since iOS 9).
|
||||
|
||||
The third-party non-free Windows software _AirMyPC_ (a commercial AirPlay emulator) uses an unhashed AES key for both audio and video encryption. _AirMyPC_ has a distinctive
|
||||
_User-Agent_ string, which is detected using two other settings in lib/global.h that can be adjusted if necessary. These settings might be useful if
|
||||
other AirPlay-emulators need support. Uxplay declares itself to be an AppleTV2,1 with sourceVersion 220.68; this can also be changed in global.h.
|
||||
|
||||
# **Usage:**
|
||||
|
||||
@@ -350,17 +386,21 @@ Also: image transforms that had been added to RPiPlay have been ported to UxPlay
|
||||
**-as 0** (or just **-a**) suppresses playing of streamed audio, but displays streamed video.
|
||||
|
||||
**-t _timeout_** will cause the server to relaunch (without stopping uxplay) if no connections
|
||||
have been present during the previous _timeout_ seconds. (You may wish to use this because the Server may not be
|
||||
visible to new Clients that were inactive when the Server was launched, and an idle Bonjour
|
||||
registration also eventually becomes unavailable for new connections.) The timer only starts once a
|
||||
Client has first made a mirror connection and then has disconnected with "Stop Mirrroring".
|
||||
_This option should **not** be
|
||||
used if the display window is an OpenGL window on macOS, as such an OpenGL window created
|
||||
have been present during the previous _timeout_ seconds. You may wish to use this if the Server
|
||||
is not visible to new Clients that were inactive when the Server was launched, and an idle Bonjour
|
||||
registration eventually becomes unavailable for new connections (this is a workaround for what
|
||||
may be due to a problem with your dns-sd or Avahi setup).
|
||||
_This option should **not** be used on macOS, as a window created
|
||||
by GStreamer does not terminate correctly (it causes a segfault)
|
||||
if it is still open when the GStreamer pipeline is closed._
|
||||
|
||||
|
||||
# ChangeLog
|
||||
1.44 2021-12-13 Omit hash of aeskey with ecdh_secret if sourceVersion <= 280.33 (this supports AirMyPC);
|
||||
internal rearrangement of where this hash is done. Replace decodebin by h264-specific
|
||||
elements in the GStreamer video pipeline. Fully report initial communications between
|
||||
client and server in -d debug mode.
|
||||
|
||||
1.43 2021-12-07 Various internal changes, such as tests for successful decryption, uniform treatment
|
||||
of informational/debug messages, etc., updated README.
|
||||
|
||||
@@ -465,6 +505,8 @@ is compiled.) On macOS, Avahi is not used.
|
||||
|
||||
12. Added support for audio-only streaming with original (non-Mirror) AirPlay protocol, with Apple Lossless (ALAC) audio.
|
||||
|
||||
13. Added suppport for the older AirPlay protocol used by third-party Windows-based AirPlay mirror emulators such as AirMyPC.
|
||||
|
||||
# Disclaimer
|
||||
|
||||
All the resources in this repository are written using only freely available information from the internet. The code and related resources are meant for educational purposes only. It is the responsibility of the user to make sure all local laws are adhered to.
|
||||
@@ -511,7 +553,7 @@ In April 2011, a talented hacker [extracted the AirPlay private key](http://www.
|
||||
For iOS 5, released in 2011, Apple added a new protocol to the AirPlay suite: AirPlay mirroring. [Initial investigators](https://www.aorensoftware.com/blog/2011/08/20/exploring-airplay-mirroring-internals/) found this new protocol used encryption in order to protect the transferred video data.
|
||||
|
||||
|
||||
By 2012, most of AirPlay's protocols had been reverse-engineered and [documented](https://nto.github.io/AirPlay.html). At this point, audio still used the AirTunes 2 protocol from around 2008, video, photos and mirroring still used their respective protocols in an unmodified form, so you could still speak of AirPlay 1 (building upon AirTunes 2). The Airplay server running on the Apple TV reported as version 130. The setup of AirPlay mirroring used the xml format, in particular a stream.xml file.
|
||||
By 2012, most of AirPlay's protocols had been reverse-engineered and [documented](https://nto.github.io/AirPlay.html) (see also [updated version](https://openairplay.github.io/airplay-spec)). At this point, audio still used the AirTunes 2 protocol from around 2008, video, photos and mirroring still used their respective protocols in an unmodified form, so you could still speak of AirPlay 1 (building upon AirTunes 2). The Airplay server running on the Apple TV reported as version 130. The setup of AirPlay mirroring used the xml format, in particular a stream.xml file.
|
||||
Additionally, it seems like the actual audio data is using the ALAC codec for audio-only (AirTunes 2) streaming and AAC for mirror audio. At least these different formats were used in [later iOS versions](https://github.com/espes/Slave-in-the-Magic-Mirror/issues/12#issuecomment-372380451).
|
||||
|
||||
|
||||
|
||||
277
README.txt
277
README.txt
@@ -1,44 +1,61 @@
|
||||
UxPlay 1.43: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.
|
||||
UxPlay 1.44: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.
|
||||
======================================================================
|
||||
|
||||
This project is a GPLv3 unix AirPlay2 server which now also works on
|
||||
macOS. Its main use is to act like an AppleTV for screen-mirroring (with
|
||||
audio) of iOS/macOS clients (iPads, iPhones, MacBooks) in a window on
|
||||
This project is a GPLv3 open source unix AirPlay2 Mirror server for
|
||||
Linux, macOS, and \*BSD. It is now hosted at the github site
|
||||
<https://github.com/FDH2/UxPlay> (where development and user-assistance
|
||||
now takes place), although it initially was developed by
|
||||
[antimof](http://github.com/antimof/Uxplay) using code from
|
||||
[RPiPlay](https://github.com/FD-/RPiPlay), which in turn derives from
|
||||
[AirplayServer](https://github.com/KqsMea8/AirplayServer),
|
||||
[shairplay](https://github.com/juhovh/shairplay), and
|
||||
[playfair](https://github.com/EstebanKubata/playfair). (The antimof site
|
||||
is mainly inactive, but periodically posts updates pulled from the [main
|
||||
UxPlay site](https://github.com/FDH2/UxPlay)).
|
||||
|
||||
Its main use is to act like an AppleTV for screen-mirroring (with audio)
|
||||
of iOS/iPadOS/macOS clients (iPhones, iPads, MacBooks) in a window on
|
||||
the server display (with the possibility of sharing that window on
|
||||
screen-sharing applications such as Zoom) on a host running Linux,
|
||||
macOS, or other unix, using Apple's AirPlay Mirror protocol first
|
||||
available in iOS 5. (Details of what is known about the AirPlay2
|
||||
protocol can be found
|
||||
macOS, or other unix. UxPlay supports a "legacy" form of Apple's AirPlay
|
||||
Mirror protocol introduced in iOS 12; client devices running iOS/iPadOS
|
||||
12 or later are supported, as is a (nonfree) Windows-based
|
||||
AirPlay-client software emulator, AirMyPC. Older (32-bit) client devices
|
||||
that can only run iOS 9.3 or iOS 10.3 are currently partially supported
|
||||
by UxPlay: reports indicate that screen-mirroring video works, audio is
|
||||
a work in progess. (Details of what is publically known about Apple's
|
||||
AirPlay2 protocol can be found
|
||||
[here](https://github.com/SteeBono/airplayreceiver/wiki/AirPlay2-Protocol)
|
||||
and [here](https://emanuelecozzi.net/docs/airplay2)). **Note that Apple
|
||||
DRM (as in Apple TV app content) cannot be decrypted by UxPlay.**
|
||||
and [here](https://emanuelecozzi.net/docs/airplay2)).
|
||||
|
||||
The UxPlay server and its client must be on the same local area network,
|
||||
on which a **Bonjour/Zeroconf mDNS/DNS-SD server** is also running (only
|
||||
DNS-SD "Service Discovery" service is 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
|
||||
DNS-SD "Service Discovery" service is strictly 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
|
||||
[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).
|
||||
|
||||
*Since v1.38, UxPlay now also supports the Airplay audio-only protocol
|
||||
as well as AirPlay Mirror protocol, and (when the client screen is not
|
||||
being mirrored) can play Apple Lossless (ALAC) audio streamed from the
|
||||
client without video (the accompanying cover-art and metadata is not
|
||||
displayed). The initial connection to the client can be either AirPlay
|
||||
audio or Airplay Mirror mode. An Airplay Mirror connection (with
|
||||
"Advanced Audio Coding" AAC-ELD lossy-compression audio) switches to
|
||||
ALAC if the mirrow window is closed and an AirPlay audio connection is
|
||||
started, and back again to AAC if a new Airplay Mirror connection is
|
||||
made*.
|
||||
Connections to the UxPlay server by iOS/MacOS clients can be initiated
|
||||
both in AirPlay Mirror mode (which streams lossily-compressed AAC audio
|
||||
while mirroring the client screen, or in the alternative AirPlay Audio
|
||||
mode which streams Apple Lossless (ALAC) audio without screen mirroring
|
||||
(the accompanying metadata and cover art in this mode is not displayed).
|
||||
*Switching between these two modes during an active connection is
|
||||
possible: in Mirror mode, close the mirror window and start an Audio
|
||||
mode connection, switch back by initiating a Mirror mode connection.*
|
||||
**Note that Apple DRM (as in Apple TV app content on the client) cannot
|
||||
be decrypted by UxPlay, and (unlike with a true AppleTV), the client
|
||||
cannot run a http connection on the server instead of streaming content
|
||||
from one on the client.**
|
||||
|
||||
UxPlay is based on https://github.com/FD-/RPiPlay, with GStreamer
|
||||
integration from https://github.com/antimof/UxPlay. (UxPlay only uses
|
||||
GStreamer, and does not contain the alternative Raspberry-Pi-specific
|
||||
audio and video renderers also found in RPiPlay.) Tested on a number of
|
||||
systems, including Ubuntu 20.04, Linux Mint 20.2, OpenSUSE 15.3, macOS
|
||||
10.15, FreeBSD 13.0.
|
||||
UxPlay uses GStreamer Plugins for rendering audio and video, and does
|
||||
not offer the alternative Raspberry-Pi-specific audio and video
|
||||
renderers available in [RPiPlay](https://github.com/FD-/RPiPlay). It is
|
||||
tested on a number of systems, including (among others) Debian 11.2,
|
||||
Ubuntu 20.04 and 21.10, Linux Mint 20.2, OpenSUSE 15.3, macOS 10.15.7,
|
||||
FreeBSD 13.0.
|
||||
|
||||
Using Gstreamer means that video and audio are supported "out of the
|
||||
box", using a choice of plugins. Gstreamer decoding is plugin agnostic,
|
||||
@@ -49,7 +66,7 @@ nVidia).
|
||||
### Note to packagers: OpenSSL-3.0.0 solves GPL v3 license issues.
|
||||
|
||||
Some Linux distributions such as Debian do not allow distribution of
|
||||
compiled GPL code linked to OpenSLL-1.1.1 because its "dual
|
||||
compiled GPL code linked to OpenSSL-1.1.1 because its "dual
|
||||
OpenSSL/SSLeay" license has some incompatibilites with GPL, unless all
|
||||
code authors have explicitly given an "exception" to allow such linking
|
||||
(the historical origins of UxPlay make this impossible to obtain). Other
|
||||
@@ -68,9 +85,9 @@ Either download and unzip
|
||||
or (if git is installed): "git clone https://github.com/FDH2/UxPlay".
|
||||
|
||||
\*This is also a pull request on the original site
|
||||
https://github.com/antimof/UxPlay ; but is unlikely to ever get
|
||||
committed into the codebase there, as that project appears to be
|
||||
inactive.
|
||||
https://github.com/antimof/UxPlay ; that original project is inactive,
|
||||
but the pull request with changes up to 2021-12-10 were recently merged
|
||||
with the antimof tree (thank you antimof!).
|
||||
|
||||
Building UxPlay on Linux (or \*BSD):
|
||||
------------------------------------
|
||||
@@ -88,8 +105,8 @@ directories to the source directory of the downloaded source code
|
||||
downloads), then do
|
||||
|
||||
1. `sudo apt-get install libssl-dev libplist-dev libavahi-compat-libdnssd-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-libav gstreamer1.0-plugins-bad`
|
||||
2. `sudo apt-get install gstreamer1.0-vaapi` (For Intel graphics, but
|
||||
not nVidia graphics)
|
||||
2. `sudo apt-get install gstreamer1.0-vaapi` (For hardware-accelerated
|
||||
Intel graphics, but not nVidia graphics)
|
||||
3. `sudo apt-get install libx11-dev` (for the "ZOOMFIX" X11\_display
|
||||
name fix for screen-sharing with e.g., ZOOM)
|
||||
4. `cmake .` (or "`cmake -DZOOMFIX=ON .`" to get a screen-sharing fix
|
||||
@@ -107,10 +124,10 @@ directory) without affecting the source directories which contain your
|
||||
modifications*.
|
||||
|
||||
The above script installs the executable file "`uxplay`" to
|
||||
`/usr/local/bin`, (and installs a manpage to `/usr/local/man/man1` and
|
||||
README files to `/usr/local/share/doc/uxplay`). It can also be found in
|
||||
the build directory after the build processs. Run uxplay in a terminal
|
||||
window.
|
||||
`/usr/local/bin`, (and installs a manpage to somewhere like
|
||||
`/usr/local/share/man/man1` and README files to somewhere like
|
||||
`/usr/local/share/doc/uxplay`). It can also be found in the build
|
||||
directory after the build processs. Run uxplay in a terminal window.
|
||||
|
||||
**Note libplist-dev (version 2.0 or greater) is a new dependency (the
|
||||
original antimof version UxPlay-1.2 supplied it). Older Linux
|
||||
@@ -189,15 +206,19 @@ change into the UxPlay source directory ("UxPlay-master" for zipfile
|
||||
downloads, "UxPlay" for "git clone" downloads) and build/install with
|
||||
"cmake . ; make ; sudo make install" (same as for Linux).
|
||||
|
||||
The macOS build uses OpenGL, not X11, to create the mirror display
|
||||
window. This has some "quirks": the window title is "OpenGL renderer"
|
||||
instead of the Airplay server name, but it is visible to screen-sharing
|
||||
apps (e.g., Zoom). The option -t *timeout* cannot be used because if the
|
||||
GStreamer pipeline is destroyed while the OpenGL window is still open,
|
||||
and uxplay is left running, a segfault occurs (this is an issue with the
|
||||
glimagesink GStreamer OpenGL plugin, not UxPlay). Also, the resolution
|
||||
settings "-s wxh" do not affect the (small) initial mirror window size,
|
||||
but the window can be expanded using the mouse or trackpad.
|
||||
On the macOS build, autovideosink uses OpenGL, not X11, to create the
|
||||
mirror display window (equivalent to "-vs glimagesink"; "-vs
|
||||
osxvideosink" can also be used). The window title does not show the
|
||||
Airplay server name, but it is visible to screen-sharing apps (e.g.,
|
||||
Zoom). On macOS, The option -t *timeout* cannot be used because if the
|
||||
GStreamer pipeline is destroyed while the mirror window is still open, a
|
||||
segfault occurs (this is an issue with the GStreamer plugins, not
|
||||
UxPlay). Also, the resolution settings "-s wxh" do not affect the
|
||||
(small) initial OpenGL mirror window size, but the window can be
|
||||
expanded using the mouse or trackpad. In contrast, a window created with
|
||||
"-vs osxvideosink" is initially big, but has the wrong aspect ratio
|
||||
(stretched image); in this case the aspect ratio changes when the window
|
||||
width is changed by dragging its side.
|
||||
|
||||
***Building OpenSSL and libplist from source on macOS***
|
||||
|
||||
@@ -249,46 +270,56 @@ pkgconfig" ; MacPorts: "sudo port install pkgconfig" ).
|
||||
**Troubleshooting:**
|
||||
====================
|
||||
|
||||
If uxplay starts, but stalls after "Initialized server socket(s)"
|
||||
appears, it is probably because a firewall is blocking access to the
|
||||
server on which it is running. If possible, either turn off the firewall
|
||||
to see if that is the problem, or get three consecutive network ports,
|
||||
starting at port n, all three in the range 1024-65535, opened for both
|
||||
tcp and udp, and use "uxplay -p n" (or open UDP 6000, 6001, 6011 TCP
|
||||
7000,7001,7100 and use "uxplay -p").
|
||||
Note: `uxplay` is run from a terminal command line, and informational
|
||||
messages are written to the terminal.
|
||||
|
||||
Stalling after "Initialize server socket(s)", with the server showing as
|
||||
available on the client iPad/iPhone, is almost certainly a firewall
|
||||
problem: one user was unaware that *two* firewalls (ufw and firewalld)
|
||||
were both active on their system.
|
||||
### 1. uxplay starts, but stalls after "Initialized server socket(s)" appears, *without any server name showing on the client*.
|
||||
|
||||
Stalling this way, but *without* the server showing as available on the
|
||||
client, probably means that your network **does not have a running
|
||||
Stalling this way, with *no* server name showing *on the client* as
|
||||
available, probably means that your network **does not have a running
|
||||
Bonjour/zeroconf DNS-SD server.** On Linux, make sure Avahi is
|
||||
installed, and start the avahi-daemon service (your distribution will
|
||||
document how to do this). Some systems may instead use the mdnsd daemon
|
||||
as an alternative to provide DNS-SD service. (FreeBSD offers both
|
||||
alternatives, but only Avahi was tested: one of the steps needed for
|
||||
getting Avahi running on this system is to edit
|
||||
/usr/local/etc/avahi/avahi-daemon.conf to uncomment a line for airplay
|
||||
support.)
|
||||
installed, and start the avahi-daemon service on the system running
|
||||
uxplay (your distribution will document how to do this). Some systems
|
||||
may instead use the mdnsd daemon as an alternative to provide DNS-SD
|
||||
service. *(FreeBSD offers both alternatives, but only Avahi was tested:
|
||||
one of the steps needed for getting Avahi running on a FreeBSD system is
|
||||
to edit `/usr/local/etc/avahi/avahi-daemon.conf` to uncomment a line for
|
||||
airplay support.*)
|
||||
|
||||
For other problems after a connection is made, use "uxplay -d" (debug
|
||||
log option) to see what is happening. **Such problems are usually due to
|
||||
a GStreamer plugin that doesn't work on your system**: (by default,
|
||||
GStreamer uses an algorithm to guess what is the "best" plugin to use on
|
||||
your system). If you use an nVidia graphics card, make sure that the
|
||||
gstreamer1.0-vaapi plugin for Intel graphics is *NOT* installed
|
||||
(**uninstall it** if it is installed!). (You can test for this by
|
||||
explicitly choosing the GStreamer videosink with option "-vs ximagesink"
|
||||
or "-vs xvimagesink", to see if this fixes the problem, or "-vs
|
||||
vaapisink" to see if this reproduces the problem.)
|
||||
### 2. uxplay starts, but stalls after "Initialized server socket(s)" appears, *with the server name showing on the client* (but the client fails to connect when the UxPlay server is selected).
|
||||
|
||||
There are some reports of GStreamer problems with Intel graphics. One
|
||||
user (on Debian) solved this with "sudo apt install
|
||||
intel-media-va-driver-non-free". This is a driver for 8'th (or later)
|
||||
generation \"\*-lake\" Intel chips, that seems to be related to VAAPI
|
||||
accelerated graphics.
|
||||
This shows that a *dns\_sd* service is working, but a firewall on the
|
||||
server is probably blocking the connection request from the client. (One
|
||||
user who insisted that the firewall had been turned off turned out to
|
||||
have had *two* active firewalls (*firewalld* and *ufw*) *both* running
|
||||
on the server!) If possible, either turn off the firewall to see if that
|
||||
is the problem, or get three consecutive network ports, starting at port
|
||||
n, all three in the range 1024-65535, opened for both tcp and udp, and
|
||||
use "uxplay -p n" (or open UDP 6000, 6001, 6011 TCP 7000,7001,7100 and
|
||||
use "uxplay -p").
|
||||
|
||||
### 3. Problems *after* the client-server connection has been made:
|
||||
|
||||
For such problems, use "uxplay -d" (debug log option) to see what is
|
||||
happening: it will show how far the connection process gets before the
|
||||
failure occurs.
|
||||
|
||||
**Most such problems are due to a GStreamer plugin that doesn't work on
|
||||
your system**: (by default, GStreamer uses an algorithm to guess what is
|
||||
the "best" plugin to use on your system). A common case is that the
|
||||
GStreamer VAAPI plugin (for hardware-accelerated intel graphics) is
|
||||
being used on a system with nVidia graphics, If you use an nVidia
|
||||
graphics card, make sure that the gstreamer1.0-vaapi plugin for Intel
|
||||
graphics is *NOT* installed (**uninstall it** if it is installed!). (You
|
||||
can test for this by explicitly choosing the GStreamer videosink with
|
||||
option "-vs ximagesink" or "-vs xvimagesink", to see if this fixes the
|
||||
problem, or "-vs vaapisink" to see if this reproduces the problem.)
|
||||
|
||||
There are some reports of other GStreamer problems with
|
||||
hardware-accelerated Intel graphics. One user (on Debian) solved this
|
||||
with "sudo apt install intel-media-va-driver-non-free". This is a driver
|
||||
for 8'th (or later) generation \"\*-lake\" Intel chips, that seems to be
|
||||
related to VAAPI accelerated graphics.
|
||||
|
||||
You can try to fix audio problems by using the "-as *audiosink*" option
|
||||
to choose the GStreamer audiosink , rather than have autoaudiosink pick
|
||||
@@ -317,12 +348,14 @@ sometimes does not close properly when its "close" button is clicked.
|
||||
(this is a GStreamer issue). You may need to terminate uxplay with
|
||||
Ctrl-C to close a "zombie" OpenGl window.
|
||||
|
||||
**GStreamer issues:** To troubleshoot GStreamer execute "export
|
||||
GST\_DEBUG=2" to set the GStreamer debug-level environment-variable in
|
||||
the terminal where you will run uxplay, so that you see warning and
|
||||
error messages; (replace "2" by "4" to see much (much) more of what is
|
||||
happening inside GStreamer). Run "gst-inspect-1.0" to see which
|
||||
GStreamer plugins are installed on your system.
|
||||
### 4. GStreamer issues (missing plugins, etc.):
|
||||
|
||||
To troubleshoot GStreamer execute "export GST\_DEBUG=2" to set the
|
||||
GStreamer debug-level environment-variable in the terminal where you
|
||||
will run uxplay, so that you see warning and error messages; (replace
|
||||
"2" by "4" to see much (much) more of what is happening inside
|
||||
GStreamer). Run "gst-inspect-1.0" to see which GStreamer plugins are
|
||||
installed on your system.
|
||||
|
||||
Some extra GStreamer packages for special plugins may need to be
|
||||
installed (or reinstalled: a user using a Wayland display system as an
|
||||
@@ -333,16 +366,31 @@ GStreamer 1.x into packages in different ways; the packages listed above
|
||||
in the build instructions should bring in other required GStreamer
|
||||
packages as dependencies, but will not install all possible plugins.
|
||||
|
||||
**Use with non-Apple clients**: one user tried to use UxPlay with an
|
||||
*airmypc* client (a non-free commercial Windows application that can
|
||||
mirror a Windows screen on an Apple TV using AirPlay mirror protocol).
|
||||
While *airmypc* can mirror to a true AppleTV and some other AirPlay
|
||||
receivers, UxPlay appears to correctly pair with this client, but then
|
||||
fails to decrypt both the audio and video streams. Possibly a different
|
||||
variant of the AirPlay encryption/decryption protocol not supported by
|
||||
UxPlay is used by this client. Without further information, there is no
|
||||
obvious fix. UxPlay reports itself to clients as "model appleTV2,1,
|
||||
sourceVersion 220.68".
|
||||
### 5. Failure to decrypt ALL video and audio streams from a particular (older) client:
|
||||
|
||||
This triggers an error message, and will be due to use of an incorrect
|
||||
protocol for getting the AES decryption key from the client.
|
||||
|
||||
Modern Apple clients use a more-encrypted protocol than older ones.
|
||||
Which protocol is used by UxPlay depends on the client *User-Agent*
|
||||
string (reported by the client and now shown in the terminal output).
|
||||
iOS 9 and 10 clients only use iTunes FairPlay encryption on the AES
|
||||
decryption key they send to the server. Somewhere around iOS
|
||||
sourceVersion 330 (part of the User-Agent string) Apple started to
|
||||
further encrypt it by a sha-512 hash with a "shared secret" created
|
||||
during the Server-Client pairing process. The sourceVersion 330 above
|
||||
which the extra decryption step is carried out is set in lib/global.h if
|
||||
you need to change it. (This applies only to audio decryption; the AES
|
||||
key used for video decryption has had this extra encryption since iOS
|
||||
9).
|
||||
|
||||
The third-party non-free Windows software *AirMyPC* (a commercial
|
||||
AirPlay emulator) uses an unhashed AES key for both audio and video
|
||||
encryption. *AirMyPC* has a distinctive *User-Agent* string, which is
|
||||
detected using two other settings in lib/global.h that can be adjusted
|
||||
if necessary. These settings might be useful if other AirPlay-emulators
|
||||
need support. Uxplay declares itself to be an AppleTV2,1 with
|
||||
sourceVersion 220.68; this can also be changed in global.h.
|
||||
|
||||
**Usage:**
|
||||
==========
|
||||
@@ -446,19 +494,24 @@ displays streamed video.
|
||||
|
||||
**-t *timeout*** will cause the server to relaunch (without stopping
|
||||
uxplay) if no connections have been present during the previous
|
||||
*timeout* seconds. (You may wish to use this because the Server may not
|
||||
be visible to new Clients that were inactive when the Server was
|
||||
launched, and an idle Bonjour registration also eventually becomes
|
||||
unavailable for new connections.) The timer only starts once a Client
|
||||
has first made a mirror connection and then has disconnected with "Stop
|
||||
Mirrroring". *This option should **not** be used if the display window
|
||||
is an OpenGL window on macOS, as such an OpenGL window created by
|
||||
*timeout* seconds. You may wish to use this if the Server is not visible
|
||||
to new Clients that were inactive when the Server was launched, and an
|
||||
idle Bonjour registration eventually becomes unavailable for new
|
||||
connections (this is a workaround for what may be due to a problem with
|
||||
your dns-sd or Avahi setup).\
|
||||
*This option should **not** be used on macOS, as a window created by
|
||||
GStreamer does not terminate correctly (it causes a segfault) if it is
|
||||
still open when the GStreamer pipeline is closed.*
|
||||
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
1.44 2021-12-13 Omit hash of aeskey with ecdh\_secret if sourceVersion
|
||||
\<= 280.33 (this supports AirMyPC); internal rearrangement of where this
|
||||
hash is done. Replace decodebin by h264-specific elements in the
|
||||
GStreamer video pipeline. Fully report initial communications between
|
||||
client and server in -d debug mode.
|
||||
|
||||
1.43 2021-12-07 Various internal changes, such as tests for successful
|
||||
decryption, uniform treatment of informational/debug messages, etc.,
|
||||
updated README.
|
||||
@@ -582,6 +635,9 @@ Improvements since the original UxPlay by antimof:
|
||||
12. Added support for audio-only streaming with original (non-Mirror)
|
||||
AirPlay protocol, with Apple Lossless (ALAC) audio.
|
||||
|
||||
13. Added suppport for the older AirPlay protocol used by third-party
|
||||
Windows-based AirPlay mirror emulators such as AirMyPC.
|
||||
|
||||
Disclaimer
|
||||
==========
|
||||
|
||||
@@ -697,11 +753,12 @@ found this new protocol used encryption in order to protect the
|
||||
transferred video data.
|
||||
|
||||
By 2012, most of AirPlay's protocols had been reverse-engineered and
|
||||
[documented](https://nto.github.io/AirPlay.html). At this point, audio
|
||||
still used the AirTunes 2 protocol from around 2008, video, photos and
|
||||
mirroring still used their respective protocols in an unmodified form,
|
||||
so you could still speak of AirPlay 1 (building upon AirTunes 2). The
|
||||
Airplay server running on the Apple TV reported as version 130. The
|
||||
[documented](https://nto.github.io/AirPlay.html) (see also [updated
|
||||
version](https://openairplay.github.io/airplay-spec)). At this point,
|
||||
audio still used the AirTunes 2 protocol from around 2008, video, photos
|
||||
and mirroring still used their respective protocols in an unmodified
|
||||
form, so you could still speak of AirPlay 1 (building upon AirTunes 2).
|
||||
The Airplay server running on the Apple TV reported as version 130. The
|
||||
setup of AirPlay mirroring used the xml format, in particular a
|
||||
stream.xml file. Additionally, it seems like the actual audio data is
|
||||
using the ALAC codec for audio-only (AirTunes 2) streaming and AAC for
|
||||
|
||||
10
lib/global.h
10
lib/global.h
@@ -5,6 +5,16 @@
|
||||
#define GLOBAL_MODEL "AppleTV2,1"
|
||||
#define GLOBAL_VERSION "220.68"
|
||||
|
||||
/* use old protocol (unhashed AESkey for audio decryption if clients source version is not greater than 330.x.x */
|
||||
#define OLD_PROTOCOL_AUDIO_CLIENT_SOURCEVERSION "330.0.0"
|
||||
|
||||
/* use old protocol for audio or video AES key if client's User-Agent string is contained in these strings */
|
||||
/* replace xxx by any new User-Agent string as needed */
|
||||
#define OLD_PROTOCOL_AUDIO_CLIENT_USER_AGENT_LIST "AirMyPC/2.0;xxx"
|
||||
#define OLD_PROTOCOL_VIDEO_CLIENT_USER_AGENT_LIST "AirMyPC/2.0;xxx"
|
||||
|
||||
#define DECRYPTION_TEST 0 /* set to 1 or 2 to examine audio decryption */
|
||||
|
||||
#define MAX_HWADDR_LEN 6
|
||||
|
||||
#endif
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "http_request.h"
|
||||
#include "llhttp/llhttp.h"
|
||||
@@ -251,3 +252,38 @@ http_request_get_data(http_request_t *request, int *datalen)
|
||||
}
|
||||
return request->data;
|
||||
}
|
||||
|
||||
int
|
||||
http_request_get_header_string(http_request_t *request, char **header_str)
|
||||
{
|
||||
if(!request || request->headers_size == 0) {
|
||||
*header_str = NULL;
|
||||
return 0;
|
||||
}
|
||||
int len = 0;
|
||||
for (int i = 0; i < request->headers_size; i++) {
|
||||
len += strlen(request->headers[i]);
|
||||
if (i%2 == 0) {
|
||||
len += 2;
|
||||
} else {
|
||||
len++;
|
||||
}
|
||||
}
|
||||
char *str = calloc(len+1, sizeof(char));
|
||||
assert(str);
|
||||
*header_str = str;
|
||||
char *p = str;
|
||||
for (int i = 0; i < request->headers_size; i++) {
|
||||
sprintf(p,"%s", request->headers[i]);
|
||||
p += strlen(request->headers[i]);
|
||||
if (i%2 == 0) {
|
||||
sprintf(p, ": ");
|
||||
p +=2;
|
||||
} else {
|
||||
sprintf(p, "\n");
|
||||
p++;
|
||||
}
|
||||
}
|
||||
assert(p == &(str[len]));
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ const char *http_request_get_method(http_request_t *request);
|
||||
const char *http_request_get_url(http_request_t *request);
|
||||
const char *http_request_get_header(http_request_t *request, const char *name);
|
||||
const char *http_request_get_data(http_request_t *request, int *datalen);
|
||||
int http_request_get_header_string(http_request_t *request, char **header_str);
|
||||
|
||||
void http_request_destroy(http_request_t *request);
|
||||
|
||||
|
||||
@@ -34,19 +34,13 @@ struct mirror_buffer_s {
|
||||
/* AES key and IV */
|
||||
// Need secondary processing to use
|
||||
unsigned char aeskey[RAOP_AESKEY_LEN];
|
||||
unsigned char ecdh_secret[32];
|
||||
};
|
||||
|
||||
void
|
||||
mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, uint64_t streamConnectionID)
|
||||
{
|
||||
sha_ctx_t *ctx = sha_init();
|
||||
unsigned char eaeskey[64] = {};
|
||||
unsigned char eaeskey[16];
|
||||
memcpy(eaeskey, mirror_buffer->aeskey, 16);
|
||||
sha_update(ctx, eaeskey, 16);
|
||||
sha_update(ctx, mirror_buffer->ecdh_secret, 32);
|
||||
sha_final(ctx, eaeskey, NULL);
|
||||
|
||||
unsigned char hash1[64];
|
||||
unsigned char hash2[64];
|
||||
char* skey = "AirPlayStreamKey";
|
||||
@@ -55,7 +49,8 @@ mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, uint64_t streamConnection
|
||||
unsigned char sivall[255];
|
||||
sprintf((char*) skeyall, "%s%" PRIu64, skey, streamConnectionID);
|
||||
sprintf((char*) sivall, "%s%" PRIu64, siv, streamConnectionID);
|
||||
sha_reset(ctx);
|
||||
|
||||
sha_ctx_t *ctx = sha_init();
|
||||
sha_update(ctx, skeyall, strlen((char*) skeyall));
|
||||
sha_update(ctx, eaeskey, 16);
|
||||
sha_final(ctx, hash1, NULL);
|
||||
@@ -82,22 +77,18 @@ mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, uint64_t streamConnection
|
||||
}
|
||||
|
||||
mirror_buffer_t *
|
||||
mirror_buffer_init(logger_t *logger,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *ecdh_secret)
|
||||
mirror_buffer_init(logger_t *logger, const unsigned char *aeskey)
|
||||
{
|
||||
mirror_buffer_t *mirror_buffer;
|
||||
assert(aeskey);
|
||||
assert(ecdh_secret);
|
||||
mirror_buffer = calloc(1, sizeof(mirror_buffer_t));
|
||||
if (!mirror_buffer) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(mirror_buffer->aeskey, aeskey, RAOP_AESKEY_LEN);
|
||||
memcpy(mirror_buffer->ecdh_secret, ecdh_secret, 32);
|
||||
mirror_buffer->logger = logger;
|
||||
mirror_buffer->nextDecryptCount = 0;
|
||||
//mirror_buffer_init_aes(mirror_buffer, aeskey, ecdh_secret, streamConnectionID);
|
||||
//mirror_buffer_init_aes(mirror_buffer, aeskey, streamConnectionID);
|
||||
return mirror_buffer;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
typedef struct mirror_buffer_s mirror_buffer_t;
|
||||
|
||||
|
||||
mirror_buffer_t *mirror_buffer_init( logger_t *logger,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *ecdh_secret);
|
||||
mirror_buffer_t *mirror_buffer_init( logger_t *logger, const unsigned char *aeskey);
|
||||
void mirror_buffer_init_aes(mirror_buffer_t *mirror_buffer, uint64_t streamConnectionID);
|
||||
void mirror_buffer_decrypt(mirror_buffer_t *raop_mirror, unsigned char* input, unsigned char* output, int datalen);
|
||||
void mirror_buffer_destroy(mirror_buffer_t *mirror_buffer);
|
||||
|
||||
66
lib/raop.c
66
lib/raop.c
@@ -161,6 +161,38 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
|
||||
if (!method || !cseq) {
|
||||
return;
|
||||
}
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s %s RTSP/1.0", method, url);
|
||||
char *header_str= NULL;
|
||||
http_request_get_header_string(request, &header_str);
|
||||
if (header_str) {
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", header_str);
|
||||
bool data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL);
|
||||
bool data_is_text = (strstr(header_str,"text/parameters") != NULL);
|
||||
free(header_str);
|
||||
int request_datalen;
|
||||
const char *request_data = http_request_get_data(request, &request_datalen);
|
||||
if (request_data) {
|
||||
if (request_datalen > 0) {
|
||||
if (data_is_plist) {
|
||||
plist_t req_root_node = NULL;
|
||||
plist_from_bin(request_data, request_datalen, &req_root_node);
|
||||
char * plist_xml;
|
||||
uint32_t plist_len;
|
||||
plist_to_xml(req_root_node, &plist_xml, &plist_len);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml);
|
||||
free(plist_xml);
|
||||
} else if (data_is_text) {
|
||||
char *data_str = utils_data_to_text((char *) request_data, request_datalen);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str);
|
||||
free(data_str);
|
||||
} else {
|
||||
char *data_str = utils_data_to_string((unsigned char *) request_data, request_datalen, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str);
|
||||
free(data_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*response = http_response_init("RTSP/1.0", 200, "OK");
|
||||
|
||||
@@ -262,8 +294,42 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
|
||||
if (handler != NULL) {
|
||||
handler(conn, request, *response, &response_data, &response_datalen);
|
||||
}
|
||||
|
||||
|
||||
http_response_finish(*response, response_data, response_datalen);
|
||||
|
||||
int len;
|
||||
const char *data = http_response_get_data(*response, &len);
|
||||
if (response_data && response_datalen > 0) {
|
||||
len -= response_datalen;
|
||||
} else {
|
||||
len -= 2;
|
||||
}
|
||||
header_str = utils_data_to_text(data, len);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s", header_str);
|
||||
bool data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL);
|
||||
bool data_is_text = (strstr(header_str,"text/parameters") != NULL);
|
||||
free(header_str);
|
||||
if (response_data) {
|
||||
if (response_datalen > 0) {
|
||||
if (data_is_plist) {
|
||||
plist_t res_root_node = NULL;
|
||||
plist_from_bin(response_data, response_datalen, &res_root_node);
|
||||
char * plist_xml;
|
||||
uint32_t plist_len;
|
||||
plist_to_xml(res_root_node, &plist_xml, &plist_len);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml);
|
||||
free(plist_xml);
|
||||
} else if (data_is_text) {
|
||||
char *data_str = utils_data_to_text((char*) response_data, response_datalen);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str);
|
||||
free(data_str);
|
||||
} else {
|
||||
char *data_str = utils_data_to_string((unsigned char *) response_data, response_datalen, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str);
|
||||
free(data_str);
|
||||
}
|
||||
}
|
||||
free(response_data);
|
||||
response_data = NULL;
|
||||
response_datalen = 0;
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "crypto.h"
|
||||
#include "compat.h"
|
||||
#include "stream.h"
|
||||
#include "global.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define RAOP_BUFFER_LENGTH 32
|
||||
|
||||
@@ -57,24 +59,20 @@ struct raop_buffer_s {
|
||||
raop_buffer_entry_t entries[RAOP_BUFFER_LENGTH];
|
||||
};
|
||||
|
||||
void
|
||||
raop_buffer_init_key_iv(raop_buffer_t *raop_buffer,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *aesiv,
|
||||
const unsigned char *ecdh_secret)
|
||||
raop_buffer_t *
|
||||
raop_buffer_init(logger_t *logger,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *aesiv)
|
||||
{
|
||||
|
||||
// Initialization key
|
||||
unsigned char eaeskey[64];
|
||||
memcpy(eaeskey, aeskey, 16);
|
||||
|
||||
sha_ctx_t *ctx = sha_init();
|
||||
sha_update(ctx, eaeskey, 16);
|
||||
sha_update(ctx, ecdh_secret, 32);
|
||||
sha_final(ctx, eaeskey, NULL);
|
||||
sha_destroy(ctx);
|
||||
|
||||
memcpy(raop_buffer->aeskey, eaeskey, 16);
|
||||
raop_buffer_t *raop_buffer;
|
||||
assert(aeskey);
|
||||
assert(aesiv);
|
||||
raop_buffer = calloc(1, sizeof(raop_buffer_t));
|
||||
if (!raop_buffer) {
|
||||
return NULL;
|
||||
}
|
||||
raop_buffer->logger = logger;
|
||||
memcpy(raop_buffer->aeskey, aeskey, RAOP_AESKEY_LEN);
|
||||
memcpy(raop_buffer->aesiv, aesiv, RAOP_AESIV_LEN);
|
||||
|
||||
#ifdef DUMP_AUDIO
|
||||
@@ -84,24 +82,6 @@ raop_buffer_init_key_iv(raop_buffer_t *raop_buffer,
|
||||
fclose(file_keyiv);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
raop_buffer_t *
|
||||
raop_buffer_init(logger_t *logger,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *aesiv,
|
||||
const unsigned char *ecdh_secret)
|
||||
{
|
||||
raop_buffer_t *raop_buffer;
|
||||
assert(aeskey);
|
||||
assert(aesiv);
|
||||
assert(ecdh_secret);
|
||||
raop_buffer = calloc(1, sizeof(raop_buffer_t));
|
||||
if (!raop_buffer) {
|
||||
return NULL;
|
||||
}
|
||||
raop_buffer->logger = logger;
|
||||
raop_buffer_init_key_iv(raop_buffer, aeskey, aesiv, ecdh_secret);
|
||||
|
||||
for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) {
|
||||
raop_buffer_entry_t *entry = &raop_buffer->entries[i];
|
||||
@@ -171,6 +151,10 @@ raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned ch
|
||||
}
|
||||
#endif
|
||||
|
||||
if (DECRYPTION_TEST) {
|
||||
printf("encrypted header %s", utils_data_to_string(data,12,12));
|
||||
if (payload_size) printf("%d before %s", payload_size, utils_data_to_string(&data[12],16,16 ));
|
||||
}
|
||||
encryptedlen = payload_size / 16*16;
|
||||
memset(output, 0, payload_size);
|
||||
// Need to be initialized internally
|
||||
@@ -180,7 +164,18 @@ raop_buffer_decrypt(raop_buffer_t *raop_buffer, unsigned char *data, unsigned ch
|
||||
|
||||
memcpy(output + encryptedlen, &data[12 + encryptedlen], payload_size - encryptedlen);
|
||||
*outputlen = payload_size;
|
||||
|
||||
if (payload_size && DECRYPTION_TEST){
|
||||
if ( !(output[0] == 0x8d || output[0] == 0x8e || output[0] == 0x81 || output[0] == 0x82 || output[0] == 0x20)) {
|
||||
printf("***ERROR AUDIO FRAME IS NOT AAC_ELD OR ALAC\n");
|
||||
}
|
||||
if (DECRYPTION_TEST == 2) {
|
||||
printf("decrypted audio frame, len = %d\n", *outputlen);
|
||||
printf("%s",utils_data_to_string(output,payload_size,16));
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("%d after %s\n", payload_size, utils_data_to_string(output,16,16 ));
|
||||
}
|
||||
}
|
||||
#ifdef DUMP_AUDIO
|
||||
// Decrypted file
|
||||
if (file_aac != NULL) {
|
||||
|
||||
@@ -24,8 +24,7 @@ typedef int (*raop_resend_cb_t)(void *opaque, unsigned short seqno, unsigned sho
|
||||
|
||||
raop_buffer_t *raop_buffer_init(logger_t *logger,
|
||||
const unsigned char *aeskey,
|
||||
const unsigned char *aesiv,
|
||||
const unsigned char *ecdh_secret);
|
||||
const unsigned char *aesiv);
|
||||
int raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned short datalen, uint64_t timestamp, int use_seqnum);
|
||||
void *raop_buffer_dequeue(raop_buffer_t *raop_buffer, unsigned int *length, uint64_t *timestamp, int no_resend);
|
||||
void raop_buffer_handle_resends(raop_buffer_t *raop_buffer, raop_resend_cb_t resend_cb, void *opaque);
|
||||
|
||||
@@ -162,12 +162,6 @@ raop_handler_info(raop_conn_t *conn,
|
||||
|
||||
plist_to_bin(r_node, response_data, (uint32_t *) response_datalen);
|
||||
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "UxPlay server info sent in response to client \"GET /info\" request, len = %d:", *response_datalen);
|
||||
char * plist_xml;
|
||||
uint32_t plist_len;
|
||||
plist_to_xml(r_node, &plist_xml, &plist_len);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml);
|
||||
free(plist_xml);
|
||||
free(pk);
|
||||
free(hw_addr);
|
||||
}
|
||||
@@ -346,15 +340,9 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
// Parsing bplist
|
||||
plist_t req_root_node = NULL;
|
||||
plist_from_bin(data, data_len, &req_root_node);
|
||||
char * plist_xml;
|
||||
uint32_t plist_len;
|
||||
plist_to_xml(req_root_node, &plist_xml, &plist_len);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml);
|
||||
free(plist_xml);
|
||||
plist_t req_streams_node = plist_dict_get_item(req_root_node, "streams");
|
||||
plist_t req_eiv_node = plist_dict_get_item(req_root_node, "eiv");
|
||||
plist_t req_ekey_node = plist_dict_get_item(req_root_node, "ekey");
|
||||
|
||||
plist_t req_eiv_node = plist_dict_get_item(req_root_node, "eiv");
|
||||
|
||||
// For the response
|
||||
plist_t res_root_node = plist_new_dict();
|
||||
|
||||
@@ -362,7 +350,12 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
// The first SETUP call that initializes keys and timing
|
||||
|
||||
unsigned char aesiv[16];
|
||||
unsigned char aeskey_old[16];
|
||||
unsigned char aeskey[16];
|
||||
unsigned char *aeskey_audio = aeskey;
|
||||
unsigned char *aeskey_video = aeskey;
|
||||
|
||||
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "SETUP 1");
|
||||
|
||||
// First setup
|
||||
@@ -371,16 +364,75 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
plist_get_data_val(req_eiv_node, &eiv, &eiv_len);
|
||||
memcpy(aesiv, eiv, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "eiv_len = %llu", eiv_len);
|
||||
char* str = utils_data_to_string(aesiv, 16, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aesiv (needed for AES-CBC audio decryption iv):\n%s", str);
|
||||
free(str);
|
||||
|
||||
char* ekey = NULL;
|
||||
uint64_t ekey_len = 0;
|
||||
plist_get_data_val(req_ekey_node, &ekey, &ekey_len);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len);
|
||||
|
||||
// ekey is 72 bytes, aeskey is 16 bytes
|
||||
int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) ekey, aeskey);
|
||||
str = utils_data_to_string((unsigned char *) ekey, ekey_len, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey:\n%s", str);
|
||||
free (str);
|
||||
|
||||
int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) ekey, aeskey_old);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "fairplay_decrypt ret = %d", ret);
|
||||
str = utils_data_to_string(aeskey_old, 16, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey (fairplay-decrypted from ekey):\n%s", str);
|
||||
free(str);
|
||||
|
||||
unsigned char ecdh_secret[X25519_KEY_SIZE];
|
||||
pairing_get_ecdh_secret_key(conn->pairing, ecdh_secret);
|
||||
str = utils_data_to_string(ecdh_secret, X25519_KEY_SIZE, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "32 byte shared ecdh_secret:\n%s", str);
|
||||
free(str);
|
||||
|
||||
unsigned char eaeskey[64] = {};
|
||||
memcpy(eaeskey, aeskey_old, 16);
|
||||
sha_ctx_t *ctx = sha_init();
|
||||
sha_update(ctx, eaeskey, 16);
|
||||
sha_update(ctx, ecdh_secret, 32);
|
||||
sha_final(ctx, eaeskey, NULL);
|
||||
sha_destroy(ctx);
|
||||
memcpy(aeskey, eaeskey, 16);
|
||||
|
||||
str = utils_data_to_string(aeskey, 16, 16);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey after sha-256 hash with ecdh_secret:\n%s", str);
|
||||
free(str);
|
||||
|
||||
/* old-protocol clients such as AirMyPC use the unhashed key aeskey_old for both audio and video (ios9, ios10 support)*/
|
||||
/* clients with sourceVersion <= OLD_PROTOCOL_AUDIO_CLIENT_SOURCEVERSION use unhashed aeskey_old for audio decryption */
|
||||
/* this is also done for audio or video if clients User-Agent string is included in OLD_PROTOCOL_[AUDIO,VIDEO]_CLIENT_LIST (AirMyPC support)*/
|
||||
/* OLD_PROTOCOL_AUDIO_CLIENT_LIST, OLD_PROTOCOL_VIDEO_CLIENT_LIST, OLD_PROTOCOL_AUDIO_CLIENT_SOURCEVERSION are defined in global.h */
|
||||
|
||||
plist_t req_source_version_node = plist_dict_get_item(req_root_node, "sourceVersion");
|
||||
char *sourceVersion;
|
||||
plist_get_string_val(req_source_version_node, &sourceVersion);
|
||||
const char *user_agent = http_request_get_header(request, "User-Agent");
|
||||
logger_log(conn->raop->logger, LOGGER_INFO, "Client identified as User-Agent: %s, sourceVersion: %s", user_agent, sourceVersion);
|
||||
|
||||
#ifdef OLD_PROTOCOL_AUDIO_CLIENT_SOURCEVERSION
|
||||
char *end_ptr;
|
||||
if (strtoul(sourceVersion, &end_ptr, 10) <= strtoul(OLD_PROTOCOL_AUDIO_CLIENT_SOURCEVERSION , &end_ptr, 10)) aeskey_audio = aeskey_old;
|
||||
#endif
|
||||
|
||||
#ifdef OLD_PROTOCOL_AUDIO_CLIENT_USER_AGENT_LIST
|
||||
if (strstr(OLD_PROTOCOL_AUDIO_CLIENT_USER_AGENT_LIST, user_agent)) aeskey_audio = aeskey_old;
|
||||
#endif
|
||||
|
||||
#ifdef OLD_PROTOCOL_VIDEO_CLIENT_USER_AGENT_LIST
|
||||
if (strstr(OLD_PROTOCOL_AUDIO_CLIENT_USER_AGENT_LIST, user_agent)) aeskey_video = aeskey_old;
|
||||
#endif
|
||||
|
||||
if (aeskey_audio == aeskey_old) {
|
||||
logger_log(conn->raop->logger, LOGGER_INFO, "Client identifed as using old protocol (unhashed) AES audio key)");
|
||||
}
|
||||
|
||||
if (aeskey_video == aeskey_old) {
|
||||
logger_log(conn->raop->logger, LOGGER_INFO, "Client identifed as using old protocol (unhashed) AES video key)");
|
||||
}
|
||||
|
||||
// Time port
|
||||
uint64_t timing_rport;
|
||||
@@ -392,8 +444,8 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
conn->raop_ntp = raop_ntp_init(conn->raop->logger, conn->remote, conn->remotelen, timing_rport);
|
||||
raop_ntp_start(conn->raop_ntp, &timing_lport);
|
||||
|
||||
conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->remote, conn->remotelen, aeskey, aesiv, ecdh_secret);
|
||||
conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->remote, conn->remotelen, aeskey, ecdh_secret);
|
||||
conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->remote, conn->remotelen, aeskey_audio, aesiv);
|
||||
conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->remote, conn->remotelen, aeskey_video);
|
||||
|
||||
plist_t res_event_port_node = plist_new_uint(conn->raop->port);
|
||||
plist_t res_timing_port_node = plist_new_uint(timing_lport);
|
||||
@@ -404,6 +456,7 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
}
|
||||
|
||||
// Process stream setup requests
|
||||
plist_t req_streams_node = plist_dict_get_item(req_root_node, "streams");
|
||||
if (PLIST_IS_ARRAY(req_streams_node)) {
|
||||
plist_t res_streams_node = plist_new_array();
|
||||
|
||||
@@ -422,7 +475,7 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID");
|
||||
uint64_t stream_connection_id;
|
||||
plist_get_uint_val(stream_id_node, &stream_connection_id);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID = %llu", stream_connection_id);
|
||||
logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID (needed for AES-CTR video decryption iv): %llu", stream_connection_id);
|
||||
|
||||
if (conn->raop_rtp_mirror) {
|
||||
raop_rtp_init_mirror_aes(conn->raop_rtp_mirror, stream_connection_id);
|
||||
|
||||
@@ -132,8 +132,8 @@ raop_rtp_parse_remote(raop_rtp_t *raop_rtp, const unsigned char *remote, int rem
|
||||
}
|
||||
|
||||
raop_rtp_t *
|
||||
raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const unsigned char *remote, int remotelen,
|
||||
const unsigned char *aeskey, const unsigned char *aesiv, const unsigned char *ecdh_secret)
|
||||
raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const unsigned char *remote,
|
||||
int remotelen, const unsigned char *aeskey, const unsigned char *aesiv)
|
||||
{
|
||||
raop_rtp_t *raop_rtp;
|
||||
|
||||
@@ -156,7 +156,7 @@ raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, co
|
||||
}
|
||||
|
||||
memcpy(&raop_rtp->callbacks, callbacks, sizeof(raop_callbacks_t));
|
||||
raop_rtp->buffer = raop_buffer_init(logger, aeskey, aesiv, ecdh_secret);
|
||||
raop_rtp->buffer = raop_buffer_init(logger, aeskey, aesiv);
|
||||
if (!raop_rtp->buffer) {
|
||||
free(raop_rtp);
|
||||
return NULL;
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
|
||||
typedef struct raop_rtp_s raop_rtp_t;
|
||||
|
||||
raop_rtp_t *raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const unsigned char *remote, int remotelen,
|
||||
const unsigned char *aeskey, const unsigned char *aesiv, const unsigned char *ecdh_secret);
|
||||
raop_rtp_t *raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, const unsigned char *remote,
|
||||
int remotelen, const unsigned char *aeskey, const unsigned char *aesiv);
|
||||
|
||||
void raop_rtp_start_audio(raop_rtp_t *raop_rtp, int use_udp, unsigned short control_rport,
|
||||
unsigned short *control_lport, unsigned short *data_lport);
|
||||
|
||||
@@ -109,8 +109,7 @@ raop_rtp_parse_remote(raop_rtp_mirror_t *raop_rtp_mirror, const unsigned char *r
|
||||
|
||||
#define NO_FLUSH (-42)
|
||||
raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp,
|
||||
const unsigned char *remote, int remotelen,
|
||||
const unsigned char *aeskey, const unsigned char *ecdh_secret)
|
||||
const unsigned char *remote, int remotelen, const unsigned char *aeskey)
|
||||
{
|
||||
raop_rtp_mirror_t *raop_rtp_mirror;
|
||||
|
||||
@@ -125,7 +124,7 @@ raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *call
|
||||
raop_rtp_mirror->ntp = ntp;
|
||||
|
||||
memcpy(&raop_rtp_mirror->callbacks, callbacks, sizeof(raop_callbacks_t));
|
||||
raop_rtp_mirror->buffer = mirror_buffer_init(logger, aeskey, ecdh_secret);
|
||||
raop_rtp_mirror->buffer = mirror_buffer_init(logger, aeskey);
|
||||
if (!raop_rtp_mirror->buffer) {
|
||||
free(raop_rtp_mirror);
|
||||
return NULL;
|
||||
|
||||
@@ -24,8 +24,7 @@ typedef struct raop_rtp_mirror_s raop_rtp_mirror_t;
|
||||
typedef struct h264codec_s h264codec_t;
|
||||
|
||||
raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp,
|
||||
const unsigned char *remote, int remotelen,
|
||||
const unsigned char *aeskey, const unsigned char *ecdh_secret);
|
||||
const unsigned char *remote, int remotelen, const unsigned char *aeskey);
|
||||
void raop_rtp_init_mirror_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t streamConnectionID);
|
||||
void raop_rtp_start_mirror(raop_rtp_mirror_t *raop_rtp_mirror, int use_udp, unsigned short *mirror_data_lport);
|
||||
void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror);
|
||||
|
||||
34
lib/utils.c
34
lib/utils.c
@@ -178,4 +178,36 @@ char *utils_parse_hex(const char *str, int str_len, int *data_len) {
|
||||
|
||||
*data_len = (str_len / 2);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
char *utils_data_to_string(const unsigned char *data, int datalen, int chars_per_line) {
|
||||
int len = 3*datalen + ((datalen-1)/chars_per_line ) + 1;
|
||||
char *str = (char *) calloc(len + 1, sizeof(char));
|
||||
assert(str);
|
||||
char *p = str;
|
||||
for (int i = 0; i < datalen; i++) {
|
||||
if (i > 0 && i % chars_per_line == 0) {
|
||||
sprintf(p,"\n");
|
||||
p++;
|
||||
}
|
||||
sprintf(p,"%2.2x ", (unsigned int) data[i]);
|
||||
p += 3;
|
||||
}
|
||||
sprintf(p,"\n");
|
||||
p++;
|
||||
assert(p == &(str[len]));
|
||||
return str;
|
||||
}
|
||||
|
||||
char *utils_data_to_text(const char *data, int datalen) {
|
||||
char *ptr = (char *) calloc(datalen + 1, sizeof(char));
|
||||
assert(ptr);
|
||||
strncpy(ptr, data, datalen);
|
||||
char *p = ptr;
|
||||
while (p) {
|
||||
p = strchr(p, '\r'); /* replace occurences of '\r' by ' ' */
|
||||
if (p) *p = ' ';
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,4 +20,6 @@ int utils_read_file(char **dst, const char *pemstr);
|
||||
int utils_hwaddr_raop(char *str, int strlen, const char *hwaddr, int hwaddrlen);
|
||||
int utils_hwaddr_airplay(char *str, int strlen, const char *hwaddr, int hwaddrlen);
|
||||
char *utils_parse_hex(const char *str, int str_len, int *data_len);
|
||||
#endif
|
||||
char *utils_data_to_string(const unsigned char *data, int datalen, int chars_per_line);
|
||||
char *utils_data_to_text(const char *data, int datalen);
|
||||
#endif
|
||||
|
||||
@@ -193,6 +193,7 @@ void audio_renderer_render_buffer(raop_ntp_t *ntp, unsigned char* data, int data
|
||||
switch (renderer->ct) {
|
||||
case 8: /*AAC-ELD*/
|
||||
valid = (data[0] == 0x8d || data[0] == 0x8e);
|
||||
valid = valid || (data[0] == 0x81 || data[0] == 0x82); /* old protocol iOS 8, iOS 9 */
|
||||
break;
|
||||
case 2: /*ALAC*/
|
||||
valid = (data[0] == 0x20);
|
||||
@@ -209,10 +210,14 @@ void audio_renderer_render_buffer(raop_ntp_t *ntp, unsigned char* data, int data
|
||||
if (counter == 2) broken_audio = false;
|
||||
if (!broken_audio) gst_app_src_push_buffer(GST_APP_SRC(renderer->appsrc), buffer);
|
||||
} else {
|
||||
if (!broken_audio) logger_log(logger, LOGGER_ERR, "*** ERROR decryption of audio (compression_type %d) failed ", renderer->ct);
|
||||
if (!broken_audio) {
|
||||
logger_log(logger, LOGGER_ERR, "*** ERROR decryption of audio frame (compression_type %d) failed ", renderer->ct);
|
||||
} else {
|
||||
logger_log(logger, LOGGER_DEBUG, "*** ERROR decryption of audio frame (compression_type %d) failed ", renderer->ct);
|
||||
}
|
||||
broken_audio = true;
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_renderer_set_volume(float volume) {
|
||||
|
||||
@@ -113,7 +113,7 @@ void video_renderer_init(logger_t *render_logger, const char *server_name, vide
|
||||
gst_init(NULL,NULL);
|
||||
|
||||
GString *launch = g_string_new("appsrc name=video_source stream-type=0 format=GST_FORMAT_TIME is-live=true !"
|
||||
"queue ! decodebin ! videoconvert ! ");
|
||||
"queue ! h264parse ! avdec_h264 ! videoconvert ! ");
|
||||
append_videoflip(launch, &videoflip[0], &videoflip[1]);
|
||||
g_string_append(launch, videosink);
|
||||
g_string_append(launch, " name=video_sink sync=false");
|
||||
@@ -141,6 +141,17 @@ void video_renderer_init(logger_t *render_logger, const char *server_name, vide
|
||||
get_X11_Display(renderer->gst_window);
|
||||
}
|
||||
#endif
|
||||
gst_element_set_state (renderer->pipeline, GST_STATE_READY);
|
||||
GstState state;
|
||||
if (gst_element_get_state (renderer->pipeline, &state, NULL, 0)) {
|
||||
if (state == GST_STATE_READY) {
|
||||
logger_log(logger, LOGGER_DEBUG, "Initialized GStreamer video renderer");
|
||||
} else {
|
||||
logger_log(logger, LOGGER_ERR, "Failed to initialize GStreamer video renderer");
|
||||
}
|
||||
} else {
|
||||
logger_log(logger, LOGGER_ERR, "Failed to initialize GStreamer video renderer");
|
||||
}
|
||||
}
|
||||
|
||||
void video_renderer_start() {
|
||||
@@ -155,7 +166,11 @@ void video_renderer_render_buffer(raop_ntp_t *ntp, unsigned char* data, int data
|
||||
/* first four bytes of valid video data are 0x0, 0x0, 0x0, 0x1 */
|
||||
/* first byte of invalid data (decryption failed) is 0x1 */
|
||||
if (data[0]) {
|
||||
if (!broken_video) logger_log(logger, LOGGER_ERR, "*** ERROR decryption of video failed ");
|
||||
if (!broken_video) {
|
||||
logger_log(logger, LOGGER_ERR, "*** ERROR decryption of video packet failed ");
|
||||
} else {
|
||||
logger_log(logger, LOGGER_DEBUG, "*** ERROR decryption of video packet failed ");
|
||||
}
|
||||
broken_video = true;
|
||||
} else {
|
||||
broken_video = false;
|
||||
|
||||
4
uxplay.1
4
uxplay.1
@@ -1,11 +1,11 @@
|
||||
.TH UXPLAY "1" "December 2021" "1.43" "User Commands"
|
||||
.TH UXPLAY "1" "December 2021" "1.44" "User Commands"
|
||||
.SH NAME
|
||||
uxplay \- start AirPlay server
|
||||
.SH SYNOPSIS
|
||||
.B uxplay
|
||||
[\fI\,-n name\/\fR] [\fI\,-s wxh\/\fR] [\fI\,-p \/\fR[\fI\,n\/\fR]] [more \fI OPTIONS \/\fR ...]
|
||||
.SH DESCRIPTION
|
||||
UxPlay 1.43: An open\-source AirPlay mirroring server based on RPiPlay
|
||||
UxPlay 1.44: An open\-source AirPlay mirroring server based on RPiPlay
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B
|
||||
|
||||
14
uxplay.cpp
14
uxplay.cpp
@@ -36,10 +36,6 @@
|
||||
#include <net/if_dl.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "log.h"
|
||||
#include "lib/raop.h"
|
||||
#include "lib/stream.h"
|
||||
@@ -48,7 +44,7 @@
|
||||
#include "renderers/video_renderer.h"
|
||||
#include "renderers/audio_renderer.h"
|
||||
|
||||
#define VERSION "1.43"
|
||||
#define VERSION "1.44"
|
||||
|
||||
#define DEFAULT_NAME "UxPlay"
|
||||
#define DEFAULT_DEBUG_LOG false
|
||||
@@ -74,13 +70,12 @@ static unsigned char compression_type = 0;
|
||||
static std::string audiosink = "autoaudiosink";
|
||||
static bool use_audio = true;
|
||||
|
||||
|
||||
gboolean connection_callback (gpointer loop){
|
||||
if (!connections_stopped) {
|
||||
counter = 0;
|
||||
} else {
|
||||
if (++counter == server_timeout) {
|
||||
LOGI("no connections for %d seconds: relaunch server\n",server_timeout);
|
||||
LOGD("no connections for %d seconds: relaunch server",server_timeout);
|
||||
g_main_loop_quit((GMainLoop *) loop);
|
||||
}
|
||||
}
|
||||
@@ -478,10 +473,11 @@ int main (int argc, char *argv[]) {
|
||||
}
|
||||
parse_hw_addr(mac_address, server_hw_addr);
|
||||
mac_address.clear();
|
||||
|
||||
|
||||
connections_stopped = true;
|
||||
relaunch:
|
||||
counter = 0;
|
||||
compression_type = 0;
|
||||
connections_stopped = false;
|
||||
if (start_raop_server(server_hw_addr, server_name, display, tcp, udp, debug_log)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user