mirror of
https://github.com/morgan9e/UxPlay
synced 2026-04-14 00:04:13 +09:00
UxPlay 1.47. Added -FPSdata option, and revised video packet handling
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<h1 id="uxplay-1.46-airplayairplay-mirror-server-for-linux-macos-and-unix.">UxPlay 1.46: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.</h1>
|
||||
<h1 id="uxplay-1.47-airplayairplay-mirror-server-for-linux-macos-and-unix.">UxPlay 1.47: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.</h1>
|
||||
<p>Highlights:</p>
|
||||
<ul>
|
||||
<li>GPLv3, open source.</li>
|
||||
@@ -71,6 +71,7 @@
|
||||
<p><strong>-s wxh</strong> (e.g. -s 1920x1080 , which is the default ) sets the display resolution (width and height, in pixels). (This may be a request made to the AirPlay client, and perhaps will not be the final resolution you get.) w and h are whole numbers with four digits or less. Note that the <strong>height</strong> pixel size is the controlling one used by the client for determining the streaming format; the width is dynamically adjusted to the shape of the image (portrait or landscape format, depending on how an iPad is held, for example).</p>
|
||||
<p><strong>-s wxh@r</strong> As above, but also informs the AirPlay client about the screen refresh rate of the display. Default is r=60 (60 Hz); r must be a whole number less than 256.</p>
|
||||
<p><strong>-fps n</strong> sets a maximum frame rate (in frames per second) for the AirPlay client to stream video; n must be a whole number less than 256. (The client may choose to serve video at any frame rate lower than this; default is 30 fps.) A setting below 30 fps might be useful to reduce latency if you are running more than one instance of uxplay at the same time. <em>This setting is only an advisory to the client device, so setting a high value will not force a high framerate.</em> (You can test using “-vs fpsdisplaysink” to see what framerate is being received.)</p>
|
||||
<p><strong>-FPSdata</strong> Turns on monitoring of regular reports about video streaming performance that are sent by the client. These will be displayed in the terminal window if this option is used.</p>
|
||||
<p><strong>-o</strong> turns on an “overscanned” option for the display window. This reduces the image resolution by using some of the pixels requested by option -s wxh (or their default values 1920x1080) by adding an empty boundary frame of unused pixels (which would be lost in a full-screen display that overscans, and is not displayed by gstreamer). Recommendation: <strong>don’t use this option</strong> unless there is some special reason to use it.</p>
|
||||
<p><strong>-p</strong> 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, 7011. -p n (e.g. -p 35000) sets TCP and UDP ports n, n+1, n+2. -p n1,n2,n3 (comma-separated values) sets each port separately; -p n1,n2 sets ports n1,n2,n2+1. -p tcp n or -p udp n sets just the TCP or UDP ports. Ports must be in the range [1024-65535].</p>
|
||||
<p>If the -p option is not used, the ports are chosen dynamically (randomly), which will not work if a firewall is running.</p>
|
||||
@@ -110,6 +111,8 @@
|
||||
<p>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 <em>AirMyPC</em>, the protocol can be switched to the older version by the setting <code>OLD_PROTOCOL_CLIENT_USER_AGENT_LIST</code> 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.</p>
|
||||
<p>Note that Uxplay declares itself to be an AppleTV3,2 with a sourceVersion 220.68; this can also be changed in global.h. It is crucial for UxPlay to declare this old value of sourceVersion, as this prompts the Apple client to use a less-encrypted “legacy” protocol needed by third-generation Apple TV’s, which are 32-bit devices that cannot run modern tvOS; it is probably not necessary for UxPlay to claim to be such an old AppleTV model.</p>
|
||||
<h1 id="changelog">ChangeLog</h1>
|
||||
<p>1.47 2022-02-05 Added -FPSdata option to display (in the terminal) regular reports sent by the client about video streaming performance.<br />
|
||||
Internal cleanups of processing of video packets received from the client.</p>
|
||||
<p>1.46 2022-01-20 Restore pre-1.44 behavior (1.44 may have broken hardware acceleration): once again use decodebin in the video pipeline; introduce new option “-avdec” to force software h264 decoding by libav h264, if needed (to prevent selection of vaapisink by autovideosink). Update llhttp to v6.0.6. UxPlay now reports itself as AppleTV3,2. Restrict connections to one client at a time (second client must now wait for first client to disconnect).</p>
|
||||
<p>1.45 2022-01-10 New behavior: close video window when client requests “stop mirroring”. (A new “no close” option “-nc” is added for users who wish to retain previous behavior that does not close the video window).</p>
|
||||
<p>1.44 2021-12-13 Omit hash of aeskey with ecdh_secret for an AirMyPC client; make an internal rearrangement of where this hash is done. Fully report all initial communications between client and server in -d debug mode. Replace decodebin in GStreamer video pipeline by h264-specific elements.</p>
|
||||
|
||||
11
README.md
11
README.md
@@ -1,4 +1,4 @@
|
||||
# UxPlay 1.46: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.
|
||||
# UxPlay 1.47: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.
|
||||
|
||||
Highlights:
|
||||
|
||||
@@ -265,7 +265,11 @@ Options:
|
||||
the client device, so setting a high value will not force a high framerate._
|
||||
(You can test using "-vs fpsdisplaysink" to see what framerate is being
|
||||
received.)
|
||||
|
||||
|
||||
**-FPSdata** Turns on monitoring of regular reports about video streaming performance
|
||||
that are sent by the client. These will be displayed in the terminal window if this
|
||||
option is used.
|
||||
|
||||
**-o** turns on an "overscanned" option for the display window. This
|
||||
reduces the image resolution by using some of the pixels requested
|
||||
by option -s wxh (or their default values 1920x1080) by adding an empty
|
||||
@@ -458,6 +462,9 @@ devices that cannot run modern tvOS; it is probably
|
||||
not necessary for UxPlay to claim to be such an old AppleTV model.
|
||||
|
||||
# ChangeLog
|
||||
1.47 2022-02-05 Added -FPSdata option to display (in the terminal) regular reports sent by the client about video streaming performance.
|
||||
Internal cleanups of processing of video packets received from the client.
|
||||
|
||||
1.46 2022-01-20 Restore pre-1.44 behavior (1.44 may have broken hardware acceleration): once again use decodebin in the video pipeline;
|
||||
introduce new option "-avdec" to force software h264 decoding by libav h264, if needed (to prevent selection of
|
||||
vaapisink by autovideosink). Update llhttp to v6.0.6. UxPlay now reports itself as AppleTV3,2. Restrict connections
|
||||
|
||||
11
README.txt
11
README.txt
@@ -1,4 +1,4 @@
|
||||
UxPlay 1.46: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.
|
||||
UxPlay 1.47: AirPlay/AirPlay-Mirror server for Linux, macOS, and Unix.
|
||||
======================================================================
|
||||
|
||||
Highlights:
|
||||
@@ -348,6 +348,10 @@ time. *This setting is only an advisory to the client device, so setting
|
||||
a high value will not force a high framerate.* (You can test using "-vs
|
||||
fpsdisplaysink" to see what framerate is being received.)
|
||||
|
||||
**-FPSdata** Turns on monitoring of regular reports about video
|
||||
streaming performance that are sent by the client. These will be
|
||||
displayed in the terminal window if this option is used.
|
||||
|
||||
**-o** turns on an "overscanned" option for the display window. This
|
||||
reduces the image resolution by using some of the pixels requested by
|
||||
option -s wxh (or their default values 1920x1080) by adding an empty
|
||||
@@ -587,6 +591,11 @@ to be such an old AppleTV model.
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
1.47 2022-02-05 Added -FPSdata option to display (in the terminal)
|
||||
regular reports sent by the client about video streaming performance.\
|
||||
Internal cleanups of processing of video packets received from the
|
||||
client.
|
||||
|
||||
1.46 2022-01-20 Restore pre-1.44 behavior (1.44 may have broken hardware
|
||||
acceleration): once again use decodebin in the video pipeline; introduce
|
||||
new option "-avdec" to force software h264 decoding by libav h264, if
|
||||
|
||||
17
lib/raop.c
17
lib/raop.c
@@ -51,12 +51,15 @@ struct raop_s {
|
||||
unsigned short data_lport;
|
||||
unsigned short mirror_data_lport;
|
||||
|
||||
/* configurable plist items: width, height, refreshRate, maxFPS, overscanned */
|
||||
/* configurable plist items: width, height, refreshRate, maxFPS, overscanned *
|
||||
* also clientFPSdata, which controls whether video stream info received *
|
||||
* from the client is shown on terminal monitor. */
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t refreshRate;
|
||||
uint8_t maxFPS;
|
||||
uint8_t overscanned;
|
||||
uint8_t clientFPSdata;
|
||||
};
|
||||
|
||||
struct raop_conn_s {
|
||||
@@ -437,7 +440,10 @@ raop_init(int max_clients, raop_callbacks_t *callbacks) {
|
||||
raop->refreshRate = 60;
|
||||
raop->maxFPS = 30;
|
||||
raop->overscanned = 0;
|
||||
|
||||
|
||||
/* initialize switch for display of client's streaming data records */
|
||||
raop->clientFPSdata = 0;
|
||||
|
||||
return raop;
|
||||
}
|
||||
|
||||
@@ -487,8 +493,11 @@ int raop_set_plist(raop_t *raop, const char *plist_item, const int value) {
|
||||
raop->maxFPS = (uint8_t) value;
|
||||
if ((int) raop->maxFPS != value) retval = 1;
|
||||
} else if (strcmp(plist_item,"overscanned") == 0) {
|
||||
raop->overscanned = (uint8_t) (value ? 1 : 0);
|
||||
if ((int) raop->overscanned != value) retval = 1;
|
||||
raop->overscanned = (uint8_t) (value ? 1 : 0);
|
||||
if ((int) raop->overscanned != value) retval = 1;
|
||||
} else if (strcmp(plist_item,"clientFPSdata") == 0) {
|
||||
raop->clientFPSdata = (value ? 1 : 0);
|
||||
if ((int) raop->clientFPSdata != value) retval = 1;
|
||||
} else {
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
@@ -453,7 +453,7 @@ raop_handler_setup(raop_conn_t *conn,
|
||||
|
||||
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, use_udp, &dport);
|
||||
raop_rtp_start_mirror(conn->raop_rtp_mirror, use_udp, &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!");
|
||||
|
||||
@@ -263,7 +263,7 @@ raop_ntp_thread(void *arg)
|
||||
byteutils_put_ntp_timestamp(request, 24, send_time);
|
||||
int send_len = sendto(raop_ntp->tsock, (char *)request, sizeof(request), 0,
|
||||
(struct sockaddr *) &raop_ntp->remote_saddr, raop_ntp->remote_saddr_len);
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp send_len = %d", send_len);
|
||||
logger_log(raop_ntp->logger, LOGGER_DEBUG, "raop_ntp send_len = %d, now = %llu", send_len, send_time);
|
||||
if (send_len < 0) {
|
||||
logger_log(raop_ntp->logger, LOGGER_ERR, "raop_ntp error sending request");
|
||||
} else {
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
#include "byteutils.h"
|
||||
#include "mirror_buffer.h"
|
||||
#include "stream.h"
|
||||
//#include "utils.h"
|
||||
#include "utils.h"
|
||||
#include "plist/plist.h"
|
||||
|
||||
/* for MacOS, where SOL_TCP and TCP_KEEPIDLE are not defined */
|
||||
#if !defined(SOL_TCP) && defined(IPPROTO_TCP)
|
||||
@@ -79,6 +80,12 @@ struct raop_rtp_mirror_s {
|
||||
|
||||
unsigned short mirror_data_lport;
|
||||
|
||||
/* SPS and PPS */
|
||||
int sps_pps_len;
|
||||
unsigned char* sps_pps;
|
||||
|
||||
/* switch for displaying client FPS data */
|
||||
uint8_t show_client_FPS_data;
|
||||
};
|
||||
|
||||
static int
|
||||
@@ -123,7 +130,9 @@ raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *call
|
||||
}
|
||||
raop_rtp_mirror->logger = logger;
|
||||
raop_rtp_mirror->ntp = ntp;
|
||||
|
||||
raop_rtp_mirror->sps_pps_len = 0;
|
||||
raop_rtp_mirror->sps_pps = NULL;
|
||||
|
||||
memcpy(&raop_rtp_mirror->callbacks, callbacks, sizeof(raop_callbacks_t));
|
||||
raop_rtp_mirror->buffer = mirror_buffer_init(logger, aeskey);
|
||||
if (!raop_rtp_mirror->buffer) {
|
||||
@@ -166,6 +175,9 @@ raop_rtp_mirror_thread(void *arg)
|
||||
unsigned char* payload = NULL;
|
||||
unsigned int readstart = 0;
|
||||
bool conn_reset = false;
|
||||
uint64_t ntp_timestamp_nal = 0;
|
||||
uint64_t ntp_timestamp_raw = 0;
|
||||
|
||||
#ifdef DUMP_H264
|
||||
// C decrypted
|
||||
FILE* file = fopen("/home/pi/Airplay.h264", "wb");
|
||||
@@ -262,19 +274,15 @@ raop_rtp_mirror_thread(void *arg)
|
||||
continue;
|
||||
} else if (payload == NULL && ret == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) continue; // Timeouts can happen even if the connection is fine
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in header recv: %d", errno);
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in header recv: %d %s", errno, strerror(errno));
|
||||
if (errno == ECONNRESET) conn_reset = true;;
|
||||
break;
|
||||
}
|
||||
|
||||
//logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "video packet header: %s", utils_data_to_string(packet, 16, 16));
|
||||
|
||||
int payload_size = byteutils_get_int(packet, 0);
|
||||
unsigned short payload_type = byteutils_get_short(packet, 4) & 0xff;
|
||||
//unsigned short payload_type = byteutils_get_short(packet, 4) & 0xff;
|
||||
//unsigned short payload_option = byteutils_get_short(packet, 6);
|
||||
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "payload_size = %d, type = %d", payload_size, payload_type);
|
||||
|
||||
if (payload == NULL) {
|
||||
payload = malloc(payload_size);
|
||||
readstart = 0;
|
||||
@@ -292,19 +300,20 @@ raop_rtp_mirror_thread(void *arg)
|
||||
break;
|
||||
} else if (ret == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) continue; // Timeouts can happen even if the connection is fine
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in recv: %d", errno);
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "raop_rtp_mirror error in recv: %d %s", errno, strerror(errno));
|
||||
if (errno == ECONNRESET) conn_reset = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (payload_type == 0) {
|
||||
switch (packet[4]) {
|
||||
case 0x00:
|
||||
// Normal video data (VCL NAL)
|
||||
|
||||
// Conveniently, the video data is already stamped with the remote wall clock time,
|
||||
// so no additional clock syncing needed. The only thing odd here is that the video
|
||||
// ntp time stamps don't include the SECONDS_FROM_1900_TO_1970, so it's really just
|
||||
// counting micro seconds since last boot.
|
||||
uint64_t ntp_timestamp_raw = byteutils_get_long(packet, 8);
|
||||
ntp_timestamp_raw = byteutils_get_long(packet, 8);
|
||||
uint64_t ntp_timestamp_remote = raop_ntp_timestamp_to_micro_seconds(ntp_timestamp_raw, false);
|
||||
uint64_t ntp_timestamp = raop_ntp_convert_remote_time(raop_rtp_mirror->ntp, ntp_timestamp_remote);
|
||||
|
||||
@@ -316,9 +325,18 @@ raop_rtp_mirror_thread(void *arg)
|
||||
fwrite(payload, payload_size, 1, file_source);
|
||||
fwrite(&readstart, sizeof(readstart), 1, file_len);
|
||||
#endif
|
||||
|
||||
unsigned char* payload_out;
|
||||
unsigned char* payload_decrypted;
|
||||
bool prepend_sps_pps = ((packet[5] != 0x00) && (raop_rtp_mirror->sps_pps_len > 0));
|
||||
if (prepend_sps_pps) {
|
||||
payload_out = (unsigned char*) malloc(payload_size + raop_rtp_mirror->sps_pps_len);
|
||||
payload_decrypted = payload_out + raop_rtp_mirror->sps_pps_len;
|
||||
memcpy(payload_out, raop_rtp_mirror->sps_pps, raop_rtp_mirror->sps_pps_len);
|
||||
} else {
|
||||
payload_out = (unsigned char*) malloc(payload_size);
|
||||
payload_decrypted = payload_out;
|
||||
}
|
||||
// Decrypt data
|
||||
unsigned char* payload_decrypted = malloc(payload_size);
|
||||
mirror_buffer_decrypt(raop_rtp_mirror->buffer, payload, payload_decrypted, payload_size);
|
||||
|
||||
int nalu_size = 0;
|
||||
@@ -326,21 +344,23 @@ raop_rtp_mirror_thread(void *arg)
|
||||
|
||||
// It seems the AirPlay protocol prepends NALs with their size, which we're replacing with the 4-byte
|
||||
// start code for the NAL Byte-Stream Format.
|
||||
bool valid = true;
|
||||
bool valid_data = true;
|
||||
while (nalu_size < payload_size) {
|
||||
int nc_len = (payload_decrypted[nalu_size + 0] << 24) | (payload_decrypted[nalu_size + 1] << 16) |
|
||||
(payload_decrypted[nalu_size + 2] << 8) | (payload_decrypted[nalu_size + 3]);
|
||||
if (nc_len < 0 || nalu_size + 4 > payload_size) {
|
||||
valid = false;
|
||||
valid_data = false;
|
||||
break;
|
||||
}
|
||||
payload_decrypted[nalu_size + 0] = 0;
|
||||
payload_decrypted[nalu_size + 1] = 0;
|
||||
payload_decrypted[nalu_size + 2] = 0;
|
||||
payload_decrypted[nalu_size + 3] = 1;
|
||||
nalu_size += nc_len + 4;
|
||||
nalu_size += 4;
|
||||
nalus_count++;
|
||||
nalu_size += nc_len;
|
||||
}
|
||||
if (nalu_size != payload_size) valid_data = false;
|
||||
|
||||
// int nalu_type = payload[4] & 0x1f;
|
||||
// logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "nalutype = %d", nalu_type);
|
||||
@@ -350,17 +370,30 @@ raop_rtp_mirror_thread(void *arg)
|
||||
#ifdef DUMP_H264
|
||||
fwrite(payload_decrypted, payload_size, 1, file);
|
||||
#endif
|
||||
if (!valid || nalu_size != payload_size) payload_decrypted[0] = 1; /* mark as invalid */
|
||||
h264_decode_struct h264_data;
|
||||
h264_data.data_len = payload_size;
|
||||
h264_data.data = payload_decrypted;
|
||||
h264_data.frame_type = 1;
|
||||
h264_data.pts = ntp_timestamp;
|
||||
raop_rtp_mirror->callbacks.video_process(raop_rtp_mirror->callbacks.cls, raop_rtp_mirror->ntp, &h264_data);
|
||||
free(payload_decrypted);
|
||||
} else if ((payload_type & 255) == 1) {
|
||||
// The information in the payload contains an SPS and a PPS NAL
|
||||
|
||||
h264_decode_struct h264_data;
|
||||
h264_data.pts = ntp_timestamp;
|
||||
h264_data.frame_type = payload_decrypted[4] & 0x1f;
|
||||
if (h264_data.frame_type == 5) {
|
||||
assert(prepend_sps_pps);
|
||||
h264_data.data = payload_out;
|
||||
h264_data.data_len = payload_size + raop_rtp_mirror->sps_pps_len;
|
||||
if (ntp_timestamp_raw != ntp_timestamp_nal) {
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, "raop_rtp_mirror: prepended sps_pps timestamp does not match that of video payload");
|
||||
}
|
||||
} else {
|
||||
h264_data.data_len = payload_size;
|
||||
h264_data.data = payload_decrypted;
|
||||
}
|
||||
|
||||
|
||||
if (!valid_data) h264_data.data[0] = 1; /* mark video data as invalid h264 (failed decryption) */
|
||||
raop_rtp_mirror->callbacks.video_process(raop_rtp_mirror->callbacks.cls, raop_rtp_mirror->ntp, &h264_data);
|
||||
free(payload_out);
|
||||
break;
|
||||
case 0x01:
|
||||
// The information in the payload contains an SPS and a PPS NAL
|
||||
ntp_timestamp_nal = byteutils_get_long(packet, 8);
|
||||
float width_source = byteutils_get_float(packet, 40);
|
||||
float height_source = byteutils_get_float(packet, 44);
|
||||
float width = byteutils_get_float(packet, 56);
|
||||
@@ -391,32 +424,69 @@ raop_rtp_mirror_thread(void *arg)
|
||||
|
||||
if (h264.sps_size + h264.pps_size < 102400) {
|
||||
// Copy the sps and pps into a buffer to hand to the decoder
|
||||
int sps_pps_len = (h264.sps_size + h264.pps_size) + 8;
|
||||
unsigned char sps_pps[sps_pps_len];
|
||||
sps_pps[0] = 0;
|
||||
sps_pps[1] = 0;
|
||||
sps_pps[2] = 0;
|
||||
sps_pps[3] = 1;
|
||||
memcpy(sps_pps + 4, h264.sequence_parameter_set, h264.sps_size);
|
||||
sps_pps[h264.sps_size + 4] = 0;
|
||||
sps_pps[h264.sps_size + 5] = 0;
|
||||
sps_pps[h264.sps_size + 6] = 0;
|
||||
sps_pps[h264.sps_size + 7] = 1;
|
||||
memcpy(sps_pps + h264.sps_size + 8, h264.picture_parameter_set, h264.pps_size);
|
||||
raop_rtp_mirror->sps_pps_len = (h264.sps_size + h264.pps_size) + 8;
|
||||
if (raop_rtp_mirror->sps_pps) {
|
||||
free(raop_rtp_mirror->sps_pps);
|
||||
raop_rtp_mirror->sps_pps = NULL;
|
||||
}
|
||||
raop_rtp_mirror->sps_pps = (unsigned char*) malloc(raop_rtp_mirror->sps_pps_len);
|
||||
assert(raop_rtp_mirror->sps_pps);
|
||||
raop_rtp_mirror->sps_pps[0] = 0;
|
||||
raop_rtp_mirror->sps_pps[1] = 0;
|
||||
raop_rtp_mirror->sps_pps[2] = 0;
|
||||
raop_rtp_mirror->sps_pps[3] = 1;
|
||||
memcpy(raop_rtp_mirror->sps_pps + 4, h264.sequence_parameter_set, h264.sps_size);
|
||||
raop_rtp_mirror->sps_pps[h264.sps_size + 4] = 0;
|
||||
raop_rtp_mirror->sps_pps[h264.sps_size + 5] = 0;
|
||||
raop_rtp_mirror->sps_pps[h264.sps_size + 6] = 0;
|
||||
raop_rtp_mirror->sps_pps[h264.sps_size + 7] = 1;
|
||||
memcpy(raop_rtp_mirror->sps_pps + h264.sps_size + 8, h264.picture_parameter_set, h264.pps_size);
|
||||
|
||||
#ifdef DUMP_H264
|
||||
fwrite(sps_pps, sps_pps_len, 1, file);
|
||||
fwrite(raop_rtp_mirror->sps_pps, raop_rtp_mirror->sps_pps_len, 1, file);
|
||||
#endif
|
||||
|
||||
h264_decode_struct h264_data;
|
||||
h264_data.data_len = sps_pps_len;
|
||||
h264_data.data = sps_pps;
|
||||
h264_data.frame_type = 0;
|
||||
h264_data.pts = 0;
|
||||
raop_rtp_mirror->callbacks.video_process(raop_rtp_mirror->callbacks.cls, raop_rtp_mirror->ntp, &h264_data);
|
||||
}
|
||||
free(h264.picture_parameter_set);
|
||||
free(h264.sequence_parameter_set);
|
||||
break;
|
||||
case 0x05:
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "\nReceived video streaming performance info packet from client");
|
||||
/* payloads with packet[4] = 0x05 have no timestamp, and carry video info from the client as a binary plist *
|
||||
* Sometimes (e.g, when the client has a locked screen), there is a 25kB trailer attached to the packet. *
|
||||
* This 25000 Byte trailer with unidentified content seems to be the same data each time it is sent. */
|
||||
|
||||
if (payload_size) {
|
||||
//char *str = utils_data_to_string(packet, 128, 16);
|
||||
//logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, "type 5 video packet header:\n%s", str);
|
||||
//free (str);
|
||||
|
||||
int plist_size = payload_size;
|
||||
if (payload_size > 25000) {
|
||||
plist_size = payload_size - 25000;
|
||||
char *str = utils_data_to_string(payload + plist_size, 16, 16);
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "video_info packet had 25kB trailer; first 16 bytes are:\n%s", str);
|
||||
free(str);
|
||||
}
|
||||
if (plist_size) {
|
||||
char *plist_xml;
|
||||
uint32_t plist_len;
|
||||
plist_t root_node = NULL;
|
||||
plist_from_bin((char *) payload, plist_size, &root_node);
|
||||
plist_to_xml(root_node, &plist_xml, &plist_len);
|
||||
switch (raop_rtp_mirror->show_client_FPS_data) {
|
||||
case 1:
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "%s", plist_xml);
|
||||
break;
|
||||
default:
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "%s", plist_xml);
|
||||
break;
|
||||
}
|
||||
free(plist_xml);
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
free(payload);
|
||||
@@ -432,7 +502,7 @@ raop_rtp_mirror_thread(void *arg)
|
||||
}
|
||||
|
||||
#ifdef DUMP_H264
|
||||
fclose(file);
|
||||
fclose(file);
|
||||
fclose(file_source);
|
||||
fclose(file_len);
|
||||
#endif
|
||||
@@ -454,13 +524,14 @@ static int
|
||||
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, int use_udp, unsigned short *mirror_data_lport)
|
||||
raop_rtp_start_mirror(raop_rtp_mirror_t *raop_rtp_mirror, int use_udp, unsigned short *mirror_data_lport, uint8_t show_client_FPS_data)
|
||||
{
|
||||
logger_log(raop_rtp_mirror->logger, LOGGER_INFO, "raop_rtp_mirror starting mirroring");
|
||||
int use_ipv6 = 0;
|
||||
|
||||
assert(raop_rtp_mirror);
|
||||
assert(mirror_data_lport);
|
||||
raop_rtp_mirror->show_client_FPS_data = show_client_FPS_data;
|
||||
|
||||
MUTEX_LOCK(raop_rtp_mirror->run_mutex);
|
||||
if (raop_rtp_mirror->running || !raop_rtp_mirror->joined) {
|
||||
@@ -521,7 +592,10 @@ void raop_rtp_mirror_destroy(raop_rtp_mirror_t *raop_rtp_mirror) {
|
||||
raop_rtp_mirror_stop(raop_rtp_mirror);
|
||||
MUTEX_DESTROY(raop_rtp_mirror->run_mutex);
|
||||
mirror_buffer_destroy(raop_rtp_mirror->buffer);
|
||||
free(raop_rtp_mirror);
|
||||
if (raop_rtp_mirror->sps_pps) {
|
||||
free(raop_rtp_mirror->sps_pps);
|
||||
}
|
||||
free(raop_rtp_mirror);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ typedef struct h264codec_s h264codec_t;
|
||||
raop_rtp_mirror_t *raop_rtp_mirror_init(logger_t *logger, raop_callbacks_t *callbacks, raop_ntp_t *ntp,
|
||||
const unsigned char *remote, int remotelen, const unsigned char *aeskey);
|
||||
void raop_rtp_init_mirror_aes(raop_rtp_mirror_t *raop_rtp_mirror, uint64_t *streamConnectionID);
|
||||
void raop_rtp_start_mirror(raop_rtp_mirror_t *raop_rtp_mirror, int use_udp, unsigned short *mirror_data_lport);
|
||||
void raop_rtp_start_mirror(raop_rtp_mirror_t *raop_rtp_mirror, int use_udp, 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
|
||||
|
||||
6
uxplay.1
6
uxplay.1
@@ -1,11 +1,11 @@
|
||||
.TH UXPLAY "1" "January 2022" "1.46" "User Commands"
|
||||
.TH UXPLAY "1" "February 2022" "1.47" "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.46: An open\-source AirPlay mirroring server based on RPiPlay
|
||||
UxPlay 1.47: An open\-source AirPlay mirroring server based on RPiPlay
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B
|
||||
@@ -57,6 +57,8 @@ UxPlay 1.46: An open\-source AirPlay mirroring server based on RPiPlay
|
||||
.TP
|
||||
\fB\-nc\fR Do not close video window when client stops mirroring
|
||||
.TP
|
||||
\fB\-FPSdata\fR Show video-streaming performance reports sent by client.
|
||||
.TP
|
||||
\fB\-d\fR Enable debug logging
|
||||
.TP
|
||||
\fB\-v\fR or \fB\-h\fR Displays this help and version information
|
||||
|
||||
15
uxplay.cpp
15
uxplay.cpp
@@ -44,7 +44,7 @@
|
||||
#include "renderers/video_renderer.h"
|
||||
#include "renderers/audio_renderer.h"
|
||||
|
||||
#define VERSION "1.46"
|
||||
#define VERSION "1.47"
|
||||
|
||||
#define DEFAULT_NAME "UxPlay"
|
||||
#define DEFAULT_DEBUG_LOG false
|
||||
@@ -76,6 +76,8 @@ static std::string audiosink = "autoaudiosink";
|
||||
static bool use_audio = true;
|
||||
static bool previous_no_close_behavior = false;
|
||||
static std::string decoder = "decodebin";
|
||||
static bool show_client_FPS_data = true;
|
||||
|
||||
|
||||
static gboolean connection_callback (gpointer loop){
|
||||
if (!connections_stopped) {
|
||||
@@ -229,7 +231,8 @@ static void print_info (char *name) {
|
||||
printf("-as Choose the GStreamer audiosink; default \"autoaudiosink\"\n");
|
||||
printf(" choices: pulsesink,alsasink,osssink,oss4sink,osxaudiosink,etc.\n");
|
||||
printf("-as 0 (or -a) Turn audio off, streamed video only\n");
|
||||
printf("-nc do not close video window when client stops mirroring\n");
|
||||
printf("-nc do Not Close video window when client stops mirroring\n");
|
||||
printf("-FPSdata Show video-streaming performance reports sent by client.\n");
|
||||
printf("-d Enable debug logging\n");
|
||||
printf("-v or -h Displays this help and version information\n");
|
||||
}
|
||||
@@ -451,7 +454,9 @@ int main (int argc, char *argv[]) {
|
||||
} else if (arg == "-avdec") {
|
||||
decoder.erase();
|
||||
decoder = "h264parse ! avdec_h264";
|
||||
} else {
|
||||
} else if (arg == "-FPSdata") {
|
||||
show_client_FPS_data = true;
|
||||
} else {
|
||||
LOGE("unknown option %s, stopping\n",argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
@@ -678,7 +683,9 @@ int start_raop_server (std::vector<char> hw_addr, std::string name, unsigned sho
|
||||
if (display[2]) raop_set_plist(raop, "refreshRate", (int) display[2]);
|
||||
if (display[3]) raop_set_plist(raop, "maxFPS", (int) display[3]);
|
||||
if (display[4]) raop_set_plist(raop, "overscanned", (int) display[4]);
|
||||
|
||||
|
||||
if (show_client_FPS_data) raop_set_plist(raop, "clientFPSdata", 1);
|
||||
|
||||
/* network port selection (ports listed as "0" will be dynamically assigned) */
|
||||
raop_set_tcp_ports(raop, tcp);
|
||||
raop_set_udp_ports(raop, udp);
|
||||
|
||||
Reference in New Issue
Block a user