From 8c55f603bc5fd145c0efe8907048a032a71d9044 Mon Sep 17 00:00:00 2001 From: Mke Brady Date: Wed, 10 Jun 2015 22:18:17 +0100 Subject: [PATCH] UDP port numbers are now chosen from port 7000 up. Two options are introduced to control this. joerg-krause's stack erro fix installed. --- common.h | 2 + configure.ac | 2 +- player.c | 13 +++++-- rtp.c | 39 ++++++++++++------- rtsp.c | 2 +- scripts/shairport-sync.conf | 2 + scripts/shairport-sync.conf~ | 75 ++++++++++++++++++++++++++++++++++++ shairport.c | 22 +++++++++++ 8 files changed, 138 insertions(+), 19 deletions(-) create mode 100644 scripts/shairport-sync.conf~ diff --git a/common.h b/common.h index 6a9436b1..792dd36c 100644 --- a/common.h +++ b/common.h @@ -48,6 +48,8 @@ typedef struct { #endif uint8_t hw_addr[6]; int port; + int udp_port_base; + int udp_port_range; int ignore_volume_control; int resyncthreshold; // if it get's out of whack my more than this, resync. Zero means never // resync. diff --git a/configure.ac b/configure.ac index 20d94f14..635ff6d9 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.50]) -AC_INIT([shairport-sync], [2.3.2], [mikebrady@eircom.net]) +AC_INIT([shairport-sync], [2.3.3], [mikebrady@eircom.net]) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([shairport.c]) AC_CONFIG_HEADERS([config.h]) diff --git a/player.c b/player.c index 1a760ce2..df12eb27 100644 --- a/player.c +++ b/player.c @@ -36,12 +36,13 @@ #include #include #include -#include +#include #include #include #include #include #include +#include #include "config.h" @@ -1271,8 +1272,14 @@ int player_play(stream_cfg *stream) { if (rc) debug(1, "Error initialising condition variable."); config.output->start(sampling_rate); - pthread_create(&player_thread, NULL, player_thread_func, NULL); - + size_t size = (PTHREAD_STACK_MIN + 128 * 1024); + pthread_attr_t tattr; + pthread_attr_init(&tattr); + rc = pthread_attr_setstacksize(&tattr, size); + if (rc) + debug(1, "Error setting stack size for player_thread: %s", strerror(errno)); + pthread_create(&player_thread, &tattr, player_thread_func, NULL); + pthread_attr_destroy(&tattr); return 0; } diff --git a/rtp.c b/rtp.c index 5590e649..37e2eccb 100644 --- a/rtp.c +++ b/rtp.c @@ -396,7 +396,7 @@ static void *rtp_timing_receiver(void *arg) { return NULL; } -static int bind_port(SOCKADDR *remote, int *sock, int desired_port) { +static int bind_port(SOCKADDR *remote, int *sock) { struct addrinfo hints, *info; memset(&hints, 0, sizeof(hints)); @@ -405,21 +405,32 @@ static int bind_port(SOCKADDR *remote, int *sock, int desired_port) { hints.ai_flags = AI_PASSIVE; char buffer[10]; - snprintf(buffer, 10, "%d", desired_port); + + // look for a port in the range, if any was specified. + int desired_port = config.udp_port_base; + int ret; + do { + snprintf(buffer, 10, "%d", desired_port); - int ret = getaddrinfo(NULL, buffer, &hints, &info); + ret = getaddrinfo(NULL, buffer, &hints, &info); - if (ret < 0) - die("failed to get usable addrinfo?! %s.", gai_strerror(ret)); + if (ret < 0) + die("failed to get usable addrinfo?! %s.", gai_strerror(ret)); - *sock = socket(remote->SAFAMILY, SOCK_DGRAM, IPPROTO_UDP); - ret = bind(*sock, info->ai_addr, info->ai_addrlen); + *sock = socket(remote->SAFAMILY, SOCK_DGRAM, IPPROTO_UDP); + ret = bind(*sock, info->ai_addr, info->ai_addrlen); - freeaddrinfo(info); - - if (ret < 0) - die("could not bind a UDP port!"); + freeaddrinfo(info); + } while ((ret<0) && (errno==EADDRINUSE) && (desired_port!=0) && (desired_port++ < config.udp_port_base+config.udp_port_range)); + + debug(1,"UDP port chosen: %d.",desired_port); + + if (ret < 0) { + die("error: could not bind a UDP port!"); + } + + int sport; SOCKADDR local; socklen_t local_len = sizeof(local); @@ -510,9 +521,9 @@ void rtp_setup(SOCKADDR *remote, int cport, int tport, uint32_t active_remote, i // now, we open three sockets -- one for the audio stream, one for the timing and one for the // control - *lsport = bind_port(remote, &audio_socket, 0); - *lcport = bind_port(remote, &control_socket, 0); - *ltport = bind_port(remote, &timing_socket, 0); + *lsport = bind_port(remote, &audio_socket); + *lcport = bind_port(remote, &control_socket); + *ltport = bind_port(remote, &timing_socket); debug(2, "listening for audio, control and timing on ports %d, %d, %d.", *lsport, *lcport, *ltport); diff --git a/rtsp.c b/rtsp.c index f13da7c1..3cf03d85 100644 --- a/rtsp.c +++ b/rtsp.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include "config.h" diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 797e8e4f..3b1c16bd 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -8,6 +8,8 @@ general = // mdns_backend = "avahi"; // not used, not tested // output_backend = "alsa"; // alsa is default, other possibilities are "stdout", "pulse", "dummy". Only alsa supports synchronisation. // port = 5000; +// udp_port_base = 7000; // start allocation UDP ports from this port number +// udp_port_range = 100; // look for free ports in this number of places, starting at the UDP port base (only three are needed). // password = "secret"; // default is no password // interpolation = "basic"; // aka "stuffing". Default is "basic", alternative is "soxr" // statistics = "no"; // print statistics in the log diff --git a/scripts/shairport-sync.conf~ b/scripts/shairport-sync.conf~ new file mode 100644 index 00000000..797e8e4f --- /dev/null +++ b/scripts/shairport-sync.conf~ @@ -0,0 +1,75 @@ +// Sample Configuration File for Shairport Sync +// Commented out settings are generally the defaults, except where noted. + +// General Settings +general = +{ +// name = "Shairport Sync Player"; // This is the name the service will advertise to iTunes. The default is "Shairport Sync on " +// mdns_backend = "avahi"; // not used, not tested +// output_backend = "alsa"; // alsa is default, other possibilities are "stdout", "pulse", "dummy". Only alsa supports synchronisation. +// port = 5000; +// password = "secret"; // default is no password +// interpolation = "basic"; // aka "stuffing". Default is "basic", alternative is "soxr" +// statistics = "no"; // print statistics in the log +// drift = 88; // allow this number of frames of drift before correcting it +// resync_threshold = 2205; // a synchronisation error greater than this will cause resynchronisation; 0 disables it +// log_verbosity = 0; // "0" means no verbosity, "3" is most verbose. +// ignore_volume_control = "no"; // set this to "yes" if you want the volume to be at 100% no matter what the source's volume control is set to. +}; + +// Latencies for different sources. +latencies = +{ +// default = 88200; +// itunes = 99400; +// airplay = 88200; +// forkedDaapd = 99400; +}; + + +// How to deal with metadata, including artwork +metadata = +{ +// enabled = "no"; +// include_cover_art = "no"; +// pipe_name = "/tmp/shairport-sync-metadata"; +}; + +// Advanced parameters for controlling how a Shairport Sync runs +sessioncontrol = +{ +// run_this_before_play_begins = "/path/to/application and args"; +// run_this_after_play_ends = "/path/to/application and args"; +// wait_for_completion = "no"; +// allow_session_interruption = "no"; +// session_timeout = 120; +}; + +// +// Back End Settings +// + +// These are parameters for the alsa back end, the only back end that supports synchronisation +{ +// output_device = "default"; +// mixer_type = "software"; // "software" or "hardware" +// mixer_device = "default"; //actually, the mixer default is whatever the output_device is. Normally you wouldn't have to use this. +// mixer_control_name = "PCM"; // the name of the mixer to use -- there is no default. +// audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410. +// audio_backend_buffer_desired_length = 6615; // If set too small, buffer underflow occurs on low-powered machines. Too long and the response times with software mixer become annoying. +}; + +// These are parameters for the pipe back end, an experimental back end that directs output to a pipe. +pipe = +{ +// audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410. +// audio_backend_buffer_desired_length = 44100; +// name = "/path/to/pipe"; // there is no default pipe name for the output +}; + +// These are parameters for the stdout back end, an experimental back end that directs output to stdout. +stdout = +{ +// audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410. +// audio_backend_buffer_desired_length = 44100; +}; diff --git a/shairport.c b/shairport.c index 16a42080..2c5fcbdd 100644 --- a/shairport.c +++ b/shairport.c @@ -369,6 +369,24 @@ int parse_options(int argc, char **argv) { config.port = value; } + /* Get the udp port base setting. */ + if (config_lookup_int(config.cfg, "general.udp_port_base", &value)) { + if ((value < 0) || (value > 65535)) + die("Invalid port number \"%sd\". It should be between 0 and 65535, default is 7000", + value); + else + config.udp_port_base = value; + } + + /* Get the udp port range setting. This is number of ports that will be tried for free ports , starting at the port base. Only three ports are needed. */ + if (config_lookup_int(config.cfg, "general.udp_port_range", &value)) { + if ((value < 0) || (value > 65535)) + die("Invalid port range \"%sd\". It should be between 0 and 65535, default is 100", + value); + else + config.udp_port_range = value; + } + /* Get the password setting. */ if (config_lookup_string(config.cfg, "general.password", &str)) config.password = (char *)str; @@ -606,6 +624,8 @@ int main(int argc, char **argv) { set_requested_connection_state_to_output( 1); // we expect to be able to connect to the output device config.audio_backend_buffer_desired_length = 6615; // 0.15 seconds. + config.udp_port_base = 7000; + config.udp_port_range = 100; // this is a bit weird, but apparently necessary char *basec = strdup(argv[0]); @@ -780,6 +800,8 @@ int main(int argc, char **argv) { debug(2, "statistics_requester status is %d.", config.statistics_requested); debug(2, "daemon status is %d.", config.daemonise); debug(2, "rtsp listening port is %d.", config.port); + debug(2, "udp base port is %d.", config.udp_port_base); + debug(2, "udp port range is %d.", config.udp_port_range); debug(2, "Shairport Sync player name is \"%s\".", config.apname); debug(2, "Audio Output name is \"%s\".", config.output_name); debug(2, "on-start action is \"%s\".", config.cmd_start);