Date: Fri, 10 May 2024 15:11:53 -0400
Subject: [PATCH 16/39] Update README with new fullscreen option for
d3d11videosink (Windows)
---
README.html | 33 ++++++++++++++++++++-------------
README.md | 17 ++++++++++++-----
README.txt | 40 +++++++++++++++++++++++-----------------
3 files changed, 55 insertions(+), 35 deletions(-)
diff --git a/README.html b/README.html
index 74a78a2..aa61121 100644
--- a/README.html
+++ b/README.html
@@ -451,11 +451,12 @@ omitting the initial "-" of the command-line option. Lines
in the configuration file beginning with "#" are treated as
comments and ignored.
Run uxplay in a terminal window. On some systems,
-you can toggle into and out of fullscreen mode with F11 or (held-down
-left Alt)+Enter keys. Use Ctrl-C (or close the window) to terminate it
-when done. If the UxPlay server is not seen by the iOS client’s
-drop-down “Screen Mirroring” panel, check that your DNS-SD server
-(usually avahi-daemon) is running: do this in a terminal window with
+you can specify fullscreen mode with the -fs option, or
+toggle into and out of fullscreen mode with F11 or (held-down left
+Alt)+Enter keys. Use Ctrl-C (or close the window) to terminate it when
+done. If the UxPlay server is not seen by the iOS client’s drop-down
+“Screen Mirroring” panel, check that your DNS-SD server (usually
+avahi-daemon) is running: do this in a terminal window with
systemctl status avahi-daemon. If this shows the
avahi-daemon is not running, control it with
sudo systemctl [start,stop,enable,disable] avahi-daemon (on
@@ -867,14 +868,20 @@ used.
-vs <videosink> option, some choices for
<videosink> are d3d11videosink,
d3dvideosink, glimagesink,
-gtksink. With Direct3D 11.0 or greater, you can get the
-ability to toggle into and out of fullscreen mode using the Alt-Enter
-key combination with option
+gtksink.
+
+- With Direct3D 11.0 or greater, you can either always be in
+fullscreen mode using option
+
-vs "d3d11videosink fullscreen-toggle-mode=property fullscreen=true",
+or get the ability to toggle into and out of fullscreen mode using the
+Alt-Enter key combination with option
-vs "d3d11videosink fullscreen-toggle-mode=alt-enter". For
-convenience, this option will be added if just
--vs d3d11videosink (by itself) is used. (You may wish to
-add “vs d3d11videosink” (no initial “-”) to
-the UxPlay startup options file; see “man uxplay” or “uxplay -h”.)
+convenience, these options will be added if just
+-vs d3d11videosink with or without the fullscreen option
+“-fs” is used. (Windows users may wish to add
+“vs d3d11videosink” (no initial “-”) to the
+UxPlay startup options file; see “man uxplay” or “uxplay -h”.)
+
The executable uxplay.exe can also be run without the MSYS2
environment, in the Windows Terminal, with
C:\msys64\mingw64\bin\uxplay.
@@ -988,7 +995,7 @@ full-screen display that overscans, and is not displayed by gstreamer).
Recommendation: don’t use this option unless there is
some special reason to use it.
-fs uses fullscreen mode, but only works with X11,
-Wayland or VAAPI.
+Wayland, VAAPI, and D3D11 (Windows).
-p allows you to select the network ports used by
UxPlay (these need to be opened if the server is behind a firewall). By
itself, -p sets “legacy” ports TCP 7100, 7000, 7001, UDP 6000, 6001,
diff --git a/README.md b/README.md
index f4f1576..3377cbc 100644
--- a/README.md
+++ b/README.md
@@ -369,7 +369,8 @@ Since UxPlay-1.64, UxPlay can be started with options read from a configuration
directory ("~"), (3) ``~/.config/uxplayrc``. The format is one option per line, omitting the initial ``"-"`` of
the command-line option. Lines in the configuration file beginning with `"#"` are treated as comments and ignored.
-**Run uxplay in a terminal window**. On some systems, you can toggle into and out of fullscreen mode
+**Run uxplay in a terminal window**. On some systems, you can specify fullscreen mode with the `-fs` option, or
+toggle into and out of fullscreen mode
with F11 or (held-down left Alt)+Enter keys. Use Ctrl-C (or close the window)
to terminate it when done. If the UxPlay server is not seen by the
iOS client's drop-down "Screen Mirroring" panel, check that your DNS-SD
@@ -695,9 +696,15 @@ default audio device is used.
If you wish to specify the videosink using the `-vs ` option, some choices for `` are
`d3d11videosink`, ``d3dvideosink``, ```glimagesink```,
-`gtksink`. With Direct3D 11.0 or greater, you can get the ability to toggle into and out of fullscreen mode using the Alt-Enter key combination with
-option `-vs "d3d11videosink fullscreen-toggle-mode=alt-enter"`. For convenience, this option will be added if just ``-vs d3d11videosink`` (by itself) is used.
-(You may wish to add "``vs d3d11videosink``" (no initial "`-`") to the UxPlay startup options file; see "man uxplay" or "uxplay -h".)
+`gtksink`.
+
+* With Direct3D 11.0 or greater, you can either always be in fullscreen mode using
+option `-vs "d3d11videosink fullscreen-toggle-mode=property fullscreen=true"`, or
+get the ability to toggle into and out of fullscreen mode using the Alt-Enter key combination with
+option `-vs "d3d11videosink fullscreen-toggle-mode=alt-enter"`.
+For convenience, these options will be added if just ``-vs d3d11videosink`` with or without the fullscreen
+option "-fs" is used. _(Windows users may wish to add "``vs d3d11videosink``" (no initial "`-`") to the
+UxPlay startup options file; see "man uxplay" or "uxplay -h".)_
The executable uxplay.exe can also be run without the MSYS2 environment, in
the Windows Terminal, with `C:\msys64\mingw64\bin\uxplay`.
@@ -783,7 +790,7 @@ using UxPlay as a second monitor for a mac computer, or monitoring a webcam; wit
Recommendation: **don't use this option** unless there is some special
reason to use it.
-**-fs** uses fullscreen mode, but only works with X11, Wayland or VAAPI.
+**-fs** uses fullscreen mode, but only works with X11, Wayland, VAAPI, and D3D11 (Windows).
**-p** allows you to select the network ports used by UxPlay (these need
to be opened if the server is behind a firewall). By itself, -p sets
diff --git a/README.txt b/README.txt
index 8320e8e..6532296 100644
--- a/README.txt
+++ b/README.txt
@@ -445,14 +445,14 @@ one option per line, omitting the initial `"-"` of the command-line
option. Lines in the configuration file beginning with `"#"` are treated
as comments and ignored.
-**Run uxplay in a terminal window**. On some systems, you can toggle
-into and out of fullscreen mode with F11 or (held-down left Alt)+Enter
-keys. Use Ctrl-C (or close the window) to terminate it when done. If the
-UxPlay server is not seen by the iOS client's drop-down "Screen
-Mirroring" panel, check that your DNS-SD server (usually avahi-daemon)
-is running: do this in a terminal window with
-`systemctl status avahi-daemon`. If this shows the avahi-daemon is not
-running, control it with
+**Run uxplay in a terminal window**. On some systems, you can specify
+fullscreen mode with the `-fs` option, or toggle into and out of
+fullscreen mode with F11 or (held-down left Alt)+Enter keys. Use Ctrl-C
+(or close the window) to terminate it when done. If the UxPlay server is
+not seen by the iOS client's drop-down "Screen Mirroring" panel, check
+that your DNS-SD server (usually avahi-daemon) is running: do this in a
+terminal window with `systemctl status avahi-daemon`. If this shows the
+avahi-daemon is not running, control it with
`sudo systemctl [start,stop,enable,disable] avahi-daemon` (on
non-systemd systems, such as \*BSD, use
`sudo service avahi-daemon [status, start, stop, restart, ...]`). If
@@ -871,14 +871,19 @@ like `\{0.0.0.00000000\}.\{98e35b2b-8eba-412e-b840-fd2c2492cf44\}`. If
If you wish to specify the videosink using the `-vs ` option,
some choices for `` are `d3d11videosink`, `d3dvideosink`,
-`glimagesink`, `gtksink`. With Direct3D 11.0 or greater, you can get the
-ability to toggle into and out of fullscreen mode using the Alt-Enter
-key combination with option
-`-vs "d3d11videosink fullscreen-toggle-mode=alt-enter"`. For
-convenience, this option will be added if just `-vs d3d11videosink` (by
-itself) is used. (You may wish to add "`vs d3d11videosink`" (no initial
-"`-`") to the UxPlay startup options file; see "man uxplay" or "uxplay
--h".)
+`glimagesink`, `gtksink`.
+
+- With Direct3D 11.0 or greater, you can either always be in
+ fullscreen mode using option
+ `-vs "d3d11videosink fullscreen-toggle-mode=property fullscreen=true"`,
+ or get the ability to toggle into and out of fullscreen mode using
+ the Alt-Enter key combination with option
+ `-vs "d3d11videosink fullscreen-toggle-mode=alt-enter"`. For
+ convenience, these options will be added if just
+ `-vs d3d11videosink` with or without the fullscreen option "-fs" is
+ used. *(Windows users may wish to add "`vs d3d11videosink`" (no
+ initial "`-`") to the UxPlay startup options file; see "man uxplay"
+ or "uxplay -h".)*
The executable uxplay.exe can also be run without the MSYS2 environment,
in the Windows Terminal, with `C:\msys64\mingw64\bin\uxplay`.
@@ -998,7 +1003,8 @@ display that overscans, and is not displayed by gstreamer).
Recommendation: **don't use this option** unless there is some special
reason to use it.
-**-fs** uses fullscreen mode, but only works with X11, Wayland or VAAPI.
+**-fs** uses fullscreen mode, but only works with X11, Wayland, VAAPI,
+and D3D11 (Windows).
**-p** allows you to select the network ports used by UxPlay (these need
to be opened if the server is behind a firewall). By itself, -p sets
From f0407d780e9c88367c56ff394a3a4cb5fbb8a783 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Sun, 12 May 2024 01:24:38 -0400
Subject: [PATCH 17/39] add zone_id to link-local ipv6 addresses
---
lib/httpd.c | 15 +++++++++------
lib/httpd.h | 3 ++-
lib/netutils.c | 8 +++++---
lib/netutils.h | 2 +-
lib/raop.c | 35 +++++++++++++----------------------
lib/raop.h | 4 ++--
lib/raop_handlers.h | 37 ++++++++++++++++---------------------
lib/utils.c | 26 ++++++++++++++++++++++++++
lib/utils.h | 3 ++-
9 files changed, 76 insertions(+), 57 deletions(-)
diff --git a/lib/httpd.c b/lib/httpd.c
index e1e3704..a4adf21 100644
--- a/lib/httpd.c
+++ b/lib/httpd.c
@@ -115,7 +115,8 @@ httpd_remove_connection(httpd_t *httpd, http_connection_t *connection)
}
static int
-httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len, unsigned char *remote, int remote_len)
+httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len, unsigned char *remote,
+ int remote_len, unsigned int zone_id)
{
void *user_data;
int i;
@@ -131,7 +132,7 @@ httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len
return -1;
}
- user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len);
+ user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len, zone_id);
if (!user_data) {
logger_log(httpd->logger, LOGGER_ERR, "Error initializing HTTP request handler");
return -1;
@@ -152,6 +153,7 @@ httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6)
struct sockaddr_storage local_saddr;
socklen_t local_saddrlen;
unsigned char *local, *remote;
+ unsigned int local_zone_id, remote_zone_id;
int local_len, remote_len;
int ret, fd;
@@ -172,9 +174,10 @@ httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6)
logger_log(httpd->logger, LOGGER_INFO, "Accepted %s client on socket %d",
(is_ipv6 ? "IPv6" : "IPv4"), fd);
- local = netutils_get_address(&local_saddr, &local_len);
- remote = netutils_get_address(&remote_saddr, &remote_len);
-
+ local = netutils_get_address(&local_saddr, &local_len, &local_zone_id);
+ remote = netutils_get_address(&remote_saddr, &remote_len, &remote_zone_id);
+ assert (local_zone_id == remote_zone_id);
+
#ifdef NOHOLD
/* remove existing connections to make way for new connections:
* this will only occur if max_connections > 2 */
@@ -190,7 +193,7 @@ httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6)
}
#endif
- ret = httpd_add_connection(httpd, fd, local, local_len, remote, remote_len);
+ ret = httpd_add_connection(httpd, fd, local, local_len, remote, remote_len, local_zone_id);
if (ret == -1) {
shutdown(fd, SHUT_RDWR);
closesocket(fd);
diff --git a/lib/httpd.h b/lib/httpd.h
index fc184e1..a606a6a 100644
--- a/lib/httpd.h
+++ b/lib/httpd.h
@@ -23,7 +23,8 @@ typedef struct httpd_s httpd_t;
struct httpd_callbacks_s {
void* opaque;
- void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen);
+ void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote,
+ int remotelen, unsigned int zone_id);
void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response);
void (*conn_destroy)(void *ptr);
};
diff --git a/lib/netutils.c b/lib/netutils.c
index 5832c43..642efba 100644
--- a/lib/netutils.c
+++ b/lib/netutils.c
@@ -53,17 +53,17 @@ netutils_cleanup()
}
unsigned char *
-netutils_get_address(void *sockaddr, int *length)
+netutils_get_address(void *sockaddr, int *length, unsigned int *zone_id)
{
unsigned char ipv4_prefix[] = { 0,0,0,0,0,0,0,0,0,0,255,255 };
struct sockaddr *address = sockaddr;
assert(address);
assert(length);
-
+ assert(zone_id);
if (address->sa_family == AF_INET) {
struct sockaddr_in *sin;
-
+ *zone_id = 0;
sin = (struct sockaddr_in *)address;
*length = sizeof(sin->sin_addr.s_addr);
return (unsigned char *)&sin->sin_addr.s_addr;
@@ -73,9 +73,11 @@ netutils_get_address(void *sockaddr, int *length)
sin6 = (struct sockaddr_in6 *)address;
if (!memcmp(sin6->sin6_addr.s6_addr, ipv4_prefix, 12)) {
/* Actually an embedded IPv4 address */
+ *zone_id = 0;
*length = sizeof(sin6->sin6_addr.s6_addr)-12;
return (sin6->sin6_addr.s6_addr+12);
}
+ *zone_id = (unsigned int) sin6->sin6_scope_id;
*length = sizeof(sin6->sin6_addr.s6_addr);
return sin6->sin6_addr.s6_addr;
}
diff --git a/lib/netutils.h b/lib/netutils.h
index 63854ad..e64c638 100644
--- a/lib/netutils.h
+++ b/lib/netutils.h
@@ -19,7 +19,7 @@ int netutils_init();
void netutils_cleanup();
int netutils_init_socket(unsigned short *port, int use_ipv6, int use_udp);
-unsigned char *netutils_get_address(void *sockaddr, int *length);
+unsigned char *netutils_get_address(void *sockaddr, int *length, unsigned int *zone_id);
int netutils_parse_address(int family, const char *src, void *dst, int dstlen);
#endif
diff --git a/lib/raop.c b/lib/raop.c
index bc9554e..9a249bc 100644
--- a/lib/raop.c
+++ b/lib/raop.c
@@ -88,6 +88,8 @@ struct raop_conn_s {
unsigned char *remote;
int remotelen;
+ unsigned int zone_id;
+
bool have_active_remote;
};
typedef struct raop_conn_s raop_conn_t;
@@ -95,10 +97,10 @@ typedef struct raop_conn_s raop_conn_t;
#include "raop_handlers.h"
static void *
-conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen) {
+conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remote, int remotelen, unsigned int zone_id) {
raop_t *raop = opaque;
raop_conn_t *conn;
-
+ char ip_address[40];
assert(raop);
conn = calloc(1, sizeof(raop_conn_t));
@@ -122,26 +124,12 @@ conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remot
return NULL;
}
- if (locallen == 4) {
- logger_log(conn->raop->logger, LOGGER_INFO,
- "Local: %d.%d.%d.%d",
- local[0], local[1], local[2], local[3]);
- } else if (locallen == 16) {
- logger_log(conn->raop->logger, LOGGER_INFO,
- "Local: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
- local[0], local[1], local[2], local[3], local[4], local[5], local[6], local[7],
- local[8], local[9], local[10], local[11], local[12], local[13], local[14], local[15]);
- }
- if (remotelen == 4) {
- logger_log(conn->raop->logger, LOGGER_INFO,
- "Remote: %d.%d.%d.%d",
- remote[0], remote[1], remote[2], remote[3]);
- } else if (remotelen == 16) {
- logger_log(conn->raop->logger, LOGGER_INFO,
- "Remote: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
- remote[0], remote[1], remote[2], remote[3], remote[4], remote[5], remote[6], remote[7],
- remote[8], remote[9], remote[10], remote[11], remote[12], remote[13], remote[14], remote[15]);
- }
+
+ utils_ipaddress_to_string(locallen, local, zone_id, ip_address, (int) sizeof(ip_address));
+ logger_log(conn->raop->logger, LOGGER_INFO, "Local : %s", ip_address);
+
+ utils_ipaddress_to_string(remotelen, remote, zone_id, ip_address, (int) sizeof(ip_address));
+ logger_log(conn->raop->logger, LOGGER_INFO, "Remote: %s", ip_address);
conn->local = malloc(locallen);
assert(conn->local);
@@ -151,8 +139,11 @@ conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remot
assert(conn->remote);
memcpy(conn->remote, remote, remotelen);
+ conn->zone_id = zone_id;
+
conn->locallen = locallen;
conn->remotelen = remotelen;
+
conn->have_active_remote = false;
if (raop->callbacks.conn_init) {
diff --git a/lib/raop.h b/lib/raop.h
index 1982773..43a7a4e 100644
--- a/lib/raop.h
+++ b/lib/raop.h
@@ -65,8 +65,8 @@ struct raop_callbacks_s {
void (*export_dacp) (void *cls, const char *active_remote, const char *dacp_id);
};
typedef struct raop_callbacks_s raop_callbacks_t;
-raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote, int remote_addr_len,
- unsigned short timing_rport, timing_protocol_t *time_protocol);
+raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const char *remote,
+ int remote_addr_len, unsigned short timing_rport, timing_protocol_t *time_protocol);
RAOP_API raop_t *raop_init(raop_callbacks_t *callbacks);
RAOP_API int raop_init2(raop_t *raop, int max_clients, const char *device_id, const char *keyfile);
diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h
index c418f70..b2ba5c9 100644
--- a/lib/raop_handlers.h
+++ b/lib/raop_handlers.h
@@ -737,28 +737,23 @@ raop_handler_setup(raop_conn_t *conn,
conn->raop_ntp = NULL;
conn->raop_rtp = NULL;
conn->raop_rtp_mirror = NULL;
- if (conn->remotelen == 4 || conn->remotelen == 16) {
- char remote[40];
- if (conn->remotelen == 4) {
- /* IPV4 */
- snprintf(remote, sizeof(remote), "%d.%d.%d.%d", conn->remote[0], conn->remote[1],
- conn->remote[2], conn->remote[3]);
- } else {
- /*IPV6*/
- snprintf(remote, sizeof(remote), "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
- conn->remote[0], conn->remote[1], conn->remote[2], conn->remote[3],
- conn->remote[4], conn->remote[5], conn->remote[6], conn->remote[7],
- conn->remote[8], conn->remote[9], conn->remote[10], conn->remote[11],
- conn->remote[12], conn->remote[13], conn->remote[14], conn->remote[15]);
- }
- conn->raop_ntp = raop_ntp_init(conn->raop->logger, &conn->raop->callbacks, remote,
- conn->remotelen, (unsigned short) timing_rport, &time_protocol);
- raop_ntp_start(conn->raop_ntp, &timing_lport, conn->raop->max_ntp_timeouts);
- conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp,
- remote, conn->remotelen, aeskey, aesiv);
- conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks,
- conn->raop_ntp, remote, conn->remotelen, aeskey);
+ char remote[40];
+ int len = utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, remote, (int) sizeof(remote));
+ if (!len || len > sizeof(remote)) {
+ char *str = utils_data_to_string(conn->remote, conn->remotelen, 16);
+ logger_log(conn->raop->logger, LOGGER_ERR, "failed to extract valid client ip address:\n"
+ "*** UxPlay will be unable to send communications to client.\n"
+ "*** address length %d, zone_id %u address data:\n%sparser returned \"%s\"\n",
+ conn->remotelen, conn->zone_id, str, remote);
+ free(str);
}
+ conn->raop_ntp = raop_ntp_init(conn->raop->logger, &conn->raop->callbacks, remote,
+ conn->remotelen, (unsigned short) timing_rport, &time_protocol);
+ raop_ntp_start(conn->raop_ntp, &timing_lport, conn->raop->max_ntp_timeouts);
+ conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp,
+ remote, conn->remotelen, aeskey, aesiv);
+ conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks,
+ conn->raop_ntp, remote, conn->remotelen, aeskey);
plist_t res_event_port_node = plist_new_uint(conn->raop->port);
plist_t res_timing_port_node = plist_new_uint(timing_lport);
diff --git a/lib/utils.c b/lib/utils.c
index 561e79b..f6c89f0 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -256,3 +256,29 @@ void ntp_timestamp_to_seconds(uint64_t ntp_timestamp, char *timestamp, size_t ma
strftime(timestamp, 3, "%S", &ts);
snprintf(timestamp + 2, 11,".%9.9lu", (unsigned long) ntp_timestamp % SECOND_IN_NSECS);
}
+
+int utils_ipaddress_to_string(int addresslen, const unsigned char *address, unsigned int zone_id, char *string, int sizeof_string) {
+ int ret = 0;
+ unsigned char ipv6_link_local_prefix[] = { 0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+ assert(sizeof_string > 0);
+ assert(string);
+ if (addresslen != 4 && addresslen != 16) { //invalid address length (only ipv4 and ipv6 allowed)
+ string[0] = '\0';
+ }
+ if (addresslen == 4) { /* IPV4 */
+ ret = snprintf(string, sizeof_string, "%d.%d.%d.%d", address[0], address[1], address[2], address[3]);
+ } else if (zone_id) { /* IPV6 link-local */
+ if (memcmp(address, ipv6_link_local_prefix, 8)) {
+ string[0] = '\0'; //only link-local ipv6 addresses can have a zone_id
+ } else {
+ ret = snprintf(string, sizeof_string, "fe80::%02x%02x:%02x%02x:%02x%02x:%02x%02x%%%u",
+ address[8], address[9], address[10], address[11],
+ address[12], address[13], address[14], address[15], zone_id);
+ }
+ } else { /* IPV6 standard*/
+ ret = snprintf(string, sizeof_string, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+ address[0], address[1], address[2], address[3], address[4], address[5], address[6], address[7],
+ address[8], address[9], address[10], address[11], address[12], address[13], address[14], address[15]);
+ }
+ return ret;
+}
diff --git a/lib/utils.h b/lib/utils.h
index 671115a..be5db30 100644
--- a/lib/utils.h
+++ b/lib/utils.h
@@ -30,5 +30,6 @@ char *utils_data_to_string(const unsigned char *data, int datalen, int chars_per
char *utils_data_to_text(const char *data, int datalen);
void ntp_timestamp_to_time(uint64_t ntp_timestamp, char *timestamp, size_t maxsize);
void ntp_timestamp_to_seconds(uint64_t ntp_timestamp, char *timestamp, size_t maxsize);
-
+int utils_ipaddress_to_string(int addresslen, const unsigned char *address,
+ unsigned int zone_id, char *string, int len);
#endif
From bdd61ad0cacae04624cbcfccfb3fc8c6429f069e Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Tue, 14 May 2024 21:29:33 -0400
Subject: [PATCH 18/39] minor cleanup in lib/CMakeLists.txt (libplist)
---
lib/CMakeLists.txt | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 4dbdd37..ed9fb67 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -80,17 +80,16 @@ else()
endif()
# libplist
+pkg_search_module(PLIST REQUIRED libplist-2.0)
+if ( PLIST_FOUND )
+ message( STATUS "found libplist-${PLIST_VERSION}" )
+endif()
if( APPLE OR WIN32 )
# use static linking
- pkg_search_module( PLIST REQUIRED libplist-2.0 )
find_library( LIBPLIST libplist-2.0.a REQUIRED )
message( STATUS "(Static linking) LIBPLIST " ${LIBPLIST} )
target_link_libraries ( airplay ${LIBPLIST} )
else ()
- pkg_search_module(PLIST libplist>=2.0)
- if(NOT PLIST_FOUND)
- pkg_search_module(PLIST REQUIRED libplist-2.0)
- endif()
find_library( LIBPLIST ${PLIST_LIBRARIES} PATH ${PLIST_LIBDIR} )
target_link_libraries ( airplay PUBLIC ${LIBPLIST} )
endif()
From c4a17b83cd07f18cb72e22150468efe27cb9e859 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Tue, 14 May 2024 21:33:14 -0400
Subject: [PATCH 19/39] README fix video memory units (GB->MB) in RPi
discussion
---
README.html | 8 ++++----
README.md | 6 +++---
README.txt | 8 ++++----
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/README.html b/README.html
index aa61121..cd745df 100644
--- a/README.html
+++ b/README.html
@@ -628,10 +628,10 @@ lower-power models to keep audio and video synchronized using
timestamps. In Legacy Raspberry Pi OS (Bullseye), raspi-config
“Performance Options” allows specifying how much memory to allocate to
the GPU, but this setting appears to be absent in Bookworm (but it can
-still be set to e.g. 128GB by adding a line “gpu_mem=128” in
-/boot/config.txt). A Pi Zero 2 W (which has 512GB memory) worked well
-when tested in 32 bit Bullseye or Bookworm Lite with 128GB allocated to
-the GPU (default seems to be 64GB).
+still be set to e.g. 128MB by adding a line “gpu_mem=128” in
+/boot/config.txt). A Pi Zero 2 W (which has 512MB memory) worked well
+when tested in 32 bit Bullseye or Bookworm Lite with 128MB allocated to
+the GPU (default seems to be 64MB).
The basic uxplay options for R Pi are
uxplay [-vs <videosink>]. The choice
<videosink> = glimagesink is sometimes
diff --git a/README.md b/README.md
index 3377cbc..f913862 100644
--- a/README.md
+++ b/README.md
@@ -502,9 +502,9 @@ See [Usage](#usage) for more run-time options.
Even with GPU video decoding, some frames may be dropped by the lower-power models to keep audio and video synchronized
using timestamps. In Legacy Raspberry Pi OS (Bullseye), raspi-config "Performance Options" allows specifying how much memory
-to allocate to the GPU, but this setting appears to be absent in Bookworm (but it can still be set to e.g. 128GB by adding a line "gpu_mem=128" in /boot/config.txt).
-A Pi Zero 2 W (which has 512GB memory) worked well when tested in 32 bit Bullseye or Bookworm Lite
-with 128GB allocated to the GPU (default seems to be 64GB).
+to allocate to the GPU, but this setting appears to be absent in Bookworm (but it can still be set to e.g. 128MB by adding a line "gpu_mem=128" in /boot/config.txt).
+A Pi Zero 2 W (which has 512MB memory) worked well when tested in 32 bit Bullseye or Bookworm Lite
+with 128MB allocated to the GPU (default seems to be 64MB).
The basic uxplay options for R Pi are ```uxplay [-vs ]```. The
choice `` = ``glimagesink`` is sometimes useful.
diff --git a/README.txt b/README.txt
index 6532296..d464317 100644
--- a/README.txt
+++ b/README.txt
@@ -626,10 +626,10 @@ lower-power models to keep audio and video synchronized using
timestamps. In Legacy Raspberry Pi OS (Bullseye), raspi-config
"Performance Options" allows specifying how much memory to allocate to
the GPU, but this setting appears to be absent in Bookworm (but it can
-still be set to e.g. 128GB by adding a line "gpu_mem=128" in
-/boot/config.txt). A Pi Zero 2 W (which has 512GB memory) worked well
-when tested in 32 bit Bullseye or Bookworm Lite with 128GB allocated to
-the GPU (default seems to be 64GB).
+still be set to e.g. 128MB by adding a line "gpu_mem=128" in
+/boot/config.txt). A Pi Zero 2 W (which has 512MB memory) worked well
+when tested in 32 bit Bullseye or Bookworm Lite with 128MB allocated to
+the GPU (default seems to be 64MB).
The basic uxplay options for R Pi are `uxplay [-vs ]`. The
choice `` = `glimagesink` is sometimes useful. With the
From 0f638503808915f4c68b03b7e863bbd5c750d571 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Thu, 16 May 2024 00:35:19 -0400
Subject: [PATCH 20/39] set all AirPlay feature bits when dnssd starts
---
uxplay.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 92 insertions(+), 7 deletions(-)
diff --git a/uxplay.cpp b/uxplay.cpp
index b68765b..43e8a5d 100644
--- a/uxplay.cpp
+++ b/uxplay.cpp
@@ -1330,6 +1330,95 @@ static int start_dnssd(std::vector hw_addr, std::string name) {
LOGE("Could not initialize dnssd library!: error %d", dnssd_error);
return 1;
}
+
+ /* after dnssd starts, reset the default feature set here
+ /* (overwrites features set in dnssdint.h */
+ /* default: FEATURES_1 = 0x5A7FFEE6, FEATURES_2 = 0 */
+
+ dnssd_set_airplay_features(dnssd, 0, 0); // AirPlay video supported
+ dnssd_set_airplay_features(dnssd, 1, 1); // photo supported
+ dnssd_set_airplay_features(dnssd, 2, 1); // video protected with FairPlay DRM
+ dnssd_set_airplay_features(dnssd, 3, 0); // volume control supported for videos
+
+ dnssd_set_airplay_features(dnssd, 4, 0); // http live streaming (HLS) supported
+ dnssd_set_airplay_features(dnssd, 5, 1); // slideshow supported
+ dnssd_set_airplay_features(dnssd, 6, 1); //
+ dnssd_set_airplay_features(dnssd, 7, 1); // mirroring supported
+
+ dnssd_set_airplay_features(dnssd, 8, 0); // screen rotation supported
+ dnssd_set_airplay_features(dnssd, 9, 1); // audio supported
+ dnssd_set_airplay_features(dnssd, 10, 1); //
+ dnssd_set_airplay_features(dnssd, 11, 1); // audio packet redundancy supported
+
+ dnssd_set_airplay_features(dnssd, 12, 1); // FaiPlay secure auth supported
+ dnssd_set_airplay_features(dnssd, 13, 1); // photo preloading supported
+ dnssd_set_airplay_features(dnssd, 14, 1); // Authentication bit 4: FairPlay authentication
+ dnssd_set_airplay_features(dnssd, 15, 1); // Metadata bit 1 support: Artwork
+
+ dnssd_set_airplay_features(dnssd, 16, 1); // Metadata bit 2 support: Soundtrack Progress
+ dnssd_set_airplay_features(dnssd, 17, 1); // Metadata bit 0 support: Text (DAACP) "Now Playing" info.
+ dnssd_set_airplay_features(dnssd, 18, 1); // Audio format 1 support:
+ dnssd_set_airplay_features(dnssd, 19, 1); // Audio format 2 support: must be set for AirPlay 2 multiroom audio
+
+ dnssd_set_airplay_features(dnssd, 20, 1); // Audio format 3 support: must be set for AirPlay 2 multiroom audio
+ dnssd_set_airplay_features(dnssd, 21, 1); // Audio format 4 support:
+ dnssd_set_airplay_features(dnssd, 22, 1); // Authentication type 4: FairPlay authentication
+ dnssd_set_airplay_features(dnssd, 23, 0); // Authentication type 1: RSA Authentication
+
+ dnssd_set_airplay_features(dnssd, 24, 0); //
+ dnssd_set_airplay_features(dnssd, 25, 1); //
+ dnssd_set_airplay_features(dnssd, 26, 0); // Has Unified Advertiser info
+ dnssd_set_airplay_features(dnssd, 27, 1); // Supports Legacy Pairing
+
+ dnssd_set_airplay_features(dnssd, 28, 1); //
+ dnssd_set_airplay_features(dnssd, 29, 0); //
+ dnssd_set_airplay_features(dnssd, 30, 1); // RAOP support: with this bit set, the AirTunes service is not required.
+ dnssd_set_airplay_features(dnssd, 31, 0); //
+
+ for (int i = 32; i < 64; i++) {
+ dnssd_set_airplay_features(dnssd, i, 0);
+ }
+
+ /* bits 32-63 are not used here: see https://emanualcozzi.net/docs/airplay2/features
+ dnssd_set_airplay_features(dnssd, 32, 0); // isCarPlay when ON,; Supports InitialVolume when OFF
+ dnssd_set_airplay_features(dnssd, 33, 0); // Supports Air Play Video Play Queue
+ dnssd_set_airplay_features(dnssd, 34, 0); // Supports Air Play from cloud (requires that bit 6 is ON)
+ dnssd_set_airplay_features(dnssd, 35, 0); // Supports TLS_PSK
+
+ dnssd_set_airplay_features(dnssd, 36, 0); //
+ dnssd_set_airplay_features(dnssd, 37, 0); //
+ dnssd_set_airplay_features(dnssd, 38, 0); // Supports Unified Media Control (CoreUtils Pairing and Encryption)
+ dnssd_set_airplay_features(dnssd, 39, 0); //
+
+ dnssd_set_airplay_features(dnssd, 40, 0); // Supports Buffered Audio
+ dnssd_set_airplay_features(dnssd, 41, 0); // Supports PTP
+ dnssd_set_airplay_features(dnssd, 42, 0); // Supports Screen Multi Codec
+ dnssd_set_airplay_features(dnssd, 43, 0); // Supports System Pairing
+
+ dnssd_set_airplay_features(dnssd, 44, 0); // is AP Valeria Screen Sender
+ dnssd_set_airplay_features(dnssd, 45, 0); //
+ dnssd_set_airplay_features(dnssd, 46, 0); // Supports HomeKit Pairing and Access Control
+ dnssd_set_airplay_features(dnssd, 47, 0); //
+
+ dnssd_set_airplay_features(dnssd, 48, 0); // Supports CoreUtils Pairing and Encryption
+ dnssd_set_airplay_features(dnssd, 49, 0); //
+ dnssd_set_airplay_features(dnssd, 50, 0); // Metadata bit 3: "Now Playing" info sent by bplist not DAACP test
+ dnssd_set_airplay_features(dnssd, 51, 0); // Supports Unified Pair Setup and MFi Authentication
+
+ dnssd_set_airplay_features(dnssd, 52, 0); // Supports Set Peers Extended Message
+ dnssd_set_airplay_features(dnssd, 53, 0); //
+ dnssd_set_airplay_features(dnssd, 54, 0); // Supports AP Sync
+ dnssd_set_airplay_features(dnssd, 55, 0); // Supports WoL
+
+ dnssd_set_airplay_features(dnssd, 56, 0); // Supports Wol
+ dnssd_set_airplay_features(dnssd, 57, 0); //
+ dnssd_set_airplay_features(dnssd, 58, 0); // Supports Hangdog Remote Control
+ dnssd_set_airplay_features(dnssd, 59, 0); // Supports AudioStreamConnection setup
+
+ dnssd_set_airplay_features(dnssd, 60, 0); // Supports Audo Media Data Control
+ dnssd_set_airplay_features(dnssd, 61, 0); // Supports RFC2198 redundancy
+ */
+
/* bit 27 of Features determines whether the AirPlay2 client-pairing protocol will be used (1) or not (0) */
dnssd_set_airplay_features(dnssd, 27, (int) setup_legacy_pairing);
return 0;
@@ -1765,13 +1854,9 @@ static int start_raop_server (unsigned short display[5], unsigned short tcp[3],
raop_start(raop, &raop_port);
raop_set_port(raop, raop_port);
- if (tcp[2]) {
- airplay_port = tcp[2];
- } else {
- /* is there a problem if this coincides with a randomly-selected tcp raop_mirror_data port?
- * probably not, as the airplay port is only used for initial client contact */
- airplay_port = (raop_port != HIGHEST_PORT ? raop_port + 1 : raop_port - 1);
- }
+ /* use raop_port for airplay_port (instead of tcp[2]) */
+ airplay_port = raop_port;
+
if (dnssd) {
raop_set_dnssd(raop, dnssd);
} else {
From 23030f1b43a12adb9b4919a5e0b86eb7751cfd5c Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Tue, 21 May 2024 17:49:19 -0400
Subject: [PATCH 21/39] use improved http_request with
http_request_get_protocol Thanks to @shuax for finding this method
---
lib/http_request.c | 11 +++++++++++
lib/http_request.h | 1 +
lib/raop.c | 30 ++++++++++++++++++------------
3 files changed, 30 insertions(+), 12 deletions(-)
diff --git a/lib/http_request.c b/lib/http_request.c
index e259a60..15bf49d 100644
--- a/lib/http_request.c
+++ b/lib/http_request.c
@@ -29,6 +29,7 @@ struct http_request_s {
const char *method;
char *url;
+ char protocol[9];
char **headers;
int headers_size;
@@ -51,6 +52,9 @@ on_url(llhttp_t *parser, const char *at, size_t length)
request->url[urllen] = '\0';
strncat(request->url, at, length);
+
+ strncpy(request->protocol, at + length + 1, 8);
+
return 0;
}
@@ -230,6 +234,13 @@ http_request_get_url(http_request_t *request)
return request->url;
}
+const char *
+http_request_get_protocol(http_request_t *request)
+{
+ assert(request);
+ return request->protocol;
+}
+
const char *
http_request_get_header(http_request_t *request, const char *name)
{
diff --git a/lib/http_request.h b/lib/http_request.h
index e8ebfd7..d72a6f1 100644
--- a/lib/http_request.h
+++ b/lib/http_request.h
@@ -28,6 +28,7 @@ const char *http_request_get_error_name(http_request_t *request);
const char *http_request_get_error_description(http_request_t *request);
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_protocol(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);
diff --git a/lib/raop.c b/lib/raop.c
index 9a249bc..fa16cba 100644
--- a/lib/raop.c
+++ b/lib/raop.c
@@ -155,18 +155,18 @@ conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remot
static void
conn_request(void *ptr, http_request_t *request, http_response_t **response) {
- raop_conn_t *conn = ptr;
- const char *method;
- const char *url;
- const char *cseq;
char *response_data = NULL;
int response_datalen = 0;
- logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request");
- bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
+ raop_conn_t *conn = ptr;
- method = http_request_get_method(request);
- url = http_request_get_url(request);
- cseq = http_request_get_header(request, "CSeq");
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request");
+
+ const char *method = http_request_get_method(request);
+ const char *url = http_request_get_url(request);
+ const char *protocol = http_request_get_protocol(request);
+ const char *cseq = http_request_get_header(request, "CSeq");
+
+ bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
if (!conn->have_active_remote) {
const char *active_remote = http_request_get_header(request, "Active-Remote");
if (active_remote) {
@@ -178,10 +178,16 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
}
}
- if (!method || !cseq) {
+ if (!method) {
return;
}
- logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s %s RTSP/1.0", method, url);
+
+ /* this rejects unsupported messages from _airplay._tcp for video streaming protocol*/
+ if (!cseq) {
+ return;
+ }
+
+ logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s %s %s", method, url, protocol);
char *header_str= NULL;
http_request_get_header_string(request, &header_str);
if (header_str) {
@@ -215,7 +221,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
}
}
- *response = http_response_init("RTSP/1.0", 200, "OK");
+ *response = http_response_init(protocol, 200, "OK");
http_response_add_header(*response, "CSeq", cseq);
//http_response_add_header(*response, "Apple-Jack-Status", "connected; type=analog");
From 3579e8402307ff947088837917945a961fa05285 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Thu, 30 May 2024 13:08:42 -0400
Subject: [PATCH 22/39] cleaned up -nohold feature + rejection of new
connections when in use
(for compatibility with possible future video streaming additions)
---
lib/httpd.c | 70 +++++++++++++++++++++++++++++++++++++++--------------
lib/httpd.h | 19 ++++++++++-----
lib/raop.c | 30 +++++++++++++++++------
lib/raop.h | 2 +-
uxplay.cpp | 8 +++---
5 files changed, 93 insertions(+), 36 deletions(-)
diff --git a/lib/httpd.c b/lib/httpd.c
index a4adf21..854fed9 100644
--- a/lib/httpd.c
+++ b/lib/httpd.c
@@ -31,6 +31,7 @@ struct http_connection_s {
int socket_fd;
void *user_data;
+ connection_type_t type;
http_request_t *request;
};
typedef struct http_connection_s http_connection_t;
@@ -42,6 +43,7 @@ struct httpd_s {
int max_connections;
int open_connections;
http_connection_t *connections;
+ char nohold;
/* These variables only edited mutex locked */
int running;
@@ -54,14 +56,43 @@ struct httpd_s {
int server_fd6;
};
+int
+httpd_set_connection_type (httpd_t *httpd, void *user_data, connection_type_t type) {
+ for (int i = 0; i < httpd->max_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+ if (!connection->connected) {
+ continue;
+ }
+ if (connection->user_data == user_data) {
+ connection->type = type;
+ return i;
+ }
+ }
+ return -1;
+}
+
+int
+httpd_count_connection_type (httpd_t *httpd, connection_type_t type) {
+ int count = 0;
+ for (int i = 0; i < httpd->max_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+ if (!connection->connected) {
+ continue;
+ }
+ if (connection->type == type) {
+ count++;
+ }
+ }
+ return count;
+}
+
+#define MAX_CONNECTIONS 12 /* value used in AppleTV 3*/
httpd_t *
-httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections)
+httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int nohold)
{
httpd_t *httpd;
-
assert(logger);
assert(callbacks);
- assert(max_connections > 0);
/* Allocate the httpd_t structure */
httpd = calloc(1, sizeof(httpd_t));
@@ -69,8 +100,10 @@ httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections)
return NULL;
}
- httpd->max_connections = max_connections;
- httpd->connections = calloc(max_connections, sizeof(http_connection_t));
+
+ httpd->nohold = (nohold ? 1 : 0);
+ httpd->max_connections = MAX_CONNECTIONS;
+ httpd->connections = calloc(httpd->max_connections, sizeof(http_connection_t));
if (!httpd->connections) {
free(httpd);
return NULL;
@@ -111,6 +144,8 @@ httpd_remove_connection(httpd_t *httpd, http_connection_t *connection)
shutdown(connection->socket_fd, SHUT_WR);
closesocket(connection->socket_fd);
connection->connected = 0;
+ connection->user_data = NULL;
+ connection->type = CONNECTION_TYPE_UNKNOWN;
httpd->open_connections--;
}
@@ -131,7 +166,6 @@ httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len
logger_log(httpd->logger, LOGGER_INFO, "Max connections reached");
return -1;
}
-
user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len, zone_id);
if (!user_data) {
logger_log(httpd->logger, LOGGER_ERR, "Error initializing HTTP request handler");
@@ -142,6 +176,7 @@ httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len
httpd->connections[i].socket_fd = fd;
httpd->connections[i].connected = 1;
httpd->connections[i].user_data = user_data;
+ httpd->connections[i].type = CONNECTION_TYPE_UNKNOWN; //should not be necessary ...
return 0;
}
@@ -178,20 +213,20 @@ httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6)
remote = netutils_get_address(&remote_saddr, &remote_len, &remote_zone_id);
assert (local_zone_id == remote_zone_id);
-#ifdef NOHOLD
- /* remove existing connections to make way for new connections:
- * this will only occur if max_connections > 2 */
- if (httpd->open_connections >= 2) {
- logger_log(httpd->logger, LOGGER_INFO, "Destroying current connections to allow connection by new client");
- for (int i = 0; imax_connections; i++) {
- http_connection_t *connection = &httpd->connections[i];
- if (!connection->connected) {
- continue;
+ /* remove existing connections to make way for new connections, if http->nohold is set:
+ * this will only occur if open_connections >= 2 and a connection with CONNECTION_TYPE_RAOP already exists */
+ if (httpd->nohold && httpd->open_connections >= 2) {
+ if (httpd_count_connection_type(httpd, CONNECTION_TYPE_RAOP)) {
+ logger_log(httpd->logger, LOGGER_INFO, "Destroying current connections to allow connection by new client");
+ for (int i = 0; imax_connections; i++) {
+ http_connection_t *connection = &httpd->connections[i];
+ if (!connection->connected) {
+ continue;
+ }
+ httpd_remove_connection(httpd, connection);
}
- httpd_remove_connection(httpd, connection);
}
}
-#endif
ret = httpd_add_connection(httpd, fd, local, local_len, remote, remote_len, local_zone_id);
if (ret == -1) {
@@ -476,4 +511,3 @@ httpd_stop(httpd_t *httpd)
httpd->joined = 1;
MUTEX_UNLOCK(httpd->run_mutex);
}
-
diff --git a/lib/httpd.h b/lib/httpd.h
index a606a6a..3ebde2f 100644
--- a/lib/httpd.h
+++ b/lib/httpd.h
@@ -21,17 +21,24 @@
typedef struct httpd_s httpd_t;
+typedef enum connectype_type_e {
+ CONNECTION_TYPE_UNKNOWN,
+ CONNECTION_TYPE_RAOP
+} connection_type_t;
+
struct httpd_callbacks_s {
- void* opaque;
- void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote,
- int remotelen, unsigned int zone_id);
- void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response);
- void (*conn_destroy)(void *ptr);
+ void* opaque;
+ void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote,
+ int remotelen, unsigned int zone_id);
+ void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response);
+ void (*conn_destroy)(void *ptr);
};
typedef struct httpd_callbacks_s httpd_callbacks_t;
+int httpd_set_connection_type (httpd_t *http, void *user_data, connection_type_t type);
+int httpd_count_connection_type (httpd_t *http, connection_type_t type);
-httpd_t *httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections);
+httpd_t *httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int nohold);
int httpd_is_running(httpd_t *httpd);
diff --git a/lib/raop.c b/lib/raop.c
index fa16cba..cc3d3f9 100644
--- a/lib/raop.c
+++ b/lib/raop.c
@@ -90,6 +90,8 @@ struct raop_conn_s {
unsigned int zone_id;
+ connection_type_t connection_type;
+
bool have_active_remote;
};
typedef struct raop_conn_s raop_conn_t;
@@ -140,10 +142,12 @@ conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remot
memcpy(conn->remote, remote, remotelen);
conn->zone_id = zone_id;
-
+
conn->locallen = locallen;
conn->remotelen = remotelen;
+ conn->connection_type = CONNECTION_TYPE_UNKNOWN;
+
conn->have_active_remote = false;
if (raop->callbacks.conn_init) {
@@ -160,13 +164,27 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
raop_conn_t *conn = ptr;
logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request");
+ bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
const char *method = http_request_get_method(request);
const char *url = http_request_get_url(request);
const char *protocol = http_request_get_protocol(request);
const char *cseq = http_request_get_header(request, "CSeq");
- bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
+ if (conn->connection_type == CONNECTION_TYPE_UNKNOWN) {
+ if (httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_RAOP)) {
+ char ipaddr[40];
+ utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, ipaddr, (int) (sizeof(ipaddr)));
+ logger_log(conn->raop->logger, LOGGER_WARNING, "rejecting new connection request from %s", ipaddr);
+ *response = http_response_init("RTSP/1.0", 409, "Conflict: Server is connected to another client");
+ http_response_add_header(*response, "CSeq", cseq);
+ http_response_add_header(*response, "Server", "AirTunes/"GLOBAL_VERSION);
+ goto finish;
+ }
+ httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_RAOP);
+ conn->connection_type = CONNECTION_TYPE_RAOP;
+ }
+
if (!conn->have_active_remote) {
const char *active_remote = http_request_get_header(request, "Active-Remote");
if (active_remote) {
@@ -264,6 +282,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
if (handler != NULL) {
handler(conn, request, *response, &response_data, &response_datalen);
}
+ finish:;
http_response_finish(*response, response_data, response_datalen);
int len;
@@ -395,14 +414,11 @@ raop_init(raop_callbacks_t *callbacks) {
}
int
-raop_init2(raop_t *raop, int max_clients, const char *device_id, const char *keyfile) {
+raop_init2(raop_t *raop, int nohold, const char *device_id, const char *keyfile) {
pairing_t *pairing;
httpd_t *httpd;
httpd_callbacks_t httpd_cbs;
- assert(max_clients > 0);
- assert(max_clients < 100);
-
/* create a new public key for pairing */
int new_key;
pairing = pairing_init_generate(device_id, keyfile, &new_key);
@@ -433,7 +449,7 @@ raop_init2(raop_t *raop, int max_clients, const char *device_id, const char *key
httpd_cbs.conn_destroy = &conn_destroy;
/* Initialize the http daemon */
- httpd = httpd_init(raop->logger, &httpd_cbs, max_clients);
+ httpd = httpd_init(raop->logger, &httpd_cbs, nohold);
if (!httpd) {
logger_log(raop->logger, LOGGER_ERR, "failed to initialize http daemon");
pairing_destroy(pairing);
diff --git a/lib/raop.h b/lib/raop.h
index 43a7a4e..63a8ef0 100644
--- a/lib/raop.h
+++ b/lib/raop.h
@@ -69,7 +69,7 @@ raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const c
int remote_addr_len, unsigned short timing_rport, timing_protocol_t *time_protocol);
RAOP_API raop_t *raop_init(raop_callbacks_t *callbacks);
-RAOP_API int raop_init2(raop_t *raop, int max_clients, const char *device_id, const char *keyfile);
+RAOP_API int raop_init2(raop_t *raop, int nohold, const char *device_id, const char *keyfile);
RAOP_API void raop_set_log_level(raop_t *raop, int level);
RAOP_API void raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls);
RAOP_API int raop_set_plist(raop_t *raop, const char *plist_item, const int value);
diff --git a/uxplay.cpp b/uxplay.cpp
index 43e8a5d..fde2bd2 100644
--- a/uxplay.cpp
+++ b/uxplay.cpp
@@ -121,7 +121,7 @@ static unsigned short display[5] = {0}, tcp[3] = {0}, udp[3] = {0};
static bool debug_log = DEFAULT_DEBUG_LOG;
static int log_level = LOGGER_INFO;
static bool bt709_fix = false;
-static int max_connections = 2;
+static int nohold = 0;
static unsigned short raop_port;
static unsigned short airplay_port;
static uint64_t remote_clock_offset = 0;
@@ -1033,7 +1033,7 @@ static void parse_arguments (int argc, char *argv[]) {
} else if (arg == "-bt709") {
bt709_fix = true;
} else if (arg == "-nohold") {
- max_connections = 3;
+ nohold = 1;
} else if (arg == "-al") {
int n;
char *end;
@@ -1825,8 +1825,8 @@ static int start_raop_server (unsigned short display[5], unsigned short tcp[3],
}
raop_set_log_callback(raop, log_callback, NULL);
raop_set_log_level(raop, log_level);
- /* set max number of connections = 2 to protect against capture by new client */
- if (raop_init2(raop, max_connections, mac_address.c_str(), keyfile.c_str())){
+ /* set nohold = 1 to allow capture by new client */
+ if (raop_init2(raop, nohold, mac_address.c_str(), keyfile.c_str())){
LOGE("Error initializing raop (2)!");
free (raop);
return -1;
From dbcd157de40062cb27a8ab3cf9a568cafdea8a4a Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Fri, 31 May 2024 10:19:40 -0400
Subject: [PATCH 23/39] (cosmetic) rename some rtp_mirror functions for
consistency
---
lib/raop_handlers.h | 4 ++--
lib/raop_rtp_mirror.c | 4 ++--
lib/raop_rtp_mirror.h | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h
index b2ba5c9..c0d31dc 100644
--- a/lib/raop_handlers.h
+++ b/lib/raop_handlers.h
@@ -787,8 +787,8 @@ raop_handler_setup(raop_conn_t *conn,
" key and iv): %llu", stream_connection_id);
if (conn->raop_rtp_mirror) {
- raop_rtp_init_mirror_aes(conn->raop_rtp_mirror, &stream_connection_id);
- raop_rtp_start_mirror(conn->raop_rtp_mirror, &dport, conn->raop->clientFPSdata);
+ raop_rtp_mirror_init_aes(conn->raop_rtp_mirror, &stream_connection_id);
+ raop_rtp_mirror_start(conn->raop_rtp_mirror, &dport, conn->raop->clientFPSdata);
logger_log(conn->raop->logger, LOGGER_DEBUG, "Mirroring initialized successfully");
} else {
logger_log(conn->raop->logger, LOGGER_ERR, "Mirroring not initialized at SETUP, playing will fail!");
diff --git a/lib/raop_rtp_mirror.c b/lib/raop_rtp_mirror.c
index 4e0bc48..832fb04 100644
--- a/lib/raop_rtp_mirror.c
+++ b/lib/raop_rtp_mirror.c
@@ -165,7 +165,7 @@ raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *call
}
void
-raop_rtp_init_mirror_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t *streamConnectionID)
+raop_rtp_mirror_init_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t *streamConnectionID)
{
mirror_buffer_init_aes(raop_rtp_mirror->buffer, streamConnectionID);
}
@@ -729,7 +729,7 @@ raop_rtp_init_mirror_sockets(raop_rtp_mirror_t *raop_rtp_mirror, int use_ipv6)
}
void
-raop_rtp_start_mirror(raop_rtp_mirror_t *raop_rtp_mirror, unsigned short *mirror_data_lport,
+raop_rtp_mirror_start(raop_rtp_mirror_t *raop_rtp_mirror, unsigned short *mirror_data_lport,
uint8_t show_client_FPS_data)
{
logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "raop_rtp_mirror starting mirroring");
diff --git a/lib/raop_rtp_mirror.h b/lib/raop_rtp_mirror.h
index f0e9e56..2e7914d 100644
--- a/lib/raop_rtp_mirror.h
+++ b/lib/raop_rtp_mirror.h
@@ -27,8 +27,8 @@ 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 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, unsigned short *mirror_data_lport, uint8_t show_client_FPS_data);
+void raop_rtp_mirror_init_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t *streamConnectionID);
+void raop_rtp_mirror_start(raop_rtp_mirror_t *raop_rtp_mirror, unsigned short *mirror_data_lport, uint8_t show_client_FPS_data);
void raop_rtp_mirror_stop(raop_rtp_mirror_t *raop_rtp_mirror);
void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror);
#endif //RAOP_RTP_MIRROR_H
From 37807914a163fb7d23080c13d131a5ba0ce678b4 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Sun, 2 Jun 2024 18:48:05 -0400
Subject: [PATCH 24/39] (cosmetic) rearrangements in GET /info plist creation
---
lib/raop_handlers.h | 58 ++++++++++++++++++++++-----------------------
1 file changed, 28 insertions(+), 30 deletions(-)
diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h
index c0d31dc..a6a86bb 100644
--- a/lib/raop_handlers.h
+++ b/lib/raop_handlers.h
@@ -36,33 +36,37 @@ raop_handler_info(raop_conn_t *conn,
{
assert(conn->raop->dnssd);
- int airplay_txt_len = 0;
- const char *airplay_txt = dnssd_get_airplay_txt(conn->raop->dnssd, &airplay_txt_len);
-
- int name_len = 0;
- const char *name = dnssd_get_name(conn->raop->dnssd, &name_len);
+ plist_t res_node = plist_new_dict();
+ /* deviceID is the physical hardware address, and will not change */
int hw_addr_raw_len = 0;
const char *hw_addr_raw = dnssd_get_hw_addr(conn->raop->dnssd, &hw_addr_raw_len);
-
char *hw_addr = calloc(1, 3 * hw_addr_raw_len);
//int hw_addr_len =
utils_hwaddr_airplay(hw_addr, 3 * hw_addr_raw_len, hw_addr_raw, hw_addr_raw_len);
+ plist_t device_id_node = plist_new_string(hw_addr);
+ plist_dict_set_item(res_node, "deviceID", device_id_node);
+ /* Persistent Public Key */
int pk_len = 0;
char *pk = utils_parse_hex(conn->raop->pk_str, strlen(conn->raop->pk_str), &pk_len);
+ plist_t pk_node = plist_new_data(pk, pk_len);
+ plist_dict_set_item(res_node, "pk", pk_node);
- plist_t r_node = plist_new_dict();
-
+ /* airplay_txt is from the _airplay._tcp dnssd announuncement, may not be necessary */
+ int airplay_txt_len = 0;
+ const char *airplay_txt = dnssd_get_airplay_txt(conn->raop->dnssd, &airplay_txt_len);
plist_t txt_airplay_node = plist_new_data(airplay_txt, airplay_txt_len);
- plist_dict_set_item(r_node, "txtAirPlay", txt_airplay_node);
+ plist_dict_set_item(res_node, "txtAirPlay", txt_airplay_node);
uint64_t features = dnssd_get_airplay_features(conn->raop->dnssd);
plist_t features_node = plist_new_uint(features);
- plist_dict_set_item(r_node, "features", features_node);
+ plist_dict_set_item(res_node, "features", features_node);
+ int name_len = 0;
+ const char *name = dnssd_get_name(conn->raop->dnssd, &name_len);
plist_t name_node = plist_new_string(name);
- plist_dict_set_item(r_node, "name", name_node);
+ plist_dict_set_item(res_node, "name", name_node);
plist_t audio_formats_node = plist_new_array();
plist_t audio_format_0_node = plist_new_dict();
@@ -81,31 +85,25 @@ raop_handler_info(raop_conn_t *conn,
plist_dict_set_item(audio_format_1_node, "audioInputFormats", audio_format_1_audio_input_formats_node);
plist_dict_set_item(audio_format_1_node, "audioOutputFormats", audio_format_1_audio_output_formats_node);
plist_array_append_item(audio_formats_node, audio_format_1_node);
- plist_dict_set_item(r_node, "audioFormats", audio_formats_node);
+ plist_dict_set_item(res_node, "audioFormats", audio_formats_node);
plist_t pi_node = plist_new_string(AIRPLAY_PI);
- plist_dict_set_item(r_node, "pi", pi_node);
+ plist_dict_set_item(res_node, "pi", pi_node);
plist_t vv_node = plist_new_uint(strtol(AIRPLAY_VV, NULL, 10));
- plist_dict_set_item(r_node, "vv", vv_node);
+ plist_dict_set_item(res_node, "vv", vv_node);
plist_t status_flags_node = plist_new_uint(68);
- plist_dict_set_item(r_node, "statusFlags", status_flags_node);
+ plist_dict_set_item(res_node, "statusFlags", status_flags_node);
plist_t keep_alive_low_power_node = plist_new_uint(1);
- plist_dict_set_item(r_node, "keepAliveLowPower", keep_alive_low_power_node);
+ plist_dict_set_item(res_node, "keepAliveLowPower", keep_alive_low_power_node);
plist_t source_version_node = plist_new_string(GLOBAL_VERSION);
- plist_dict_set_item(r_node, "sourceVersion", source_version_node);
-
- plist_t pk_node = plist_new_data(pk, pk_len);
- plist_dict_set_item(r_node, "pk", pk_node);
+ plist_dict_set_item(res_node, "sourceVersion", source_version_node);
plist_t keep_alive_send_stats_as_body_node = plist_new_uint(1);
- plist_dict_set_item(r_node, "keepAliveSendStatsAsBody", keep_alive_send_stats_as_body_node);
-
- plist_t device_id_node = plist_new_string(hw_addr);
- plist_dict_set_item(r_node, "deviceID", device_id_node);
+ plist_dict_set_item(res_node, "keepAliveSendStatsAsBody", keep_alive_send_stats_as_body_node);
plist_t audio_latencies_node = plist_new_array();
plist_t audio_latencies_0_node = plist_new_dict();
@@ -128,13 +126,13 @@ raop_handler_info(raop_conn_t *conn,
plist_dict_set_item(audio_latencies_1_node, "audioType", audio_latencies_1_audio_type_node);
plist_dict_set_item(audio_latencies_1_node, "inputLatencyMicros", audio_latencies_1_input_latency_micros_node);
plist_array_append_item(audio_latencies_node, audio_latencies_1_node);
- plist_dict_set_item(r_node, "audioLatencies", audio_latencies_node);
+ plist_dict_set_item(res_node, "audioLatencies", audio_latencies_node);
plist_t model_node = plist_new_string(GLOBAL_MODEL);
- plist_dict_set_item(r_node, "model", model_node);
+ plist_dict_set_item(res_node, "model", model_node);
plist_t mac_address_node = plist_new_string(hw_addr);
- plist_dict_set_item(r_node, "macAddress", mac_address_node);
+ plist_dict_set_item(res_node, "macAddress", mac_address_node);
plist_t displays_node = plist_new_array();
plist_t displays_0_node = plist_new_dict();
@@ -164,10 +162,10 @@ raop_handler_info(raop_conn_t *conn,
plist_dict_set_item(displays_0_node, "overscanned", displays_0_overscanned_node);
plist_dict_set_item(displays_0_node, "features", displays_0_features);
plist_array_append_item(displays_node, displays_0_node);
- plist_dict_set_item(r_node, "displays", displays_node);
+ plist_dict_set_item(res_node, "displays", displays_node);
- plist_to_bin(r_node, response_data, (uint32_t *) response_datalen);
- plist_free(r_node);
+ plist_to_bin(res_node, response_data, (uint32_t *) response_datalen);
+ plist_free(res_node);
http_response_add_header(response, "Content-Type", "application/x-apple-binary-plist");
free(pk);
free(hw_addr);
From af195e3f2cd7c096420e0856b53b813bf5259103 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Mon, 3 Jun 2024 05:14:53 -0400
Subject: [PATCH 25/39] improved debug log on httpd: see ALL requests received
---
lib/httpd.c | 13 +++++++++++--
lib/raop.c | 1 -
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/lib/httpd.c b/lib/httpd.c
index 854fed9..aae0f10 100644
--- a/lib/httpd.c
+++ b/lib/httpd.c
@@ -19,6 +19,7 @@
#include
#include
#include
+#include
#include "httpd.h"
#include "netutils.h"
@@ -243,7 +244,8 @@ httpd_thread(void *arg)
httpd_t *httpd = arg;
char buffer[1024];
int i;
-
+ bool logger_debug = (logger_get_level(httpd->logger) >= LOGGER_DEBUG);
+
assert(httpd);
while (1) {
@@ -336,7 +338,7 @@ httpd_thread(void *arg)
assert(connection->request);
}
- logger_log(httpd->logger, LOGGER_DEBUG, "httpd receiving on socket %d", connection->socket_fd);
+ logger_log(httpd->logger, LOGGER_DEBUG, "httpd receiving on socket %d, connection %d", connection->socket_fd, i);
ret = recv(connection->socket_fd, buffer, sizeof(buffer), 0);
if (ret == 0) {
logger_log(httpd->logger, LOGGER_INFO, "Connection closed for socket %d", connection->socket_fd);
@@ -356,6 +358,13 @@ httpd_thread(void *arg)
if (http_request_is_complete(connection->request)) {
http_response_t *response = NULL;
// Callback the received data to raop
+ if (logger_debug) {
+ const char *method = http_request_get_method(connection->request);
+ const char *url = http_request_get_url(connection->request);
+ const char *protocol = http_request_get_protocol(connection->request);
+ logger_log(httpd->logger, LOGGER_INFO, "httpd request received on socket %d, connection %d, "
+ "method = %s, url = %s, protocol = %s", connection->socket_fd, i, method, url, protocol);
+ }
httpd->callbacks.conn_request(connection->user_data, connection->request, &response);
http_request_destroy(connection->request);
connection->request = NULL;
diff --git a/lib/raop.c b/lib/raop.c
index cc3d3f9..e94f116 100644
--- a/lib/raop.c
+++ b/lib/raop.c
@@ -163,7 +163,6 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
int response_datalen = 0;
raop_conn_t *conn = ptr;
- logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request");
bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
const char *method = http_request_get_method(request);
From c361cfb6375ad273ab9da0674afb2efdaace45e6 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Mon, 3 Jun 2024 05:54:04 -0400
Subject: [PATCH 26/39] clearer debug message when remote host closes tcp
socket for video
---
lib/raop_rtp_mirror.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/raop_rtp_mirror.c b/lib/raop_rtp_mirror.c
index 832fb04..141c65e 100644
--- a/lib/raop_rtp_mirror.c
+++ b/lib/raop_rtp_mirror.c
@@ -291,7 +291,7 @@ raop_rtp_mirror_thread(void *arg)
if (payload == NULL && ret == 0) {
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
- "raop_rtp_mirror tcp socket is closed, got %d bytes of 128 byte header",readstart);
+ "raop_rtp_mirror tcp socket was closed by client (recv returned 0); got %d bytes of 128 byte header",readstart);
FD_CLR(stream_fd, &rfds);
stream_fd = -1;
continue;
@@ -354,7 +354,7 @@ raop_rtp_mirror_thread(void *arg)
}
if (ret == 0) {
- logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror tcp socket is closed");
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror tcp socket was closed by client (recv returned 0)");
break;
} else if (ret == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) continue; // Timeouts can happen even if the connection is fine
@@ -699,7 +699,7 @@ raop_rtp_mirror_thread(void *arg)
}
static int
-raop_rtp_init_mirror_sockets(raop_rtp_mirror_t *raop_rtp_mirror, int use_ipv6)
+raop_rtp_mirror_init_socket(raop_rtp_mirror_t *raop_rtp_mirror, int use_ipv6)
{
assert(raop_rtp_mirror);
@@ -751,8 +751,8 @@ raop_rtp_mirror_start(raop_rtp_mirror_t *raop_rtp_mirror, unsigned short *mirror
//use_ipv6 = 0;
raop_rtp_mirror->mirror_data_lport = *mirror_data_lport;
- if (raop_rtp_init_mirror_sockets(raop_rtp_mirror, use_ipv6) < 0) {
- logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror initializing sockets failed");
+ if (raop_rtp_mirror_init_socket(raop_rtp_mirror, use_ipv6) < 0) {
+ logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror initializing socket failed");
MUTEX_UNLOCK(raop_rtp_mirror->run_mutex);
return;
}
From c9806d552758b440bce1c68719c355006a30a502 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Mon, 3 Jun 2024 08:19:02 -0400
Subject: [PATCH 27/39] httpd.c: support for Upgrade to reverse http prototcol
(PTTH)
---
lib/http_request.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/lib/http_request.c b/lib/http_request.c
index 15bf49d..5a6343a 100644
--- a/lib/http_request.c
+++ b/lib/http_request.c
@@ -187,8 +187,11 @@ http_request_add_data(http_request_t *request, const char *data, int datalen)
assert(request);
- ret = llhttp_execute(&request->parser,
- data, datalen);
+ ret = llhttp_execute(&request->parser, data, datalen);
+
+ /* support for "Upgrade" to reverse http ("PTTH/1.0") protocol */
+ llhttp_resume_after_upgrade(&request->parser);
+
return ret;
}
From e5f2f66f2a006b4bc65a9735ef9c6623c609e014 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Mon, 3 Jun 2024 19:25:57 -0400
Subject: [PATCH 28/39] raop_handlers.h: minor fix in debug output
---
lib/raop_handlers.h | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h
index a6a86bb..535d103 100644
--- a/lib/raop_handlers.h
+++ b/lib/raop_handlers.h
@@ -203,8 +203,8 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) {
- const char *request_data;
- int request_datalen;
+ const char *request_data = NULL;;
+ int request_datalen = 0;
bool data_is_plist = false;
bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG);
request_data = http_request_get_data(request, &request_datalen);
@@ -217,7 +217,8 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
free(header_str);
}
if (!data_is_plist) {
- logger_log(conn->raop->logger, LOGGER_INFO, "did not receive expected plist from client, request_datalen = %d");
+ logger_log(conn->raop->logger, LOGGER_INFO, "did not receive expected plist from client, request_datalen = %d",
+ request_datalen);
goto authentication_failed;
}
From c4fb40ee31b61b1b3b5ae406900af72edac2ca47 Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Tue, 4 Jun 2024 13:06:56 -0400
Subject: [PATCH 29/39] reorder position of CSeq and Server entries in Response
header
---
lib/raop.c | 12 ++++++------
lib/raop_handlers.h | 6 ------
2 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/lib/raop.c b/lib/raop.c
index e94f116..b388be8 100644
--- a/lib/raop.c
+++ b/lib/raop.c
@@ -175,9 +175,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
char ipaddr[40];
utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, ipaddr, (int) (sizeof(ipaddr)));
logger_log(conn->raop->logger, LOGGER_WARNING, "rejecting new connection request from %s", ipaddr);
- *response = http_response_init("RTSP/1.0", 409, "Conflict: Server is connected to another client");
- http_response_add_header(*response, "CSeq", cseq);
- http_response_add_header(*response, "Server", "AirTunes/"GLOBAL_VERSION);
+ *response = http_response_init(protocol, 409, "Conflict: Server is connected to another client");
goto finish;
}
httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_RAOP);
@@ -210,7 +208,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
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);
+ bool data_is_text = (strstr(header_str,"text/") != NULL);
free(header_str);
int request_datalen;
const char *request_data = http_request_get_data(request, &request_datalen);
@@ -240,9 +238,9 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
*response = http_response_init(protocol, 200, "OK");
- http_response_add_header(*response, "CSeq", cseq);
+
//http_response_add_header(*response, "Apple-Jack-Status", "connected; type=analog");
- http_response_add_header(*response, "Server", "AirTunes/"GLOBAL_VERSION);
+
logger_log(conn->raop->logger, LOGGER_DEBUG, "Handling request %s with URL %s", method, url);
raop_handler_t handler = NULL;
@@ -282,6 +280,8 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
handler(conn, request, *response, &response_data, &response_datalen);
}
finish:;
+ http_response_add_header(*response, "Server", "AirTunes/"GLOBAL_VERSION);
+ http_response_add_header(*response, "CSeq", cseq);
http_response_finish(*response, response_data, response_datalen);
int len;
diff --git a/lib/raop_handlers.h b/lib/raop_handlers.h
index 535d103..9942970 100644
--- a/lib/raop_handlers.h
+++ b/lib/raop_handlers.h
@@ -356,12 +356,6 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
authentication_failed:;
http_response_destroy(response);
response = http_response_init("RTSP/1.0", 470, "Client Authentication Failure");
- const char *cseq = http_request_get_header(request, "CSeq");
- http_response_add_header(response, "CSeq", cseq);
- http_response_add_header(response, "Server", "AirTunes/"GLOBAL_VERSION);
- *response_data = NULL;
- response_datalen = 0;
- return;
}
static void
From 53ac57dc428ba79d007e786dd4004f8b22a08ded Mon Sep 17 00:00:00 2001
From: "F. Duncanh"
Date: Wed, 10 Jul 2024 10:54:27 -0400
Subject: [PATCH 30/39] switch from static to dynamic linking of libplist in
Windows build
---
README.html | 5 ++---
README.md | 1 -
README.txt | 9 ++++-----
lib/CMakeLists.txt | 5 ++++-
4 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/README.html b/README.html
index cd745df..44d79a4 100644
--- a/README.html
+++ b/README.html
@@ -802,9 +802,8 @@ environment (this uses “ninja” in place of
install UxPlay dependencies (openssl is already installed with
MSYS2):
pacman -S mingw-w64-x86_64-libplist mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base
-Note that libplist will be linked statically to the uxplay
-executable. If you are trying a different Windows build system, MSVC
-versions of GStreamer for Windows are available from the If you are trying a different Windows build system, MSVC versions of
+GStreamer for Windows are available from the official GStreamer
site, but only the MinGW 64-bit build on MSYS2 has been
tested.