From 3a1a244b0f395b55698fb70f754f64138a460590 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Mon, 2 May 2022 15:28:35 -0400 Subject: [PATCH 01/30] cleanups of debug text and initial audio syc --- lib/raop_buffer.c | 3 ++- lib/raop_rtp.c | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/lib/raop_buffer.c b/lib/raop_buffer.c index 4848b61..cdd199c 100644 --- a/lib/raop_buffer.c +++ b/lib/raop_buffer.c @@ -28,6 +28,7 @@ #include "stream.h" #include "global.h" #include "utils.h" +#include "byteutils.h" #define RAOP_BUFFER_LENGTH 32 @@ -221,7 +222,7 @@ raop_buffer_enqueue(raop_buffer_t *raop_buffer, unsigned char *data, unsigned sh /* Get correct seqnum for the packet */ unsigned short seqnum; if (use_seqnum) { - seqnum = (data[2] << 8) | data[3]; + seqnum = byteutils_get_short_be(data, 2); } else { seqnum = raop_buffer->first_seqnum; } diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index a9bb1ae..84c3e14 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -419,7 +419,6 @@ raop_rtp_thread_udp(void *arg) unsigned int packetlen; struct sockaddr_storage saddr; socklen_t saddrlen; - int64_t ntp_start_time = 0; bool have_synced = false; bool no_data = true; /* the 44.1 kHZ rtp_time epoch is about 27 hours */ @@ -430,6 +429,8 @@ raop_rtp_thread_udp(void *arg) int64_t half_epoch = (int64_t) epoch_length/2; uint32_t prev_rtp_timestamp = 0; assert(raop_rtp); + int64_t ntp_start_time = (int64_t) raop_ntp_get_local_time(raop_rtp->ntp); + logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp start_time = %8.6f (raop_rtp audio)", ((double) ntp_start_time) / SEC); while(1) { fd_set rfds; struct timeval tv; @@ -480,7 +481,7 @@ raop_rtp_thread_udp(void *arg) int result = raop_buffer_enqueue(raop_rtp->buffer, packet + offset, packetlen - offset, rtp_timestamp, 1); assert(result >= 0); } else if (type_c == 0x54 && packetlen >= 20) { - /* packet[0] = 0x90 (first sync) or 0x80 (subsequent ones) * + /* packet[0] = 0x90 (first sync ?) or 0x80 (subsequent ones) * * packet[1:3] = 0xd4, 0xd4 && ~0x80 = type 54 * * packet[2:3] = 0x00 0x04 * * packet[4:7] : sync_rtp big-endian uint32_t) * @@ -491,7 +492,10 @@ raop_rtp_thread_udp(void *arg) // The unit for the rtp clock is 1 / sample rate = 1 / 44100 uint32_t sync_rtp = byteutils_get_int_be(packet, 4); - have_synced = true; + if (have_synced == false) { + logger_log(raop_rtp->logger, LOGGER_DEBUG, "first audio rtp sync"); + have_synced = true; + } uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8); uint64_t sync_ntp_remote = raop_ntp_timestamp_to_micro_seconds(sync_ntp_raw, true); uint64_t sync_ntp_local = raop_ntp_convert_remote_time(raop_rtp->ntp, sync_ntp_remote); @@ -506,8 +510,8 @@ raop_rtp_thread_udp(void *arg) raop_rtp_sync_clock(raop_rtp, sync_ntp_local, ntp_start_time, sync_rtp, shift); char *str = utils_data_to_string(packet, packetlen, 20); logger_log(raop_rtp->logger, LOGGER_DEBUG, - "raop_rtp sync: client ntp=%llu, local ntp: %llu, local ntp_start_time %lld, sync_rtp=%u\n%s", - sync_ntp_remote, sync_ntp_local, ntp_start_time, sync_rtp, str); + "raop_rtp sync: client ntp=%8.6f, ntp = %8.6f, ntp_start_time %8.6f, sync_rtp=%u\n%s", + ((double) sync_ntp_remote) / SEC, ((double)sync_ntp_local) / SEC, ((double)ntp_start_time) / SEC, sync_rtp, str); free(str); } else { logger_log(raop_rtp->logger, LOGGER_DEBUG, "raop_rtp unknown packet"); @@ -551,21 +555,34 @@ raop_rtp_thread_udp(void *arg) } else { if (no_data) { no_data = false; - logger_log(raop_rtp->logger, LOGGER_DEBUG, "First audio packet received"); if (have_synced == false) { - /* until the first rpt sync occurs, we don't know the exact client ntp timestamp that matches the client rtp timesamp */ - int64_t ntp_time = ((uint64_t) raop_ntp_get_local_time(raop_rtp->ntp)) - ntp_start_time; + /* until the first rtp sync occurs, we don't know the exact client ntp timestamp that matches the client rtp timesamp */ + int64_t ntp_now = (int64_t) raop_ntp_get_local_time(raop_rtp->ntp); + int64_t ntp_time = ((int64_t) ntp_now) - ntp_start_time; + int64_t latency = 0; switch (raop_rtp->ct) { case 0x02: - ntp_time += DELAY_ALAC; /* DELAY = 2000000 (2.0 sec) is empirical choice for ALAC */ + latency = -DELAY_ALAC; /* DELAY = 2000000 (2.0 sec) is empirical choice for ALAC */ break; case 0x08: - ntp_time += DELAY_AAC; /* DELAY = 500000 (0.5 sec) is empirical choice for AAC-ELD */ + latency = -DELAY_AAC; /* DELAY = 500000 (0.5 sec) is empirical choice for AAC-ELD */ break; default: break; } - raop_rtp->rtp_sync_offset = ((int64_t) (((double) rtp_timestamp) / raop_rtp->rtp_sync_scale)) - ntp_time; + ntp_time -= latency; + logger_log(raop_rtp->logger, LOGGER_DEBUG, "First audio packet received, have_synced = false, using assumed latency %8.6f", + ((double)latency) / SEC); + raop_rtp->rtp_sync_offset = (int64_t) ((double) rtp_timestamp) / raop_rtp->rtp_sync_scale; + raop_rtp->rtp_sync_offset -= ntp_time; + int64_t ntp_timestamp = ntp_start_time - raop_rtp->rtp_sync_offset; + ntp_timestamp += (int64_t) ((double) rtp_timestamp) / raop_rtp->rtp_sync_scale; + latency = ntp_now - ntp_timestamp; + unsigned short seqnum = byteutils_get_short_be(packet, 2); + logger_log(raop_rtp->logger, LOGGER_DEBUG, "initial audio: now = %8.6f, npt = %8.6f, latency = %8.6f, rtp_time=%u seqnum = %u (not from sync)", + ((double) ntp_now ) / SEC, ((double) ntp_timestamp) / SEC, ((double) latency) / SEC, rtp_timestamp, seqnum); + } else { + logger_log(raop_rtp->logger, LOGGER_DEBUG, "First audio packet received, have_synced = true"); } } int result = raop_buffer_enqueue(raop_rtp->buffer, packet, packetlen, rtp_timestamp, 1); From d67cecbb45c16745a2d61f4389dc38cea814263f Mon Sep 17 00:00:00 2001 From: fduncanh Date: Mon, 2 May 2022 15:33:21 -0400 Subject: [PATCH 02/30] whitespace cleanup --- lib/raop_rtp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index 84c3e14..2ee8911 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -492,7 +492,7 @@ raop_rtp_thread_udp(void *arg) // The unit for the rtp clock is 1 / sample rate = 1 / 44100 uint32_t sync_rtp = byteutils_get_int_be(packet, 4); - if (have_synced == false) { + if (have_synced == false) { logger_log(raop_rtp->logger, LOGGER_DEBUG, "first audio rtp sync"); have_synced = true; } @@ -556,7 +556,7 @@ raop_rtp_thread_udp(void *arg) if (no_data) { no_data = false; if (have_synced == false) { - /* until the first rtp sync occurs, we don't know the exact client ntp timestamp that matches the client rtp timesamp */ + /* until the first rtp sync occurs, we don't know the exact client ntp timestamp that matches the client rtp timesamp */ int64_t ntp_now = (int64_t) raop_ntp_get_local_time(raop_rtp->ntp); int64_t ntp_time = ((int64_t) ntp_now) - ntp_start_time; int64_t latency = 0; @@ -570,9 +570,9 @@ raop_rtp_thread_udp(void *arg) default: break; } - ntp_time -= latency; + ntp_time -= latency; logger_log(raop_rtp->logger, LOGGER_DEBUG, "First audio packet received, have_synced = false, using assumed latency %8.6f", - ((double)latency) / SEC); + ((double)latency) / SEC); raop_rtp->rtp_sync_offset = (int64_t) ((double) rtp_timestamp) / raop_rtp->rtp_sync_scale; raop_rtp->rtp_sync_offset -= ntp_time; int64_t ntp_timestamp = ntp_start_time - raop_rtp->rtp_sync_offset; @@ -580,7 +580,7 @@ raop_rtp_thread_udp(void *arg) latency = ntp_now - ntp_timestamp; unsigned short seqnum = byteutils_get_short_be(packet, 2); logger_log(raop_rtp->logger, LOGGER_DEBUG, "initial audio: now = %8.6f, npt = %8.6f, latency = %8.6f, rtp_time=%u seqnum = %u (not from sync)", - ((double) ntp_now ) / SEC, ((double) ntp_timestamp) / SEC, ((double) latency) / SEC, rtp_timestamp, seqnum); + ((double) ntp_now ) / SEC, ((double) ntp_timestamp) / SEC, ((double) latency) / SEC, rtp_timestamp, seqnum); } else { logger_log(raop_rtp->logger, LOGGER_DEBUG, "First audio packet received, have_synced = true"); } From 83aee1fd077355f22af2d735661cb81506c18c9b Mon Sep 17 00:00:00 2001 From: fduncanh Date: Mon, 2 May 2022 22:12:05 -0400 Subject: [PATCH 03/30] cosmetic changes --- README.html | 3 ++- README.md | 5 ++++- README.txt | 6 +++++- lib/raop_rtp.c | 5 +++-- renderers/video_renderer_gstreamer.c | 11 ++++++++++- uxplay.1 | 4 ++-- uxplay.cpp | 2 +- 7 files changed, 27 insertions(+), 9 deletions(-) diff --git a/README.html b/README.html index 0e1034c..0b0dd7e 100644 --- a/README.html +++ b/README.html @@ -1,4 +1,4 @@ -

UxPlay 1.51: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.

+

UxPlay 1.52: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.

Now developed at GitHub site https://github.com/FDH2/UxPlay (where user issues should be posted).

Highlights:

    @@ -139,6 +139,7 @@

    This triggers an unending stream of error messages, and means that the audio decryption key (also used in video decryption) was not correctly extracted from data sent by the client. This should not happen for iOS 9.3 or later clients. However, if a client uses the same older version of the protocol that is used by the Windows-based AirPlay client emulator AirMyPC, the protocol can be switched to the older version by the setting OLD_PROTOCOL_CLIENT_USER_AGENT_LIST in lib/global.h. UxPlay reports the client’s “User Agent” string when it connects. If some other client also fails to decrypt all audio and video, try adding its “User Agent” string in place of “xxx” in the entry “AirMyPC/2.0;xxx” in global.h and rebuild uxplay.

    Note that Uxplay declares itself to be an AppleTV3,2 with a sourceVersion 220.68; this can also be changed in global.h. It had been thought that it was necessary for UxPlay to claim to be an older 32 bit AppleTV model that cannot run modern 64bit tvOS, in order for the client to use a “legacy” protocol for pairing with the server (see the “Notes on AirPlay protocol versions” at the end of this README). However, UxPlay still works if it declares itself as an AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen, introduced 2017, running tvOS 12.2.1), so it is unclear what setting prompts the client to use the “legacy” protocol needed by UxPlay.

    ChangeLog

    +

    1.52 2022-05-02 Cleaned up initial audio sync code, and reformatted streaming debug output (readable aligned timestamps with decimal points in seconds).

    1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option -v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2, -v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from 1.48 that broke reconnection after “Stop Mirroring” is sent by client.

    1.50 2022-04-22 Added -fs fullscreen option (for Wayland or VAAPI plugins only), Changed -rpi to be for framebuffer (“lite”) RPi systems and added -rpigl (OpenGL) and -rpiwl (Wayland) options for RPi Desktop systems. Also modified timestamps from “DTS” to “PTS” for latency improvement, plus internal cleanups.

    1.49 2022-03-28 Addded options for dumping video and/or audio to file, for debugging, etc. h264 PPS/SPS NALU’s are shown with -d. Fixed video-not-working for M1 Mac clients.

    diff --git a/README.md b/README.md index 504f692..ea52980 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# UxPlay 1.51: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix. +# UxPlay 1.52: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix. ### Now developed at GitHub site [https://github.com/FDH2/UxPlay](https://github.com/FDH2/UxPlay) (where user issues should be posted). @@ -595,6 +595,9 @@ tvOS 12.2.1), so it is unclear what setting prompts the client to use the "legacy" protocol needed by UxPlay. # ChangeLog +1.52 2022-05-02 Cleaned up initial audio sync code, and reformatted streaming debug output (readable aligned timestamps with + decimal points in seconds). + 1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option -v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2, -v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from 1.48 that broke reconnection after "Stop Mirroring" is sent by client. diff --git a/README.txt b/README.txt index 41bf19b..8800c52 100644 --- a/README.txt +++ b/README.txt @@ -1,4 +1,4 @@ -UxPlay 1.51: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix. +UxPlay 1.52: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix. ====================================================================== ### Now developed at GitHub site (where user issues should be posted). @@ -777,6 +777,10 @@ the "legacy" protocol needed by UxPlay. ChangeLog ========= +1.52 2022-05-02 Cleaned up initial audio sync code, and reformatted +streaming debug output (readable aligned timestamps with decimal points +in seconds). + 1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option -v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2, -v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index 2ee8911..3ba7f78 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -579,8 +579,9 @@ raop_rtp_thread_udp(void *arg) ntp_timestamp += (int64_t) ((double) rtp_timestamp) / raop_rtp->rtp_sync_scale; latency = ntp_now - ntp_timestamp; unsigned short seqnum = byteutils_get_short_be(packet, 2); - logger_log(raop_rtp->logger, LOGGER_DEBUG, "initial audio: now = %8.6f, npt = %8.6f, latency = %8.6f, rtp_time=%u seqnum = %u (not from sync)", - ((double) ntp_now ) / SEC, ((double) ntp_timestamp) / SEC, ((double) latency) / SEC, rtp_timestamp, seqnum); + logger_log(raop_rtp->logger, LOGGER_DEBUG, + "initial audio: now = %8.6f, npt = %8.6f, latency = %8.6f, rtp_time=%u seqnum = %u (not from sync)", + ((double) ntp_now ) / SEC, ((double) ntp_timestamp) / SEC, ((double) latency) / SEC, rtp_timestamp, seqnum); } else { logger_log(raop_rtp->logger, LOGGER_DEBUG, "First audio packet received, have_synced = true"); } diff --git a/renderers/video_renderer_gstreamer.c b/renderers/video_renderer_gstreamer.c index 462b876..b581d8e 100644 --- a/renderers/video_renderer_gstreamer.c +++ b/renderers/video_renderer_gstreamer.c @@ -264,7 +264,16 @@ gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, gpoin gboolean flushing; gst_message_parse_error (message, &err, &debug); logger_log(logger, LOGGER_INFO, "GStreamer error: %s", err->message); - g_error_free (err); + if (strstr(err->message,"Internal data stream error")) { + logger_log(logger, LOGGER_INFO, + "*** This is a generic GStreamer error that usually means that GStreamer\n" + "*** was unable to construct a working video pipeline. If you are using\n" + "*** the default autovideosink for automated selection of the videosink,\n " + "*** GStreamer may be trying to use non-functional hardware h264 video decoding.\n" + "*** Try using option -avdec to force software decoding or use -vs \n" + "*** to select a videosink of your choice (see \"man uxplay\")"); + } + g_error_free (err); g_free (debug); gst_app_src_end_of_stream (GST_APP_SRC(renderer->appsrc)); flushing = TRUE; diff --git a/uxplay.1 b/uxplay.1 index 48b4f8e..7006a94 100644 --- a/uxplay.1 +++ b/uxplay.1 @@ -1,11 +1,11 @@ -.TH UXPLAY "1" "April 2022" "1.51" "User Commands" +\41;330;0c.TH UXPLAY "1" "May 2022" "1.52" "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.51: An open\-source AirPlay mirroring server based on RPiPlay +UxPlay 1.52: An open\-source AirPlay mirroring server based on RPiPlay .SH OPTIONS .TP .B diff --git a/uxplay.cpp b/uxplay.cpp index 7f9b022..037cb81 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -44,7 +44,7 @@ #include "renderers/video_renderer.h" #include "renderers/audio_renderer.h" -#define VERSION "1.51" +#define VERSION "1.52" #define DEFAULT_NAME "UxPlay" #define DEFAULT_DEBUG_LOG false From b3469df73d6c492b4d9b05e478ad719556074a7d Mon Sep 17 00:00:00 2001 From: fduncanh Date: Tue, 3 May 2022 02:07:25 -0400 Subject: [PATCH 04/30] fix a corrupted first line from last commit --- uxplay.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uxplay.1 b/uxplay.1 index 7006a94..f1a43ce 100644 --- a/uxplay.1 +++ b/uxplay.1 @@ -1,4 +1,4 @@ -\41;330;0c.TH UXPLAY "1" "May 2022" "1.52" "User Commands" +.TH UXPLAY "1" "May 2022" "1.52" "User Commands" .SH NAME uxplay \- start AirPlay server .SH SYNOPSIS From c5c58bc116da9c9ffd2f490470c771154314edeb Mon Sep 17 00:00:00 2001 From: fduncanh Date: Tue, 3 May 2022 02:15:32 -0400 Subject: [PATCH 05/30] fix broken -rpi option (broken by typo) --- uxplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uxplay.cpp b/uxplay.cpp index 037cb81..d5b2b61 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -585,7 +585,7 @@ int main (int argc, char *argv[]) { video_decoder = "avdec_h264"; video_converter.erase(); video_converter = "videoconvert"; - } else if (arg == "-v4l2" || arg == "rpi") { + } else if (arg == "-v4l2" || arg == "-rpi") { video_decoder.erase(); video_decoder = "v4l2h264dec"; video_converter.erase(); From e6ffc828d2d1746f737308d5956b748dc73d2678 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Tue, 3 May 2022 16:29:08 -0400 Subject: [PATCH 06/30] place more complete timing data in audio_decode_struct --- lib/raop_rtp.c | 4 +++- lib/stream.h | 5 ++++- uxplay.cpp | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index 3ba7f78..f618ac9 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -613,7 +613,9 @@ raop_rtp_thread_udp(void *arg) audio_decode_struct audio_data; audio_data.data_len = payload_size; audio_data.data = payload; - audio_data.pts = (uint64_t) ntp_timestamp; + audio_data.ntp_time = (uint64_t) ntp_timestamp; + audio_data.rtp_time = (uint64_t) rtp_time; + audio_data.have_synced = have_synced; raop_rtp->callbacks.audio_process(raop_rtp->callbacks.cls, raop_rtp->ntp, &audio_data); free(payload); uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp->ntp); diff --git a/lib/stream.h b/lib/stream.h index fdc20ac..1f56774 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -16,6 +16,7 @@ #define AIRPLAYSERVER_STREAM_H #include +#include typedef struct { int nal_count; @@ -27,7 +28,9 @@ typedef struct { typedef struct { unsigned char *data; int data_len; - uint64_t pts; + uint64_t ntp_time; + uint64_t rtp_time; + bool have_synced; } audio_decode_struct; #endif //AIRPLAYSERVER_STREAM_H diff --git a/uxplay.cpp b/uxplay.cpp index d5b2b61..abc795f 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -811,7 +811,7 @@ extern "C" void audio_process (void *cls, raop_ntp_t *ntp, audio_decode_struct * dump_audio_to_file(data->data, data->data_len, (data->data)[0] & 0xf0); } if (use_audio) { - audio_renderer_render_buffer(ntp, data->data, data->data_len, data->pts); + audio_renderer_render_buffer(ntp, data->data, data->data_len, data->ntp_time); } } From 8fd59814be7f909b2363ecdfd516ceeff67dca2a Mon Sep 17 00:00:00 2001 From: fduncanh Date: Tue, 3 May 2022 16:35:32 -0400 Subject: [PATCH 07/30] whitespace --- lib/raop_rtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index f618ac9..ebacc13 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -615,7 +615,7 @@ raop_rtp_thread_udp(void *arg) audio_data.data = payload; audio_data.ntp_time = (uint64_t) ntp_timestamp; audio_data.rtp_time = (uint64_t) rtp_time; - audio_data.have_synced = have_synced; + audio_data.have_synced = have_synced; raop_rtp->callbacks.audio_process(raop_rtp->callbacks.cls, raop_rtp->ntp, &audio_data); free(payload); uint64_t ntp_now = raop_ntp_get_local_time(raop_rtp->ntp); From 145070779493399b3bed74c273e3268016e92998 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Tue, 3 May 2022 16:51:02 -0400 Subject: [PATCH 08/30] make more audio time details available to renderer --- renderers/audio_renderer.h | 4 +++- renderers/audio_renderer_gstreamer.c | 5 +++-- uxplay.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/renderers/audio_renderer.h b/renderers/audio_renderer.h index 084c502..b879a16 100644 --- a/renderers/audio_renderer.h +++ b/renderers/audio_renderer.h @@ -26,12 +26,14 @@ extern "C" { #include #include +#include #include "../lib/raop_ntp.h" void audio_renderer_init(logger_t *logger, const char* audiosink); void audio_renderer_start(unsigned char* compression_type); void audio_renderer_stop(); -void audio_renderer_render_buffer(raop_ntp_t *ntp, unsigned char* data, int data_len, uint64_t pts); +void audio_renderer_render_buffer(raop_ntp_t *ntp, unsigned char* data, int data_len, + uint64_t ntp_time, uint64_t rtp_time, bool have_synced); void audio_renderer_set_volume(float volume); void audio_renderer_flush(); void audio_renderer_destroy(); diff --git a/renderers/audio_renderer_gstreamer.c b/renderers/audio_renderer_gstreamer.c index ab3f673..4522d1d 100644 --- a/renderers/audio_renderer_gstreamer.c +++ b/renderers/audio_renderer_gstreamer.c @@ -177,7 +177,8 @@ void audio_renderer_start(unsigned char *ct) { } -void audio_renderer_render_buffer(raop_ntp_t *ntp, unsigned char* data, int data_len, uint64_t pts) { +void audio_renderer_render_buffer(raop_ntp_t *ntp, unsigned char* data, int data_len, uint64_t ntp_time, + uint64_t rtp_time, bool rtp_and_ntp_have_synced) { GstBuffer *buffer; bool valid; if (data_len == 0 || renderer == NULL) return; @@ -191,7 +192,7 @@ void audio_renderer_render_buffer(raop_ntp_t *ntp, unsigned char* data, int data buffer = gst_buffer_new_and_alloc(data_len); assert(buffer != NULL); - GST_BUFFER_PTS(buffer) = (GstClockTime) pts; + GST_BUFFER_PTS(buffer) = (GstClockTime) ntp_time; gst_buffer_fill(buffer, 0, data, data_len); switch (renderer->ct){ case 8: /*AAC-ELD*/ diff --git a/uxplay.cpp b/uxplay.cpp index abc795f..480dd7c 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -811,7 +811,7 @@ extern "C" void audio_process (void *cls, raop_ntp_t *ntp, audio_decode_struct * dump_audio_to_file(data->data, data->data_len, (data->data)[0] & 0xf0); } if (use_audio) { - audio_renderer_render_buffer(ntp, data->data, data->data_len, data->ntp_time); + audio_renderer_render_buffer(ntp, data->data, data->data_len, data->ntp_time, data->rtp_time, data->have_synced); } } From 03507b4cb25e0a9b26131f2c789ae6bd6ee4994b Mon Sep 17 00:00:00 2001 From: fduncanh Date: Tue, 3 May 2022 19:51:39 -0400 Subject: [PATCH 09/30] fix small memory leak (ekey, eiv) in raop_handlers.h --- lib/raop_handlers.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h index 1f73404..45264c2 100644 --- a/lib/raop_handlers.h +++ b/lib/raop_handlers.h @@ -349,6 +349,7 @@ raop_handler_setup(raop_conn_t *conn, unsigned char aesiv[16]; unsigned char aeskey[16]; + unsigned char eaeskey[72]; logger_log(conn->raop->logger, LOGGER_DEBUG, "SETUP 1"); @@ -357,6 +358,7 @@ raop_handler_setup(raop_conn_t *conn, uint64_t eiv_len = 0; plist_get_data_val(req_eiv_node, &eiv, &eiv_len); memcpy(aesiv, eiv, 16); + free(eiv); 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); @@ -365,13 +367,15 @@ raop_handler_setup(raop_conn_t *conn, char* ekey = NULL; uint64_t ekey_len = 0; plist_get_data_val(req_ekey_node, &ekey, &ekey_len); + memcpy(eaeskey,ekey,72); + free(ekey); logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len); - // ekey is 72 bytes, aeskey is 16 bytes - str = utils_data_to_string((unsigned char *) ekey, ekey_len, 16); + // eaeskey is 72 bytes, aeskey is 16 bytes + str = utils_data_to_string((unsigned char *) eaeskey, 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); + int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) eaeskey, aeskey); logger_log(conn->raop->logger, LOGGER_DEBUG, "fairplay_decrypt ret = %d", ret); str = utils_data_to_string(aeskey, 16, 16); logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey (fairplay-decrypted from ekey):\n%s", str); @@ -393,8 +397,7 @@ raop_handler_setup(raop_conn_t *conn, if (old_protocol) { /* some windows AirPlay-client emulators use old AirPlay 1 protocol with unhashed AES key */ logger_log(conn->raop->logger, LOGGER_INFO, "Client identifed as using old protocol (unhashed) AES audio key)"); } else { - unsigned char eaeskey[64] = {}; - memcpy(eaeskey, aeskey, 16); + memcpy(eaeskey, aeskey, 16); sha_ctx_t *ctx = sha_init(); sha_update(ctx, eaeskey, 16); sha_update(ctx, ecdh_secret, 32); From 8745a28ec6dc05a98eb3a21e8204dee9b50110db Mon Sep 17 00:00:00 2001 From: fduncanh Date: Tue, 3 May 2022 19:56:48 -0400 Subject: [PATCH 10/30] whitespace --- lib/raop_handlers.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h index 45264c2..c11b9b1 100644 --- a/lib/raop_handlers.h +++ b/lib/raop_handlers.h @@ -368,9 +368,9 @@ raop_handler_setup(raop_conn_t *conn, uint64_t ekey_len = 0; plist_get_data_val(req_ekey_node, &ekey, &ekey_len); memcpy(eaeskey,ekey,72); - free(ekey); + free(ekey); logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len); - // eaeskey is 72 bytes, aeskey is 16 bytes + // eaeskey is 72 bytes, aeskey is 16 bytes str = utils_data_to_string((unsigned char *) eaeskey, ekey_len, 16); logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey:\n%s", str); free (str); @@ -397,7 +397,7 @@ raop_handler_setup(raop_conn_t *conn, if (old_protocol) { /* some windows AirPlay-client emulators use old AirPlay 1 protocol with unhashed AES key */ logger_log(conn->raop->logger, LOGGER_INFO, "Client identifed as using old protocol (unhashed) AES audio key)"); } else { - memcpy(eaeskey, aeskey, 16); + memcpy(eaeskey, aeskey, 16); sha_ctx_t *ctx = sha_init(); sha_update(ctx, eaeskey, 16); sha_update(ctx, ecdh_secret, 32); From c25012161bef7234ede921448fdb5cbeddf791a8 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Tue, 3 May 2022 23:39:10 -0400 Subject: [PATCH 11/30] fix some plist not-freed memory leaks --- lib/raop.c | 1 + lib/raop_handlers.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/lib/raop.c b/lib/raop.c index 3a672b0..de573cc 100644 --- a/lib/raop.c +++ b/lib/raop.c @@ -334,6 +334,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { char * plist_xml; uint32_t plist_len; plist_to_xml(res_root_node, &plist_xml, &plist_len); + plist_free(res_root_node); logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml); free(plist_xml); } else if (data_is_text) { diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h index c11b9b1..cd5ff26 100644 --- a/lib/raop_handlers.h +++ b/lib/raop_handlers.h @@ -548,6 +548,8 @@ raop_handler_setup(raop_conn_t *conn, } plist_to_bin(res_root_node, response_data, (uint32_t*) response_datalen); + plist_free(res_root_node); + plist_free(req_root_node); http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist"); } From 6bf2056f267e0d987d88bc2245e6a811e6442120 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Tue, 3 May 2022 23:54:21 -0400 Subject: [PATCH 12/30] fix yet more plist not-freed memory leaks --- lib/raop.c | 1 + lib/raop_handlers.h | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/raop.c b/lib/raop.c index de573cc..cae247e 100644 --- a/lib/raop.c +++ b/lib/raop.c @@ -186,6 +186,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { plist_to_xml(req_root_node, &plist_xml, &plist_len); logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", plist_xml); free(plist_xml); + plist_free(req_root_node); } 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); diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h index cd5ff26..0d340b3 100644 --- a/lib/raop_handlers.h +++ b/lib/raop_handlers.h @@ -161,6 +161,7 @@ raop_handler_info(raop_conn_t *conn, plist_dict_set_item(r_node, "displays", displays_node); plist_to_bin(r_node, response_data, (uint32_t *) response_datalen); + plist_free(r_node); http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist"); free(pk); free(hw_addr); From 863ae86b07dc20fc26a89b60f2c49ca0bb26f2c4 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 01:47:20 -0400 Subject: [PATCH 13/30] one more plist=-not-freed memory leak fix --- lib/raop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/raop.c b/lib/raop.c index cae247e..6abd1b8 100644 --- a/lib/raop.c +++ b/lib/raop.c @@ -279,6 +279,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { } } } + plist_free(req_root_node); if (conn->raop->callbacks.conn_teardown) { conn->raop->callbacks.conn_teardown(conn->raop->callbacks.cls, &teardown_96, &teardown_110); } From ca631197b49b880face7f0514f9b1641025bf239 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 02:07:12 -0400 Subject: [PATCH 14/30] nullify freed address in buffer --- lib/raop_buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/raop_buffer.c b/lib/raop_buffer.c index cdd199c..4fbe58a 100644 --- a/lib/raop_buffer.c +++ b/lib/raop_buffer.c @@ -336,6 +336,7 @@ void raop_buffer_flush(raop_buffer_t *raop_buffer, int next_seq) { for (int i = 0; i < RAOP_BUFFER_LENGTH; i++) { if (raop_buffer->entries[i].payload_data) { free(raop_buffer->entries[i].payload_data); + raop_buffer->entries[i].payload_data = NULL; raop_buffer->entries[i].payload_size = 0; } raop_buffer->entries[i].filled = 0; From c2491a3e9f55a355aed7930d5f00a7a03a504694 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 02:56:42 -0400 Subject: [PATCH 15/30] destroy raop_ntp after rtp and rtp_mirror --- lib/raop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/raop.c b/lib/raop.c index 6abd1b8..3b986f9 100644 --- a/lib/raop.c +++ b/lib/raop.c @@ -365,9 +365,6 @@ conn_destroy(void *ptr) { conn->raop->callbacks.conn_destroy(conn->raop->callbacks.cls); } - if (conn->raop_ntp) { - raop_ntp_destroy(conn->raop_ntp); - } if (conn->raop_rtp) { /* This is done in case TEARDOWN was not called */ raop_rtp_destroy(conn->raop_rtp); @@ -376,7 +373,10 @@ conn_destroy(void *ptr) { /* This is done in case TEARDOWN was not called */ raop_rtp_mirror_destroy(conn->raop_rtp_mirror); } - + if (conn->raop_ntp) { + raop_ntp_destroy(conn->raop_ntp); + } + if (conn->raop->callbacks.video_flush) { conn->raop->callbacks.video_flush(conn->raop->callbacks.cls); } From 300d0439f089107ee433eca4308f82bd1bb3f064 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 13:16:36 -0400 Subject: [PATCH 16/30] initialize pointers for coverart dacp_id etc. --- lib/raop_rtp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index ebacc13..3a8b533 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -167,6 +167,11 @@ raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, co raop_rtp->sync_data[i].rtp_epoch = 0; } + raop_rtp->dacp_id = NULL; + raop_rtp->active_remote_header = NULL; + raop_rtp->metadata = NULL; + raop_rpt->coverart = NULL; + memcpy(&raop_rtp->callbacks, callbacks, sizeof(raop_callbacks_t)); raop_rtp->buffer = raop_buffer_init(logger, aeskey, aesiv); if (!raop_rtp->buffer) { From 419852bb464905be4964b6969e0e5ee1660cab58 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 13:27:34 -0400 Subject: [PATCH 17/30] fix typo in last commit --- lib/raop_rtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index 3a8b533..fe298e7 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -170,7 +170,7 @@ raop_rtp_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp, co raop_rtp->dacp_id = NULL; raop_rtp->active_remote_header = NULL; raop_rtp->metadata = NULL; - raop_rpt->coverart = NULL; + raop_rtp->coverart = NULL; memcpy(&raop_rtp->callbacks, callbacks, sizeof(raop_callbacks_t)); raop_rtp->buffer = raop_buffer_init(logger, aeskey, aesiv); From 49971d8d0bbb9615212897fe82cbede65a894218 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 13:56:01 -0400 Subject: [PATCH 18/30] fix small memory leak (from valgrind) --- lib/raop_rtp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index fe298e7..b0a4945 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -762,7 +762,13 @@ raop_rtp_remote_control_id(raop_rtp_t *raop_rtp, const char *dacp_id, const char /* Set dacp stuff in thread instead */ MUTEX_LOCK(raop_rtp->run_mutex); + if (raop_rtp->dacp_id) { + free(raop_rtp->dacp_id); + } raop_rtp->dacp_id = strdup(dacp_id); + if (raop_rtp->active_remote_header) { + free(raop_rtp->active_remote_header); + } raop_rtp->active_remote_header = strdup(active_remote_header); MUTEX_UNLOCK(raop_rtp->run_mutex); } From 7eb4aaf1e79e4b95be3a1d5ea521107abb404443 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 14:33:01 -0400 Subject: [PATCH 19/30] cleaner error message handling --- renderers/audio_renderer_gstreamer.c | 2 +- renderers/video_renderer_gstreamer.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/renderers/audio_renderer_gstreamer.c b/renderers/audio_renderer_gstreamer.c index 4522d1d..fc6a6c4 100644 --- a/renderers/audio_renderer_gstreamer.c +++ b/renderers/audio_renderer_gstreamer.c @@ -105,7 +105,7 @@ void audio_renderer_init(logger_t *render_logger, const char* audiosink) { g_string_append (launch, " sync=false"); renderer_type[i]->pipeline = gst_parse_launch(launch->str, &error); if (error) { - g_error ("get_parse_launch error:\n %s\n",error->message); + g_error ("get_parse_launch error (audio %d):\n %s\n", i, error->message); g_clear_error (&error); } g_assert (renderer_type[i]->pipeline); diff --git a/renderers/video_renderer_gstreamer.c b/renderers/video_renderer_gstreamer.c index b581d8e..0884dd1 100644 --- a/renderers/video_renderer_gstreamer.c +++ b/renderers/video_renderer_gstreamer.c @@ -146,6 +146,10 @@ void video_renderer_init(logger_t *render_logger, const char *server_name, vide g_string_append(launch, " name=video_sink sync=false"); logger_log(logger, LOGGER_DEBUG, "GStreamer video pipeline will be:\n\"%s\"", launch->str); renderer->pipeline = gst_parse_launch(launch->str, &error); + if (error) { + g_error ("get_parse_launch error (video) :\n %s\n",error->message); + g_clear_error (&error); + } g_assert (renderer->pipeline); g_string_free(launch, TRUE); From e5c2a0ca6bf162ec9b83c3e1d2136bb0419172ed Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 15:19:58 -0400 Subject: [PATCH 20/30] small change in format label --- renderers/audio_renderer_gstreamer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderers/audio_renderer_gstreamer.c b/renderers/audio_renderer_gstreamer.c index fc6a6c4..ff9f80d 100644 --- a/renderers/audio_renderer_gstreamer.c +++ b/renderers/audio_renderer_gstreamer.c @@ -105,7 +105,7 @@ void audio_renderer_init(logger_t *render_logger, const char* audiosink) { g_string_append (launch, " sync=false"); renderer_type[i]->pipeline = gst_parse_launch(launch->str, &error); if (error) { - g_error ("get_parse_launch error (audio %d):\n %s\n", i, error->message); + g_error ("get_parse_launch error (audio %d):\n %s\n", i+1, error->message); g_clear_error (&error); } g_assert (renderer_type[i]->pipeline); From fd3e13dabf9683701fc22d286ffbf64eda4a60ed Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 15:28:30 -0400 Subject: [PATCH 21/30] fix typo in message --- renderers/audio_renderer_gstreamer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/renderers/audio_renderer_gstreamer.c b/renderers/audio_renderer_gstreamer.c index ff9f80d..9e8bb56 100644 --- a/renderers/audio_renderer_gstreamer.c +++ b/renderers/audio_renderer_gstreamer.c @@ -104,12 +104,13 @@ void audio_renderer_init(logger_t *render_logger, const char* audiosink) { g_string_append (launch, audiosink); g_string_append (launch, " sync=false"); renderer_type[i]->pipeline = gst_parse_launch(launch->str, &error); - if (error) { - g_error ("get_parse_launch error (audio %d):\n %s\n", i+1, error->message); + if (error) { + g_error ("gst_parse_launch error (audio %d):\n %s\n", i+1, error->message); g_clear_error (&error); } - g_assert (renderer_type[i]->pipeline); g_string_free(launch, TRUE); + g_assert (renderer_type[i]->pipeline); + renderer_type[i]->appsrc = gst_bin_get_by_name (GST_BIN (renderer_type[i]->pipeline), "audio_source"); renderer_type[i]->volume = gst_bin_get_by_name (GST_BIN (renderer_type[i]->pipeline), "volume"); switch (i) { From 584572fd33b93b7ff7e17475d3d3a4de6de2ab09 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 16:59:42 -0400 Subject: [PATCH 22/30] set shift to 0 as default for ALAC --- lib/raop_rtp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index b0a4945..c668eca 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -506,10 +506,12 @@ raop_rtp_thread_udp(void *arg) uint64_t sync_ntp_local = raop_ntp_convert_remote_time(raop_rtp->ntp, sync_ntp_remote); int64_t shift; switch (raop_rtp->ct) { - case 0x08: /*AAC-ELD */ + case 0x08: /*AAC-ELD */ shift = -11025; /* 44100/4 */ break; + case 0x02: default: + shift = 0; /* not needed fopr ALAC (audio only) */ break; } raop_rtp_sync_clock(raop_rtp, sync_ntp_local, ntp_start_time, sync_rtp, shift); From c1dde65e6e9d0a1544ebfd80f953f957b76209ef Mon Sep 17 00:00:00 2001 From: fduncanh Date: Wed, 4 May 2022 17:01:27 -0400 Subject: [PATCH 23/30] typo in comment --- lib/raop_rtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/raop_rtp.c b/lib/raop_rtp.c index c668eca..0a7aa03 100644 --- a/lib/raop_rtp.c +++ b/lib/raop_rtp.c @@ -511,7 +511,7 @@ raop_rtp_thread_udp(void *arg) break; case 0x02: default: - shift = 0; /* not needed fopr ALAC (audio only) */ + shift = 0; /* not needed for ALAC (audio only) */ break; } raop_rtp_sync_clock(raop_rtp, sync_ntp_local, ntp_start_time, sync_rtp, shift); From 0cb5d03ac0b525738f5022af97e0786b39833e77 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Thu, 5 May 2022 01:30:04 -0400 Subject: [PATCH 24/30] update ChangeLog in README --- README.html | 2 +- README.md | 4 ++-- README.txt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.html b/README.html index 0b0dd7e..2e835bb 100644 --- a/README.html +++ b/README.html @@ -139,7 +139,7 @@

    This triggers an unending stream of error messages, and means that the audio decryption key (also used in video decryption) was not correctly extracted from data sent by the client. This should not happen for iOS 9.3 or later clients. However, if a client uses the same older version of the protocol that is used by the Windows-based AirPlay client emulator AirMyPC, the protocol can be switched to the older version by the setting OLD_PROTOCOL_CLIENT_USER_AGENT_LIST in lib/global.h. UxPlay reports the client’s “User Agent” string when it connects. If some other client also fails to decrypt all audio and video, try adding its “User Agent” string in place of “xxx” in the entry “AirMyPC/2.0;xxx” in global.h and rebuild uxplay.

    Note that Uxplay declares itself to be an AppleTV3,2 with a sourceVersion 220.68; this can also be changed in global.h. It had been thought that it was necessary for UxPlay to claim to be an older 32 bit AppleTV model that cannot run modern 64bit tvOS, in order for the client to use a “legacy” protocol for pairing with the server (see the “Notes on AirPlay protocol versions” at the end of this README). However, UxPlay still works if it declares itself as an AppleTV6,2 with sourceVersion 380.20.1 (an AppleTV 4K 1st gen, introduced 2017, running tvOS 12.2.1), so it is unclear what setting prompts the client to use the “legacy” protocol needed by UxPlay.

    ChangeLog

    -

    1.52 2022-05-02 Cleaned up initial audio sync code, and reformatted streaming debug output (readable aligned timestamps with decimal points in seconds).

    +

    1.52 2022-05-05 Cleaned up initial audio sync code, and reformatted streaming debug output (readable aligned timestamps with decimal points in seconds). Eliminate memory leaks (found by valgrind).

    1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option -v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2, -v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from 1.48 that broke reconnection after “Stop Mirroring” is sent by client.

    1.50 2022-04-22 Added -fs fullscreen option (for Wayland or VAAPI plugins only), Changed -rpi to be for framebuffer (“lite”) RPi systems and added -rpigl (OpenGL) and -rpiwl (Wayland) options for RPi Desktop systems. Also modified timestamps from “DTS” to “PTS” for latency improvement, plus internal cleanups.

    1.49 2022-03-28 Addded options for dumping video and/or audio to file, for debugging, etc. h264 PPS/SPS NALU’s are shown with -d. Fixed video-not-working for M1 Mac clients.

    diff --git a/README.md b/README.md index ea52980..6874b7b 100644 --- a/README.md +++ b/README.md @@ -595,8 +595,8 @@ tvOS 12.2.1), so it is unclear what setting prompts the client to use the "legacy" protocol needed by UxPlay. # ChangeLog -1.52 2022-05-02 Cleaned up initial audio sync code, and reformatted streaming debug output (readable aligned timestamps with - decimal points in seconds). +1.52 2022-05-05 Cleaned up initial audio sync code, and reformatted streaming debug output (readable aligned timestamps with + decimal points in seconds). Eliminate memory leaks (found by valgrind). 1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option -v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2, -v4l2 -vs kmssink, and -v4l2 -vs waylandsink. Reverted a change from 1.48 that broke diff --git a/README.txt b/README.txt index 8800c52..15b69d6 100644 --- a/README.txt +++ b/README.txt @@ -777,9 +777,9 @@ the "legacy" protocol needed by UxPlay. ChangeLog ========= -1.52 2022-05-02 Cleaned up initial audio sync code, and reformatted +1.52 2022-05-05 Cleaned up initial audio sync code, and reformatted streaming debug output (readable aligned timestamps with decimal points -in seconds). +in seconds). Eliminate memory leaks (found by valgrind). 1.51 2022-04-24 Reworked options forVideo4Linux2 support (new option -v4l2) and short options -rpi, -rpifb, -rpiwl as synonyms for -v4l2, From c1af5c8cc81af811e75e9568973f2d6cced04723 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Thu, 5 May 2022 11:36:54 -0400 Subject: [PATCH 25/30] remove unnecessary assertion about packet[5] --- lib/raop_rtp_mirror.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/raop_rtp_mirror.c b/lib/raop_rtp_mirror.c index 9c28b67..fdbab5b 100644 --- a/lib/raop_rtp_mirror.c +++ b/lib/raop_rtp_mirror.c @@ -364,10 +364,6 @@ raop_rtp_mirror_thread(void *arg) memcpy(payload_out, raop_rtp_mirror->sps_pps, raop_rtp_mirror->sps_pps_len); raop_rtp_mirror->sps_pps_waiting = false; } else { - if (packet[5] != 0x00) { - logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, "Warning: regular NAL unit, but packet[5] = 0x%2.2x is not 0x00", packet[5]); - } - assert(packet[5] == 0); payload_out = (unsigned char*) malloc(payload_size); payload_decrypted = payload_out; } From b41da5636b1fab0361a07b52ce217e7658186ebf Mon Sep 17 00:00:00 2001 From: fduncanh Date: Thu, 5 May 2022 12:11:29 -0400 Subject: [PATCH 26/30] add explanatory comment about when the SPS+PPS NAL is prepended to current NAL --- lib/raop_rtp_mirror.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/raop_rtp_mirror.c b/lib/raop_rtp_mirror.c index fdbab5b..5a04c6f 100644 --- a/lib/raop_rtp_mirror.c +++ b/lib/raop_rtp_mirror.c @@ -356,6 +356,17 @@ raop_rtp_mirror_thread(void *arg) if (!raop_rtp_mirror->sps_pps_waiting && packet[5] != 0x00) { logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, "unexpected: packet[5] = %2.2x, but not preceded by SPS+PPS packet", packet[5]); } + /* if a previous unencrypted packet contains an SPS (type 7) and PPS (type 8) NAL which has not + * yet been sent, it should be prepended to the current NAL. In this case packet[5] is usually + * 0x10; however, the M1 Macs have increased the h264 level, and now the encrypted packet after the + * unencrypted SPS+PPS packet may contain a SEI (type 6) NAL prepended to the next VCL NAL, with + * packet[5] = 0x00. Now the flag raop_rtp_mirror->sps_pps_waiting = true will signal that a + * previous packet contained a SPS NAL + a PPS NAL, that has not yet been sent. This will trigger + * prepending it to the current NAL, and the sps_pps_waiting flag will be set to false after + * it has been prepended. It is not clear if the case packet[5] = 0x10 will occur when + * raop_rtp_mirror->sps_pps = false, but if it does, the current code will prepend the stored + * PPS + NSPS NAL to the current encrypted NAL, and issue a warning message */ + bool prepend_sps_pps = (raop_rtp_mirror->sps_pps_waiting || packet[5] != 0x00); if (prepend_sps_pps) { assert(raop_rtp_mirror->sps_pps); From e524fe10817d794e65921d631ff4993d92851c9a Mon Sep 17 00:00:00 2001 From: fduncanh Date: Thu, 5 May 2022 12:15:09 -0400 Subject: [PATCH 27/30] fix typo in comment added in last commit --- lib/raop_rtp_mirror.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/raop_rtp_mirror.c b/lib/raop_rtp_mirror.c index 5a04c6f..6f125a5 100644 --- a/lib/raop_rtp_mirror.c +++ b/lib/raop_rtp_mirror.c @@ -365,7 +365,7 @@ raop_rtp_mirror_thread(void *arg) * prepending it to the current NAL, and the sps_pps_waiting flag will be set to false after * it has been prepended. It is not clear if the case packet[5] = 0x10 will occur when * raop_rtp_mirror->sps_pps = false, but if it does, the current code will prepend the stored - * PPS + NSPS NAL to the current encrypted NAL, and issue a warning message */ + * PPS + SPS NAL to the current encrypted NAL, and issue a warning message */ bool prepend_sps_pps = (raop_rtp_mirror->sps_pps_waiting || packet[5] != 0x00); if (prepend_sps_pps) { From cfb6bc63cef74c73eb233353ac0be1deaa70521e Mon Sep 17 00:00:00 2001 From: fduncanh Date: Fri, 6 May 2022 12:12:29 -0400 Subject: [PATCH 28/30] update README (current iOS = 15.4) --- README.html | 2 +- README.md | 2 +- README.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.html b/README.html index 2e835bb..fbe5c7b 100644 --- a/README.html +++ b/README.html @@ -3,7 +3,7 @@

    Highlights:

    • GPLv3, open source.
    • -
    • Support for both AirPlay Mirror and AirPlay Audio-only (Apple Lossless ALAC) protocols from current iOS/iPadOS 15.2 client devices.
    • +
    • Support for both AirPlay Mirror and AirPlay Audio-only (Apple Lossless ALAC) protocols from current iOS/iPadOS 15.4 client devices.
    • macOS computers (2011 or later) can act either as AirPlay clients, or as the server running UxPlay (tested on macOS 10.15 Catalina and 12.3 Monterey). Using AirPlay, UxPlay can emulate a second display for macOS clients. Both Intel and “Apple Silicon” M1 Macs are now fully supported in both roles.
    • Support for older 32-bit iOS clients (such as iPad 2nd gen, iPhone 4S, when upgraded to iOS 9.3.5 or later), and a Windows AirPlay-client emulator, AirMyPC.
    • Uses GStreamer, with options to select different output “videosinks” and “audiosinks”.
    • diff --git a/README.md b/README.md index 6874b7b..b5d57e2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Highlights: * GPLv3, open source. * Support for both AirPlay Mirror and AirPlay Audio-only (Apple Lossless ALAC) protocols - from current iOS/iPadOS 15.2 client devices. + from current iOS/iPadOS 15.4 client devices. * macOS computers (2011 or later) can act either as AirPlay clients, or as the server running UxPlay (tested on macOS 10.15 Catalina and 12.3 Monterey). Using AirPlay, UxPlay can emulate a second display for macOS clients. Both Intel and "Apple Silicon" M1 Macs are now fully supported in both roles. diff --git a/README.txt b/README.txt index 15b69d6..9ecdd30 100644 --- a/README.txt +++ b/README.txt @@ -7,7 +7,7 @@ Highlights: - GPLv3, open source. - Support for both AirPlay Mirror and AirPlay Audio-only (Apple - Lossless ALAC) protocols from current iOS/iPadOS 15.2 client + Lossless ALAC) protocols from current iOS/iPadOS 15.4 client devices. - macOS computers (2011 or later) can act either as AirPlay clients, or as the server running UxPlay (tested on macOS 10.15 Catalina and From ce2fe373d682b9101bd7f99f52976ef75fcbb989 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Fri, 6 May 2022 12:38:41 -0400 Subject: [PATCH 29/30] edit README --- README.html | 4 ++-- README.md | 13 ++++++------- README.txt | 18 +++++++++--------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/README.html b/README.html index fbe5c7b..75d289d 100644 --- a/README.html +++ b/README.html @@ -42,11 +42,11 @@
    • cmake . (or “cmake -DZOOMFIX=ON .” to get a screen-sharing fix to make X11 mirror display windows visible to screen-sharing applications such as Zoom, see Improvements #3 below). ZOOMFIX is only needed for GStreamer-1.18.x or earlier.
    • make
    • sudo make install (you can afterwards uninstall with sudo make uninstall in the same directory in which this was run)
    • -
    • Install GStreamer plugins that you need: sudo apt-get install gstreamer1.0-<plugin>; values of <plugin> needed are: “plugins-base”, “libav” (for sound), “plugins-good” (for v4l2 hardware h264 decoding) and “plugins-bad” (for h264 decoding). Also needed may be “gl” for OpenGL support, and “x” for X11 support, although these may already be installed; “vaapi” is needed for hardware-accelerated h264 video decoding by Intel graphics (not for NVIDIA). Also install “tools” to get the utility gst-inspect-1.0 for examining the GStreamer installation.
    • +
    • Install GStreamer plugins that you need: sudo apt-get install gstreamer1.0-<plugin>; values of <plugin> needed are: “plugins-base”, “libav” (for sound), “plugins-good” (for v4l2 hardware h264 decoding) and “plugins-bad” (for h264 decoding). Also needed may be “gl” for OpenGL support (which may be useful, and should be used with h264 decoding by the NVIDIA GPU), and “x” for X11 support, although these may already be installed; “vaapi” is needed for hardware-accelerated h264 video decoding by Intel graphics (not for NVIDIA). Also install “tools” to get the utility gst-inspect-1.0 for examining the GStreamer installation.
    • If you intend to modify the code, use a separate “build” directory: replacecmake [ ] .bymkdir build ; cd build ; cmake [ ] ..”; you can then clean the build directory withrm -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 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.

      -

      Finally, run uxplay in a terminal window. If it is not seen by the iOS client’s drop-down “Screen Mirroring” panel, check that your DNS-SD server (usually avahi-daemon) is running: do this in a terminal window with systemctl status avahi-daemon. If this shows the avahi-daemon is not running, control it with sudo systemctl [start,stop,enable,disable] avahi-daemon (or avahi-daemon.service). If UxPlay is seen, but the client fails to connect when it is selected, there may be a firewall on the server that prevents UxPlay from receiving client connection requests unless some network ports are opened. See Troubleshooting below for help with this or other problems. See Usage for run-time options. For OpenGL support (option -vs glimagesink), needed for Raspberry Pi and NVIDIA GPU-based video decoding, make sure gstreamer1.0-gl is installed.

      +

      Finally, run uxplay in a terminal window. If it is not seen by the iOS client’s drop-down “Screen Mirroring” panel, check that your DNS-SD server (usually avahi-daemon) is running: do this in a terminal window with systemctl status avahi-daemon. If this shows the avahi-daemon is not running, control it with sudo systemctl [start,stop,enable,disable] avahi-daemon (or avahi-daemon.service). If UxPlay is seen, but the client fails to connect when it is selected, there may be a firewall on the server that prevents UxPlay from receiving client connection requests unless some network ports are opened. See Troubleshooting below for help with this or other problems. See Usage for run-time options.

      • Red Hat, Fedora, CentOS (now continued as Rocky Linux or Alma Linux): (sudo yum install) openssl-devel libplist-devel avahi-compat-libdns_sd-devel (some from the “PowerTools” add-on repository) (+libX11-devel for ZOOMFIX). The required GStreamer packages (some from rpmfusion.org) are: gstreamer1-devel gstreamer1-plugins-base-devel gstreamer1-libav gstreamer1-plugins-bad-free (+ gstreamer1-vaapi for intel graphics).

      • OpenSUSE: (sudo zypper install) libopenssl-devel libplist-devel avahi-compat-mDNSResponder-devel (+ libX11-devel for ZOOMFIX). The required GStreamer packages (you may need to use versions from Packman) are: gstreamer-devel gstreamer-plugins-base-devel gstreamer-plugins-libav gstreamer-plugins-bad (+ gstreamer-plugins-vaapi for Intel graphics).

      • diff --git a/README.md b/README.md index b5d57e2..7347a2a 100644 --- a/README.md +++ b/README.md @@ -156,11 +156,11 @@ for GStreamer-1.18.x or earlier**. 5. `make` 6. `sudo make install` (you can afterwards uninstall with `sudo make uninstall` in the same directory in which this was run) 7. Install GStreamer plugins that you need: `sudo apt-get install gstreamer1.0-`; values of - `` needed are: "plugins-base", "libav" (for sound), "plugins-good" (for v4l2 hardware h264 decoding) - and "plugins-bad" (for h264 decoding). Also needed may be "gl" for OpenGL support, and "x" for X11 support, although - these may already be installed; "vaapi" is needed for hardware-accelerated h264 video decoding by Intel graphics (not for NVIDIA). - Also install "tools" to get the utility gst-inspect-1.0 for examining the GStreamer installation. - + `` needed are: "**plugins-base**", "**libav**" (for sound), "**plugins-good**" (for v4l2 hardware h264 decoding) + and "**plugins-bad**" (for h264 decoding). Also needed may be "**gl**" for OpenGL support (which may be useful, and should + be used with h264 decoding by the NVIDIA GPU), and "**x**" for X11 support, although these may already be installed; "**vaapi**" + is needed for hardware-accelerated h264 video decoding by Intel graphics (not for NVIDIA). + Also install "**tools**" to get the utility gst-inspect-1.0 for examining the GStreamer installation. _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_. @@ -175,8 +175,7 @@ check that your DNS-SD server (usually avahi-daemon) is running: do this in a te If this shows the avahi-daemon is not running, control it with ```sudo systemctl [start,stop,enable,disable] avahi-daemon``` (or avahi-daemon.service). If UxPlay is seen, but the client fails to connect when it is selected, there may be a firewall on the server that prevents UxPlay from receiving client connection requests unless some network ports are opened. See [Troubleshooting](#troubleshooting) below for -help with this or other problems. See [Usage](#usage) for run-time options. For OpenGL support (option -vs glimagesink), needed for -Raspberry Pi and NVIDIA GPU-based video decoding, make sure gstreamer1.0-gl is installed. +help with this or other problems. See [Usage](#usage) for run-time options. * **Red Hat, Fedora, CentOS (now continued as Rocky Linux or Alma Linux):** (sudo yum install) openssl-devel libplist-devel avahi-compat-libdns_sd-devel (some from the "PowerTools" add-on repository) diff --git a/README.txt b/README.txt index 9ecdd30..fcdd3f7 100644 --- a/README.txt +++ b/README.txt @@ -203,13 +203,15 @@ packaging for a distribution, use the cmake option `sudo make uninstall` in the same directory in which this was run) 7. Install GStreamer plugins that you need: `sudo apt-get install gstreamer1.0-`; values of `` - needed are: "plugins-base", "libav" (for sound), "plugins-good" (for - v4l2 hardware h264 decoding) and "plugins-bad" (for h264 decoding). - Also needed may be "gl" for OpenGL support, and "x" for X11 support, - although these may already be installed; "vaapi" is needed for + needed are: "**plugins-base**", "**libav**" (for sound), + "**plugins-good**" (for v4l2 hardware h264 decoding) and + "**plugins-bad**" (for h264 decoding). Also needed may be "**gl**" + for OpenGL support (which may be useful, and should be used with + h264 decoding by the NVIDIA GPU), and "**x**" for X11 support, + although these may already be installed; "**vaapi**" is needed for hardware-accelerated h264 video decoding by Intel graphics (not for - NVIDIA). Also install "tools" to get the utility gst-inspect-1.0 for - examining the GStreamer installation. + NVIDIA). Also install "**tools**" to get the utility gst-inspect-1.0 + for examining the GStreamer installation. *If you intend to modify the code, use a separate "build" directory: replace* "`cmake [ ] .`" *by* @@ -235,9 +237,7 @@ connect when it is selected, there may be a firewall on the server that prevents UxPlay from receiving client connection requests unless some network ports are opened. See [Troubleshooting](#troubleshooting) below for help with this or other problems. See [Usage](#usage) for run-time -options. For OpenGL support (option -vs glimagesink), needed for -Raspberry Pi and NVIDIA GPU-based video decoding, make sure -gstreamer1.0-gl is installed. +options. - **Red Hat, Fedora, CentOS (now continued as Rocky Linux or Alma Linux):** (sudo yum install) openssl-devel libplist-devel From 7377d2c74268296616f65c0fc31c91be35884283 Mon Sep 17 00:00:00 2001 From: fduncanh Date: Fri, 6 May 2022 12:53:27 -0400 Subject: [PATCH 30/30] edit README --- README.html | 2 +- README.md | 2 +- README.txt | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.html b/README.html index 75d289d..eae8d41 100644 --- a/README.html +++ b/README.html @@ -42,7 +42,7 @@
      • cmake . (or “cmake -DZOOMFIX=ON .” to get a screen-sharing fix to make X11 mirror display windows visible to screen-sharing applications such as Zoom, see Improvements #3 below). ZOOMFIX is only needed for GStreamer-1.18.x or earlier.
      • make
      • sudo make install (you can afterwards uninstall with sudo make uninstall in the same directory in which this was run)
      • -
      • Install GStreamer plugins that you need: sudo apt-get install gstreamer1.0-<plugin>; values of <plugin> needed are: “plugins-base”, “libav” (for sound), “plugins-good” (for v4l2 hardware h264 decoding) and “plugins-bad” (for h264 decoding). Also needed may be “gl” for OpenGL support (which may be useful, and should be used with h264 decoding by the NVIDIA GPU), and “x” for X11 support, although these may already be installed; “vaapi” is needed for hardware-accelerated h264 video decoding by Intel graphics (not for NVIDIA). Also install “tools” to get the utility gst-inspect-1.0 for examining the GStreamer installation.
      • +
      • Install GStreamer plugins that you need: sudo apt-get install gstreamer1.0-<plugin>; values of <plugin> needed are: “plugins-base”, “libav” (for sound), “plugins-good” (for v4l2 hardware h264 decoding) and “plugins-bad” (for h264 decoding). Also needed may be “gl” for OpenGL support (which may be useful, and should be used with h264 decoding by the NVIDIA GPU), and “x” for X11 support, although these may already be installed; “vaapi” is needed for hardware-accelerated h264 video decoding by Intel or AMD graphics (but not for use with NVIDIA using proprietary drivers). Also install “tools” to get the utility gst-inspect-1.0 for examining the GStreamer installation.
      • If you intend to modify the code, use a separate “build” directory: replacecmake [ ] .bymkdir build ; cd build ; cmake [ ] ..”; you can then clean the build directory withrm -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 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.

        diff --git a/README.md b/README.md index 7347a2a..a61f437 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ for GStreamer-1.18.x or earlier**. `` needed are: "**plugins-base**", "**libav**" (for sound), "**plugins-good**" (for v4l2 hardware h264 decoding) and "**plugins-bad**" (for h264 decoding). Also needed may be "**gl**" for OpenGL support (which may be useful, and should be used with h264 decoding by the NVIDIA GPU), and "**x**" for X11 support, although these may already be installed; "**vaapi**" - is needed for hardware-accelerated h264 video decoding by Intel graphics (not for NVIDIA). + is needed for hardware-accelerated h264 video decoding by Intel or AMD graphics (but not for use with NVIDIA using proprietary drivers). Also install "**tools**" to get the utility gst-inspect-1.0 for examining the GStreamer installation. _If you intend to modify the code, use a separate "build" directory: replace_ "```cmake [ ] . ```" _by_ "```mkdir build ; cd build ; cmake [ ] ..```"; _you can then clean diff --git a/README.txt b/README.txt index fcdd3f7..47e8671 100644 --- a/README.txt +++ b/README.txt @@ -209,9 +209,10 @@ packaging for a distribution, use the cmake option for OpenGL support (which may be useful, and should be used with h264 decoding by the NVIDIA GPU), and "**x**" for X11 support, although these may already be installed; "**vaapi**" is needed for - hardware-accelerated h264 video decoding by Intel graphics (not for - NVIDIA). Also install "**tools**" to get the utility gst-inspect-1.0 - for examining the GStreamer installation. + hardware-accelerated h264 video decoding by Intel or AMD graphics + (but not for use with NVIDIA using proprietary drivers). Also + install "**tools**" to get the utility gst-inspect-1.0 for examining + the GStreamer installation. *If you intend to modify the code, use a separate "build" directory: replace* "`cmake [ ] .`" *by*