fixed incorrect framerate and missing maxfps plist settings in raop_handler.h

and added  option to set these in the API through raop_set_display
(renamed from raop_set_display_size)..   Updated uxplay.cpp to include these
new options.   Moved version slightly to UxPlay-1.31

tested: confirmed to work using gstreamer videosink "fpsdisplaysink" to
explicitly show reduction of streaming fps when set below 30 fps.
This commit is contained in:
fduncanh
2021-08-09 18:37:58 -04:00
parent 00865c9023
commit cdcfb88510
6 changed files with 112 additions and 58 deletions

View File

@@ -14,10 +14,7 @@ include_directories(${X11_INCLUDE_DIR})
endif (ZOOMFIX)
if (NO_AVAHI_FIX)
else()
add_definitions(-DSUPPRESS_AVAHI_COMPAT_WARNING)
endif(NO_AVAHI_FIX)
add_subdirectory(lib/llhttp)
add_subdirectory(lib/playfair)

View File

@@ -77,7 +77,21 @@ AirPlay services to your iPad, iPhone etc.
**-s wxh** (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).
be the final resolution you get.) w and h are whole numbers with four
digits or less.
**-s wxh@r** As above, but also informs the AirPlay client about the screen
refresh rate of the display. Default is r=60 (60 Hz); r is a whole number
with three digits or less.
**-fps n ** sets a maximum frame rate (in frames per second) for the AirPlay
client to stream video; n must be a whole number with 3 digits or less.
(The client may choose to serve video at any frame rate lower
than this; default is 30 fps.) Values greater than the display
refresh rate are ignored, and replaced by the refresh rate. 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.
**-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
@@ -104,16 +118,16 @@ Also: image transforms that had been added to RPiPlay have been ported to UxPlay
**-r {R|L}** 90 degree Right (clockwise) or Left (counter-clockwise)
rotations; these are carried out after any **-f** transforms.
# New features available: (v 1.3 2021-08)
# New features available: (v 1.31 2021-08-09)
1. Updates of the RAOP (AirPlay protocol, not AirPlay 2) collection of codes maintained
1. Updates of the RAOP (AirPlay protocol) collection of codes maintained
at https://github.com/FD-/RPiPlay.git so it is current as of 2021-08-01,
adding all changes since the original release of UxPlay by antimof.
This involved crypto updates, replacement
of the included plist library by the system-installed version, and a change
over to a library llhttp for http parsing.
2. Added the -s, -p, -m, -r and -f options.
2. Added the -s, -p, -m, -r, -f, and -fps options.
3. If "cmake -DZOOMFIX=ON .." is run before compiling,
the mirrored window is now visible to screen-sharing applications such as
@@ -131,9 +145,10 @@ The program uxplay terminates when Ctrl-C is typed in the terminal window.
5. In principle, multiple instances of uxplay can be run simultaneously
using the **-m** (generate random MAC address) option to give each a
different ("local" as opposed to "universal") MAC address.
If the **-p** option is used, they also need separate network port choices.
If the **-p [n]** option is used, they also need separate network port choices.
(However, there may be a large latency, and running two instances of uxplay
simultaneously on the same computer may not be very useful.)
simultaneously on the same computer may not be very usefu;l using -fps option
to force streaming framerates below 30fps could be useful.)
6. Without the **-p** [n] option, uxplay makes a random dynamic assignment of
network ports. This will not work if most ports are closed by a firewall.
@@ -148,14 +163,16 @@ with 4 or less digits. It seems that the width and height may be negotiated
with the AirPlay client, so this may not be the actual screen geometry that
displays.
8. The title on the GStreamer display window is now is the Airplay server name
8. The title on the GStreamer display window is now is the AirPlay server name
(default "UxPlay", but can be changed with option **-n**), rather than the program
name "uxplay" (note the difference in capitalization).
9. The avahi_compat "nag" warning on startup is suppressed, by placing
"AVAHI_COMPAT_NOWARN=1" into the runtime environment when uxplay starts.
(This uses a call to putenv(), If for any reason you dont want this fix,
run cmake as "cmake -DNO_AVAHI_FIX=ON -DZOOMFIX=ON .. ").
(This uses a call to putenv() in a form that is believed to be safe against
memory leaks, at least in modern Linux; if for any reason you don't want
this fix, comment out the line in CMakeLists.txt that activates it when uxplay
is compiled.)
# Disclaimer

View File

@@ -52,7 +52,8 @@ struct raop_s {
unsigned short display_width;
unsigned short display_height;
unsigned short display_refresh_rate;
unsigned short display_max_fps;
};
struct raop_conn_s {
@@ -319,9 +320,11 @@ raop_init(int max_clients, raop_callbacks_t *callbacks) {
raop->data_lport = 0;
raop->mirror_data_lport = 0;
/* initialize display width, height */
/* initialize display width, height, refresh_rate, max_fps */
raop->display_width = 1920;
raop->display_height = 1080;
raop->display_refresh_rate = 60;
raop->display_max_fps = 30;
return raop;
}
@@ -354,11 +357,17 @@ raop_set_log_level(raop_t *raop, int level) {
logger_set_level(raop->logger, level);
}
void raop_set_display_size(raop_t *raop, unsigned short width, unsigned short height){
assert(raop);
void raop_set_display(raop_t *raop, unsigned short width, unsigned short height,
unsigned short refresh_rate, unsigned short max_fps){
assert(raop);
if (width) raop->display_width = width;
if (height) raop->display_height = height;
if (width) raop->display_width = width;
if (height) raop->display_height = height;
if (refresh_rate) raop->display_refresh_rate = refresh_rate;
if (max_fps) raop->display_max_fps = max_fps;
if (raop->display_max_fps > raop->display_refresh_rate) {
raop->display_max_fps = raop->display_refresh_rate;
}
}
void

View File

@@ -54,7 +54,8 @@ RAOP_API raop_t *raop_init(int max_clients, raop_callbacks_t *callbacks);
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 void raop_set_display_size(raop_t *raop, unsigned short width, unsigned short height);
RAOP_API void raop_set_display(raop_t *raop, unsigned short width, unsigned short height,
unsigned short refresh_rate, unsigned short max_fps);
RAOP_API void raop_set_port(raop_t *raop, unsigned short port);
RAOP_API void raop_set_udp_ports(raop_t *raop, unsigned short port[3]);
RAOP_API void raop_set_tcp_ports(raop_t *raop, unsigned short port[2]);

View File

@@ -60,16 +60,16 @@ raop_handler_info(raop_conn_t *conn,
plist_t audio_formats_node = plist_new_array();
plist_t audio_format_0_node = plist_new_dict();
plist_t audio_format_0_type_node = plist_new_uint(100);
plist_t audio_format_0_audio_input_formats_node = plist_new_uint(67108860);
plist_t audio_format_0_audio_output_formats_node = plist_new_uint(67108860);
plist_t audio_format_0_audio_input_formats_node = plist_new_uint(0x3fffffc);
plist_t audio_format_0_audio_output_formats_node = plist_new_uint(0x3fffffc);
plist_dict_set_item(audio_format_0_node, "type", audio_format_0_type_node);
plist_dict_set_item(audio_format_0_node, "audioInputFormats", audio_format_0_audio_input_formats_node);
plist_dict_set_item(audio_format_0_node, "audioOutputFormats", audio_format_0_audio_output_formats_node);
plist_array_append_item(audio_formats_node, audio_format_0_node);
plist_t audio_format_1_node = plist_new_dict();
plist_t audio_format_1_type_node = plist_new_uint(101);
plist_t audio_format_1_audio_input_formats_node = plist_new_uint(67108860);
plist_t audio_format_1_audio_output_formats_node = plist_new_uint(67108860);
plist_t audio_format_1_audio_input_formats_node = plist_new_uint(0x3fffffc);
plist_t audio_format_1_audio_output_formats_node = plist_new_uint(0x3fffffc);
plist_dict_set_item(audio_format_1_node, "type", audio_format_1_type_node);
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);
@@ -102,20 +102,20 @@ raop_handler_info(raop_conn_t *conn,
plist_t audio_latencies_node = plist_new_array();
plist_t audio_latencies_0_node = plist_new_dict();
plist_t audio_latencies_0_output_latency_micros_node = plist_new_uint(0);
plist_t audio_latencies_0_output_latency_micros_node = plist_new_bool(0);
plist_t audio_latencies_0_type_node = plist_new_uint(100);
plist_t audio_latencies_0_audio_type_node = plist_new_string("default");
plist_t audio_latencies_0_input_latency_micros_node = plist_new_uint(0);
plist_t audio_latencies_0_input_latency_micros_node = plist_new_bool(0);
plist_dict_set_item(audio_latencies_0_node, "outputLatencyMicros", audio_latencies_0_output_latency_micros_node);
plist_dict_set_item(audio_latencies_0_node, "type", audio_latencies_0_type_node);
plist_dict_set_item(audio_latencies_0_node, "audioType", audio_latencies_0_audio_type_node);
plist_dict_set_item(audio_latencies_0_node, "inputLatencyMicros", audio_latencies_0_input_latency_micros_node);
plist_array_append_item(audio_latencies_node, audio_latencies_0_node);
plist_t audio_latencies_1_node = plist_new_dict();
plist_t audio_latencies_1_output_latency_micros_node = plist_new_uint(0);
plist_t audio_latencies_1_output_latency_micros_node = plist_new_bool(0);
plist_t audio_latencies_1_type_node = plist_new_uint(101);
plist_t audio_latencies_1_audio_type_node = plist_new_string("default");
plist_t audio_latencies_1_input_latency_micros_node = plist_new_uint(0);
plist_t audio_latencies_1_input_latency_micros_node = plist_new_bool(0);
plist_dict_set_item(audio_latencies_1_node, "outputLatencyMicros", audio_latencies_1_output_latency_micros_node);
plist_dict_set_item(audio_latencies_1_node, "type", audio_latencies_1_type_node);
plist_dict_set_item(audio_latencies_1_node, "audioType", audio_latencies_1_audio_type_node);
@@ -139,7 +139,8 @@ raop_handler_info(raop_conn_t *conn,
plist_t displays_0_width_pixels_node = plist_new_uint(conn->raop->display_width);
plist_t displays_0_height_pixels_node = plist_new_uint(conn->raop->display_height);
plist_t displays_0_rotation_node = plist_new_bool(0);
plist_t displays_0_refresh_rate_node = plist_new_real(1.0 / 60.0);
plist_t displays_0_refresh_rate_node = plist_new_uint(conn->raop->display_refresh_rate);
plist_t displays_0_max_fps_node = plist_new_uint(conn->raop->display_max_fps);
plist_t displays_0_overscanned_node = plist_new_bool(1);
plist_t displays_0_features = plist_new_uint(14);
@@ -152,6 +153,7 @@ raop_handler_info(raop_conn_t *conn,
plist_dict_set_item(displays_0_node, "heightPixels", displays_0_height_pixels_node);
plist_dict_set_item(displays_0_node, "rotation", displays_0_rotation_node);
plist_dict_set_item(displays_0_node, "refreshRate", displays_0_refresh_rate_node);
plist_dict_set_item(displays_0_node, "maxFPS", displays_0_max_fps_node);
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);

View File

@@ -33,7 +33,7 @@
#include "renderers/video_renderer.h"
#include "renderers/audio_renderer.h"
#define VERSION "1.3"
#define VERSION "1.31"
#define DEFAULT_NAME "UxPlay"
#define DEFAULT_DEBUG_LOG false
@@ -41,7 +41,7 @@
#define HIGHEST_PORT 65535
static int start_server (std::vector<char> hw_addr, std::string name, unsigned short display_size[2],
static int start_server (std::vector<char> hw_addr, std::string name, unsigned short display[4],
unsigned short tcp[2], unsigned short udp[3], videoflip_t videoflip[2],
bool use_audio, bool debug_log);
@@ -116,7 +116,8 @@ static void print_info (char *name) {
printf("Usage: %s [-n name] [-s wxh] [-p [n]]\n", name);
printf("Options:\n");
printf("-n name Specify the network name of the AirPlay server\n");
printf("-s wxh Set display resolution: width w height h default 1920x1080\n");
printf("-s wxh[@r]Set display resolution [refresh_rate] default 1920x1080@60\n");
printf("-fps n Set maximum streaming fps, default 30 \n");
printf("-f {H|V|I}Horizontal|Vertical flip, or both=Inversion=rotate 180 deg\n");
printf("-r {R|L} rotate 90 degrees Right (cw) or Left (ccw)\n");
printf("-p n Use fixed UDP+TCP network ports n:n+1:n+2. (n>1023)\n");
@@ -127,9 +128,16 @@ static void print_info (char *name) {
printf("-v/-h Displays this help and version information\n");
}
static bool get_display_size (char *str, unsigned short *w, unsigned short *h) {
// assume str = wxh is valid if w and h are positive decimal integers with less than 5 digits.
static bool get_display_settings (char *str, unsigned short *w, unsigned short *h, unsigned short *r) {
// assume str = wxh@r is valid if w and h are positive decimal integers
// with no more than 5 digits, r no more than 3 digits.
char *str1 = strchr(str,'x');
char *str2 = strchr(str1,'@');
if (str2) {
if (strlen(str2) == 0) return false;
str2[0] = '\0'; str2++;
if (strlen(str2) > 3 || str2[0] == '-') return false;
}
if (str1 == NULL) return false;
str1[0] = '\0'; str1++;
if (str1[0] == '-') return false; // first character of str is never '-'
@@ -139,6 +147,18 @@ static bool get_display_size (char *str, unsigned short *w, unsigned short *h) {
if (*end || *w == 0) return false;
*h = (unsigned short) strtoul(str1, &end, 10);
if (*end || *h == 0) return false;
if (!str2) return true;;
*r = (unsigned short) strtoul(str2, &end, 10);
if (*end || *r == 0) return false;
return true;
}
static bool get_fps (char *str, unsigned short *n) {
if (strlen(str) > 3) return false;
char *end;
unsigned long l;
*n = (unsigned short) (l = strtoul(str, &end, 10)); // first character of str is never '-'
if (*end || !l) return false;
return true;
}
@@ -185,6 +205,14 @@ static bool get_videorotate (char *str, videoflip_t *videoflip) {
return true;
}
bool option_has_value(int i, int argc, char *argv[]) {
if (i >= argc - 1 || argv[i + 1][0] == '-') {
fprintf(stderr,"invalid \"%s\" had no argument\n",argv[i]);
return false;
}
return true;
}
int main (int argc, char *argv[]) {
init_signals();
@@ -193,7 +221,7 @@ int main (int argc, char *argv[]) {
bool use_audio = true;
bool use_random_hw_addr = false;
bool debug_log = DEFAULT_DEBUG_LOG;
unsigned short display_size[2] = {0}, tcp[2] = {0}, udp[3] = {0};
unsigned short display[4] = {0}, tcp[2] = {0}, udp[3] = {0};
videoflip_t videoflip[2] = { NONE , NONE };
#ifdef SUPPRESS_AVAHI_COMPAT_WARNING
@@ -207,40 +235,38 @@ int main (int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
std::string arg(argv[i]);
if (arg == "-n") {
if (i == argc - 1) continue;
if (!option_has_value(i, argc, argv)) exit(1);
server_name = std::string(argv[++i]);
} else if (arg == "-s") {
if (i == argc - 1 || argv[i + 1][0] == '-') {
fprintf(stderr,"invalid \"-s\" had no argument\n");
exit(1);
}
if (!option_has_value(i, argc, argv)) exit(1);
std::string value(argv[++i]);
if (!get_display_size(argv[i], &display_size[0], &display_size[1])) {
fprintf(stderr, "invalid \"-s %s\"; default is \"-s 1920x1080\" (< 5 digits)\n",
if (!get_display_settings(argv[i], &display[0], &display[1],&display[2])) {
fprintf(stderr, "invalid \"-s %s\"; default is \"-s 1920x1080\" (up to 4 digits)\n",
value.c_str());
exit(1);
}
} else if (arg == "-fps") {
if (!option_has_value(i, argc, argv)) exit(1);
std::string value(argv[++i]);
if (!get_fps(argv[i], &display[3])) {
fprintf(stderr, "invalid \"-fps %s\"; default is \"-s 30\" (up to 3 digits)\n",
value.c_str());
exit(1);
}
} else if (arg == "-f") {
if (i == argc - 1 || argv[i + 1][0] == '-') {
fprintf(stderr,"invalid \"-f\" had no argument\n");
exit(1);
}
if (!option_has_value(i, argc, argv)) exit(1);
if (!get_videoflip(argv[++i], &videoflip[0])) {
fprintf(stderr,"invalid \"-f %s\" , unknown flip type, choices are H, V, I\n",argv[i]);
exit(1);
}
} else if (arg == "-r") {
if (i == argc - 1 || argv[i + 1][0] == '-') {
fprintf(stderr,"invalid \"-r\" had no argument\n");
exit(1);
}
if (!option_has_value(i, argc, argv)) exit(1);
if (!get_videorotate(argv[++i], &videoflip[1])) {
fprintf(stderr,"invalid \"-r %s\" , unknown rotation type, choices are R, L\n",argv[i]);
exit(1);
}
} else if (arg == "-p") {
if (i == argc - 1 || argv[i + 1][0] == '-') {
if (i >= argc - 1 || argv[i + 1][0] == '-') {
tcp[0] = 7100; tcp[1] = 7000;
udp[0] = 7011; udp[1] = 6001; udp[2] = 6000;
continue;
@@ -266,7 +292,8 @@ int main (int argc, char *argv[]) {
print_info(argv[0]);
exit(0);
} else {
LOGI("unknown option %s, skipping\n",argv[i]);
LOGE("unknown option %s, stopping\n",argv[i]);
exit(1);
}
}
@@ -284,7 +311,7 @@ int main (int argc, char *argv[]) {
mac_address.clear();
relaunch:
if (start_server(server_hw_addr, server_name, display_size, tcp, udp,
if (start_server(server_hw_addr, server_name, display, tcp, udp,
videoflip,use_audio, debug_log) != 0) {
return 1;
}
@@ -357,7 +384,7 @@ extern "C" void log_callback (void *cls, int level, const char *msg) {
}
int start_server (std::vector<char> hw_addr, std::string name, unsigned short display_size[2],
int start_server (std::vector<char> hw_addr, std::string name, unsigned short display[4],
unsigned short tcp[2], unsigned short udp[3], videoflip_t videoflip[2],
bool use_audio, bool debug_log) {
raop_callbacks_t raop_cbs;
@@ -399,8 +426,9 @@ int start_server (std::vector<char> hw_addr, std::string name, unsigned short di
if (video_renderer) video_renderer_start(video_renderer);
if (audio_renderer) audio_renderer_start(audio_renderer);
/* write desired display pixel width, pixel height to raop (use 0 for default values) */
raop_set_display_size(raop, display_size[0], display_size[1]);
/* write desired display pixel width, pixel height, refresh_rate, */
/* and max_fps to raop (use 0 for default values) */
raop_set_display(raop, display[0], display[1], display[2], display[3]);
/* network port selection (ports listed as "0" will be dynamically assigned) */
raop_set_tcp_ports(raop, tcp);