summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMke Brady <mikebrady@eircom.net>2015-06-01 23:16:50 +0100
committerMke Brady <mikebrady@eircom.net>2015-06-01 23:16:50 +0100
commit87a0475c16a8203c9029a34e60cec00e23a86ab7 (patch)
treec454ba62ad2d964f7fde8481774115662cc3587f
parent04e27cfe4f37f411612e44a7af939ec5848f35d6 (diff)
Reformat using clang-format.
-rw-r--r--audio.c40
-rw-r--r--audio.h54
-rw-r--r--audio_alsa.c355
-rw-r--r--audio_ao.c159
-rw-r--r--audio_dummy.c50
-rw-r--r--audio_pipe.c83
-rw-r--r--audio_pulse.c157
-rw-r--r--audio_sndio.c80
-rw-r--r--common.c595
-rw-r--r--common.h105
-rw-r--r--mdns.c99
-rw-r--r--mdns.h18
-rw-r--r--mdns_avahi.c189
-rw-r--r--mdns_dns_sd.c103
-rw-r--r--mdns_external.c205
-rw-r--r--mdns_tinysvcmdns.c200
-rw-r--r--player.c1109
-rw-r--r--player.h6
-rw-r--r--rtp.c944
-rw-r--r--rtp.h7
-rw-r--r--rtsp.c2029
-rw-r--r--rtsp.h2
-rw-r--r--shairport.c1178
-rw-r--r--tinysvcmdns.c2429
-rw-r--r--tinysvcmdns.h156
25 files changed, 5169 insertions, 5183 deletions
diff --git a/audio.c b/audio.c
index 95bedc2..9887531 100644
--- a/audio.c
+++ b/audio.c
@@ -56,36 +56,32 @@ static audio_output *outputs[] = {
#ifdef CONFIG_AO
&audio_ao,
#endif
- &audio_dummy,
- &audio_pipe,
- NULL
-};
-
+ &audio_dummy, &audio_pipe, NULL};
audio_output *audio_get_output(char *name) {
- audio_output **out;
+ audio_output **out;
- // default to the first
- if (!name)
- return outputs[0];
+ // default to the first
+ if (!name)
+ return outputs[0];
- for (out=outputs; *out; out++)
- if (!strcasecmp(name, (*out)->name))
- return *out;
+ for (out = outputs; *out; out++)
+ if (!strcasecmp(name, (*out)->name))
+ return *out;
- return NULL;
+ return NULL;
}
void audio_ls_outputs(void) {
- audio_output **out;
+ audio_output **out;
- printf("Available audio outputs:\n");
- for (out=outputs; *out; out++)
- printf(" %s%s\n", (*out)->name, out==outputs ? " (default)" : "");
+ printf("Available audio outputs:\n");
+ for (out = outputs; *out; out++)
+ printf(" %s%s\n", (*out)->name, out == outputs ? " (default)" : "");
- for (out=outputs; *out; out++) {
- printf("\n");
- printf("Options for output %s:\n", (*out)->name);
- (*out)->help();
- }
+ for (out = outputs; *out; out++) {
+ printf("\n");
+ printf("Options for output %s:\n", (*out)->name);
+ (*out)->help();
+ }
}
diff --git a/audio.h b/audio.h
index 8990b4f..4738a07 100644
--- a/audio.h
+++ b/audio.h
@@ -15,33 +15,33 @@ typedef struct {
} audio_parameters;
typedef struct {
- void (*help)(void);
- char *name;
-
- // start of program
- int (*init)(int argc, char **argv);
- // at end of program
- void (*deinit)(void);
-
- void (*start)(int sample_rate);
-
- // block of samples
- void (*play)(short buf[], int samples);
- void (*stop)(void);
-
- // may be null if not implemented
- void (*flush)(void);
-
- // returns the delay before the next frame to be sent to the device would actually be audible.
- // almost certainly wrong if the buffer is empty, so put silent buffers into it to make it busy.
- // will change dynamically, so keep watching it. Implemented in ALSA only.
- uint32_t (*delay)();
-
- // may be NULL, in which case soft volume is applied
- void (*volume)(double vol);
-
- // may be NULL, in which case soft volume parameters are used
- void (*parameters)(audio_parameters* info);
+ void (*help)(void);
+ char *name;
+
+ // start of program
+ int (*init)(int argc, char **argv);
+ // at end of program
+ void (*deinit)(void);
+
+ void (*start)(int sample_rate);
+
+ // block of samples
+ void (*play)(short buf[], int samples);
+ void (*stop)(void);
+
+ // may be null if not implemented
+ void (*flush)(void);
+
+ // returns the delay before the next frame to be sent to the device would actually be audible.
+ // almost certainly wrong if the buffer is empty, so put silent buffers into it to make it busy.
+ // will change dynamically, so keep watching it. Implemented in ALSA only.
+ uint32_t (*delay)();
+
+ // may be NULL, in which case soft volume is applied
+ void (*volume)(double vol);
+
+ // may be NULL, in which case soft volume parameters are used
+ void (*parameters)(audio_parameters *info);
} audio_output;
audio_output *audio_get_output(char *name);
diff --git a/audio_alsa.c b/audio_alsa.c
index 257497f..02af8d8 100644
--- a/audio_alsa.c
+++ b/audio_alsa.c
@@ -43,9 +43,9 @@ static void stop(void);
static void flush(void);
static uint32_t delay(void);
static void volume(double vol);
-static void parameters(audio_parameters* info);
-static int has_mute=0;
-static int has_db_vol=0;
+static void parameters(audio_parameters *info);
+static int has_mute = 0;
+static int has_db_vol = 0;
static double set_volume;
audio_output audio_alsa = {
@@ -58,7 +58,7 @@ audio_output audio_alsa = {
.flush = &flush,
.delay = &delay,
.play = &play,
- .volume = NULL, // to be set later on...
+ .volume = NULL, // to be set later on...
.parameters = NULL // to be set later on...
};
@@ -81,16 +81,15 @@ static char *alsa_mix_ctrl = "Master";
static int alsa_mix_index = 0;
static int play_number;
-static int64_t accumulated_delay,accumulated_da_delay;
+static int64_t accumulated_delay, accumulated_da_delay;
static void help(void) {
- printf(" -d output-device set the output device [default*|...]\n"
- " -t mixer-type set the mixer type [software*|hardware]\n"
- " -m mixer-device set the mixer device ['output-device'*|...]\n"
- " -c mixer-control set the mixer control [Master*|...]\n"
- " -i mixer-index set the mixer index [0*|...]\n"
- " *) default option\n"
- );
+ printf(" -d output-device set the output device [default*|...]\n"
+ " -t mixer-type set the mixer type [software*|hardware]\n"
+ " -m mixer-device set the mixer device ['output-device'*|...]\n"
+ " -c mixer-control set the mixer control [Master*|...]\n"
+ " -i mixer-index set the mixer index [0*|...]\n"
+ " *) default option\n");
}
static int init(int argc, char **argv) {
@@ -100,52 +99,55 @@ static int init(int argc, char **argv) {
int hardware_mixer = 0;
config.audio_backend_buffer_desired_length = 6615; // this is the default for ALSA
- config.audio_backend_latency_offset = 0; // this is the default for ALSA
-
+ config.audio_backend_latency_offset = 0; // this is the default for ALSA
+
// get settings from settings file first, allow them to be over-ridden by command line options
-
- if (config.cfg!=NULL) {
- /* Get the desired buffer size setting. */
- if(config_lookup_int(config.cfg, "alsa.audio_backend_buffer_desired_length", &value)) {
- if ((value<0) || (value>66150))
- die("Invalid alsa audio backend buffer desired length \"%sd\". It should be between 0 and 66150, default is 6615",value);
- else
- config.audio_backend_buffer_desired_length=value;
- }
-
- /* Get the latency offset. */
- if(config_lookup_int(config.cfg, "alsa.audio_backend_latency_offset", &value)) {
- if ((value<-22050) || (value>22050))
- die("Invalid alsa audio backend buffer latency offset \"%sd\". It should be between -22050 and +22050, default is 0",value);
- else
- config.audio_backend_latency_offset=value;
- }
- /* Get the Output Device Name. */
- if(config_lookup_string(config.cfg, "alsa.output_device", &str)) {
- alsa_out_dev = (char*)str;
- }
-
- /* Get the Mixer Type setting. */
- if(config_lookup_string(config.cfg, "alsa.mixer_type", &str)) {
- if (strcasecmp(str,"software")==0)
- hardware_mixer=0;
- else if (strcasecmp(str,"hardware")==0)
- hardware_mixer=1;
- else
- die("Invalid alsa mixer option choice \"%s\". It should be \"software\" or \"hardware\"");
- }
+ if (config.cfg != NULL) {
+ /* Get the desired buffer size setting. */
+ if (config_lookup_int(config.cfg, "alsa.audio_backend_buffer_desired_length", &value)) {
+ if ((value < 0) || (value > 66150))
+ die("Invalid alsa audio backend buffer desired length \"%sd\". It should be between 0 and "
+ "66150, default is 6615",
+ value);
+ else
+ config.audio_backend_buffer_desired_length = value;
+ }
- /* Get the Mixer Device Name. */
- if(config_lookup_string(config.cfg, "alsa.mixer_device", &str)) {
- alsa_mix_dev = (char*)str;
- }
-
- /* Get the Mixer Control Name. */
- if(config_lookup_string(config.cfg, "alsa.mixer_control_name", &str)) {
- alsa_mix_ctrl = (char*)str;
- }
+ /* Get the latency offset. */
+ if (config_lookup_int(config.cfg, "alsa.audio_backend_latency_offset", &value)) {
+ if ((value < -22050) || (value > 22050))
+ die("Invalid alsa audio backend buffer latency offset \"%sd\". It should be between -22050 "
+ "and +22050, default is 0",
+ value);
+ else
+ config.audio_backend_latency_offset = value;
+ }
+
+ /* Get the Output Device Name. */
+ if (config_lookup_string(config.cfg, "alsa.output_device", &str)) {
+ alsa_out_dev = (char *)str;
+ }
+
+ /* Get the Mixer Type setting. */
+ if (config_lookup_string(config.cfg, "alsa.mixer_type", &str)) {
+ if (strcasecmp(str, "software") == 0)
+ hardware_mixer = 0;
+ else if (strcasecmp(str, "hardware") == 0)
+ hardware_mixer = 1;
+ else
+ die("Invalid alsa mixer option choice \"%s\". It should be \"software\" or \"hardware\"");
+ }
+
+ /* Get the Mixer Device Name. */
+ if (config_lookup_string(config.cfg, "alsa.mixer_device", &str)) {
+ alsa_mix_dev = (char *)str;
+ }
+ /* Get the Mixer Control Name. */
+ if (config_lookup_string(config.cfg, "alsa.mixer_control_name", &str)) {
+ alsa_mix_ctrl = (char *)str;
+ }
}
optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
@@ -154,37 +156,37 @@ static int init(int argc, char **argv) {
// some platforms apparently require optreset = 1; - which?
int opt;
while ((opt = getopt(argc, argv, "d:t:m:c:i:")) > 0) {
- switch (opt) {
- case 'd':
- alsa_out_dev = optarg;
- break;
- case 't':
- if (strcmp(optarg, "hardware") == 0)
- hardware_mixer = 1;
- break;
- case 'm':
- alsa_mix_dev = optarg;
- break;
- case 'c':
- alsa_mix_ctrl = optarg;
- break;
- case 'i':
- alsa_mix_index = strtol(optarg, NULL, 10);
- break;
- default:
- help();
- die("Invalid audio option -%c specified", opt);
- }
+ switch (opt) {
+ case 'd':
+ alsa_out_dev = optarg;
+ break;
+ case 't':
+ if (strcmp(optarg, "hardware") == 0)
+ hardware_mixer = 1;
+ break;
+ case 'm':
+ alsa_mix_dev = optarg;
+ break;
+ case 'c':
+ alsa_mix_ctrl = optarg;
+ break;
+ case 'i':
+ alsa_mix_index = strtol(optarg, NULL, 10);
+ break;
+ default:
+ help();
+ die("Invalid audio option -%c specified", opt);
+ }
}
if (optind < argc)
- die("Invalid audio argument: %s", argv[optind]);
-
+ die("Invalid audio argument: %s", argv[optind]);
+
if (!hardware_mixer)
- return 0;
+ return 0;
if (alsa_mix_dev == NULL)
- alsa_mix_dev = alsa_out_dev;
+ alsa_mix_dev = alsa_out_dev;
int ret = 0;
@@ -193,39 +195,41 @@ static int init(int argc, char **argv) {
snd_mixer_selem_id_set_name(alsa_mix_sid, alsa_mix_ctrl);
if ((snd_mixer_open(&alsa_mix_handle, 0)) < 0)
- die ("Failed to open mixer");
+ die("Failed to open mixer");
if ((snd_mixer_attach(alsa_mix_handle, alsa_mix_dev)) < 0)
- die ("Failed to attach mixer");
+ die("Failed to attach mixer");
if ((snd_mixer_selem_register(alsa_mix_handle, NULL, NULL)) < 0)
- die ("Failed to register mixer element");
+ die("Failed to register mixer element");
ret = snd_mixer_load(alsa_mix_handle);
if (ret < 0)
- die ("Failed to load mixer element");
+ die("Failed to load mixer element");
alsa_mix_elem = snd_mixer_find_selem(alsa_mix_handle, alsa_mix_sid);
if (!alsa_mix_elem)
- die ("Failed to find mixer element");
- if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv)<0)
- debug(1,"Can't read mixer's [linear] min and max volumes.");
+ die("Failed to find mixer element");
+ if (snd_mixer_selem_get_playback_volume_range(alsa_mix_elem, &alsa_mix_minv, &alsa_mix_maxv) < 0)
+ debug(1, "Can't read mixer's [linear] min and max volumes.");
else {
- if ((snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem,alsa_mix_minv,&alsa_mix_mindb)==0) &&
- (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem,alsa_mix_maxv,&alsa_mix_maxdb)==0)) {
- has_db_vol=1;
+ if ((snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv, &alsa_mix_mindb) == 0) &&
+ (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_maxv, &alsa_mix_maxdb) == 0)) {
+ has_db_vol = 1;
audio_alsa.volume = &volume; // insert the volume function now we know it can do dB stuff
audio_alsa.parameters = &parameters; // likewise the parameters stuff
- if (alsa_mix_mindb==-9999999) {
+ if (alsa_mix_mindb == -9999999) {
// trying to say that the lowest vol is mute, maybe? Raspberry Pi does this
- if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem,alsa_mix_minv+1,&alsa_mix_mindb)==0)
- debug(1,"Can't get dB value corresponding to a \"volume\" of 1.");
+ if (snd_mixer_selem_ask_playback_vol_dB(alsa_mix_elem, alsa_mix_minv + 1,
+ &alsa_mix_mindb) == 0)
+ debug(1, "Can't get dB value corresponding to a \"volume\" of 1.");
}
- debug(1,"Hardware mixer has dB volume from %f to %f.",(1.0*alsa_mix_mindb)/100.0,(1.0*alsa_mix_maxdb)/100.0);
+ debug(1, "Hardware mixer has dB volume from %f to %f.", (1.0 * alsa_mix_mindb) / 100.0,
+ (1.0 * alsa_mix_maxdb) / 100.0);
} else {
- debug(1,"Hardware mixer does not have dB volume -- not used.");
+ debug(1, "Hardware mixer does not have dB volume -- not used.");
}
}
if (snd_mixer_selem_has_playback_switch(alsa_mix_elem)) {
- has_mute=1;
- debug(1,"Has mute ability.");
+ has_mute = 1;
+ debug(1, "Has mute ability.");
}
return 0;
}
@@ -233,20 +237,20 @@ static int init(int argc, char **argv) {
static void deinit(void) {
stop();
if (alsa_mix_handle) {
- snd_mixer_close(alsa_mix_handle);
+ snd_mixer_close(alsa_mix_handle);
}
}
-int open_alsa_device(void) {
+int open_alsa_device(void) {
int ret, dir = 0;
unsigned int my_sample_rate = desired_sample_rate;
- snd_pcm_uframes_t frames = 441*10;
- snd_pcm_uframes_t buffer_size = frames*4;
+ snd_pcm_uframes_t frames = 441 * 10;
+ snd_pcm_uframes_t buffer_size = frames * 4;
ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0);
if (ret < 0)
- return(ret);
- // die("Alsa initialization failed: unable to open pcm device: %s.", snd_strerror(ret));
+ return (ret);
+ // die("Alsa initialization failed: unable to open pcm device: %s.", snd_strerror(ret));
snd_pcm_hw_params_alloca(&alsa_params);
snd_pcm_hw_params_any(alsa_handle, alsa_params);
@@ -260,75 +264,79 @@ int open_alsa_device(void) {
if (ret < 0) {
die("unable to set hw parameters: %s.", snd_strerror(ret));
}
- if (my_sample_rate!=desired_sample_rate) {
- die("Can't set the D/A converter to %d -- set to %d instead./n",desired_sample_rate,my_sample_rate);
+ if (my_sample_rate != desired_sample_rate) {
+ die("Can't set the D/A converter to %d -- set to %d instead./n", desired_sample_rate,
+ my_sample_rate);
}
- return(0);
+ return (0);
}
static void start(int sample_rate) {
if (sample_rate != 44100)
- die("Unexpected sample rate %d -- only 44,100 supported!",sample_rate);
+ die("Unexpected sample rate %d -- only 44,100 supported!", sample_rate);
desired_sample_rate = sample_rate; // must be a variable
}
static uint32_t delay() {
- if (alsa_handle==NULL) {
- return 0;
- } else {
- pthread_mutex_lock(&alsa_mutex);
- snd_pcm_sframes_t current_avail,current_delay = 0;
- int derr,ignore;
- if (snd_pcm_state(alsa_handle)==SND_PCM_STATE_RUNNING) {
- derr = snd_pcm_avail_delay(alsa_handle,&current_avail,&current_delay);
- // current_avail not used
- if (derr != 0) {
- ignore = snd_pcm_recover(alsa_handle, derr, 0);
- debug(1,"Error %d in delay(): %s. Delay reported is %d frames.", derr, snd_strerror(derr),current_delay);
- current_delay=-1;
- }
- } else if (snd_pcm_state(alsa_handle)==SND_PCM_STATE_PREPARED) {
- current_delay=0;
- } else {
- if (snd_pcm_state(alsa_handle)==SND_PCM_STATE_XRUN)
- current_delay=0;
- else {
- current_delay=-1;
- debug(1,"Error -- ALSA delay(): bad state: %d.",snd_pcm_state(alsa_handle));
- }
- if (derr = snd_pcm_prepare(alsa_handle)) {
- ignore = snd_pcm_recover(alsa_handle, derr, 0);
- debug(1,"Error preparing after delay error: %s.", snd_strerror(derr));
- current_delay = -1;
- }
- }
- pthread_mutex_unlock(&alsa_mutex);
- return current_delay;
+ if (alsa_handle == NULL) {
+ return 0;
+ } else {
+ pthread_mutex_lock(&alsa_mutex);
+ snd_pcm_sframes_t current_avail, current_delay = 0;
+ int derr, ignore;
+ if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_RUNNING) {
+ derr = snd_pcm_avail_delay(alsa_handle, &current_avail, &current_delay);
+ // current_avail not used
+ if (derr != 0) {
+ ignore = snd_pcm_recover(alsa_handle, derr, 0);
+ debug(1, "Error %d in delay(): %s. Delay reported is %d frames.", derr, snd_strerror(derr),
+ current_delay);
+ current_delay = -1;
+ }
+ } else if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) {
+ current_delay = 0;
+ } else {
+ if (snd_pcm_state(alsa_handle) == SND_PCM_STATE_XRUN)
+ current_delay = 0;
+ else {
+ current_delay = -1;
+ debug(1, "Error -- ALSA delay(): bad state: %d.", snd_pcm_state(alsa_handle));
+ }
+ if (derr = snd_pcm_prepare(alsa_handle)) {
+ ignore = snd_pcm_recover(alsa_handle, derr, 0);
+ debug(1, "Error preparing after delay error: %s.", snd_strerror(derr));
+ current_delay = -1;
+ }
+ }
+ pthread_mutex_unlock(&alsa_mutex);
+ return current_delay;
}
}
static void play(short buf[], int samples) {
int ret = 0;
- if (alsa_handle==NULL) {
- ret = open_alsa_device();
- if ((ret==0) && (audio_alsa.volume))
- volume(set_volume);
- }
- if (ret==0) {
+ if (alsa_handle == NULL) {
+ ret = open_alsa_device();
+ if ((ret == 0) && (audio_alsa.volume))
+ volume(set_volume);
+ }
+ if (ret == 0) {
pthread_mutex_lock(&alsa_mutex);
snd_pcm_sframes_t current_delay = 0;
- int err,ignore;
- if ((snd_pcm_state(alsa_handle)==SND_PCM_STATE_PREPARED) || (snd_pcm_state(alsa_handle)==SND_PCM_STATE_RUNNING)) {
- err = snd_pcm_writei(alsa_handle, (char*)buf, samples);
+ int err, ignore;
+ if ((snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) ||
+ (snd_pcm_state(alsa_handle) == SND_PCM_STATE_RUNNING)) {
+ err = snd_pcm_writei(alsa_handle, (char *)buf, samples);
if (err < 0) {
ignore = snd_pcm_recover(alsa_handle, err, 0);
- debug(1,"Error %d writing %d samples in play() %s.",err,samples, snd_strerror(err));
+ debug(1, "Error %d writing %d samples in play() %s.", err, samples, snd_strerror(err));
}
} else {
- debug(1,"Error -- ALSA device in incorrect state (%d) for play.",snd_pcm_state(alsa_handle));
+ debug(1, "Error -- ALSA device in incorrect state (%d) for play.",
+ snd_pcm_state(alsa_handle));
if (err = snd_pcm_prepare(alsa_handle)) {
ignore = snd_pcm_recover(alsa_handle, err, 0);
- debug(1,"Error preparing after play error: %s.", snd_strerror(err));
+ debug(1, "Error preparing after play error: %s.", snd_strerror(err));
}
}
pthread_mutex_unlock(&alsa_mutex);
@@ -340,20 +348,21 @@ static void flush(void) {
if (alsa_handle) {
// debug(1,"Dropping frames for flush...");
if (derr = snd_pcm_drop(alsa_handle))
- debug(1,"Error dropping frames: %s.", snd_strerror(derr));
+ debug(1, "Error dropping frames: %s.", snd_strerror(derr));
// debug(1,"Dropped frames ok. State is %d.",snd_pcm_state(alsa_handle));
if (derr = snd_pcm_prepare(alsa_handle))
- debug(1,"Error preparing after flush: %s.", snd_strerror(derr));
+ debug(1, "Error preparing after flush: %s.", snd_strerror(derr));
// debug(1,"Frames successfully dropped.");
/*
if (snd_pcm_state(alsa_handle)==SND_PCM_STATE_PREPARED)
- debug(1,"Flush returns to SND_PCM_STATE_PREPARED state.");
+ debug(1,"Flush returns to SND_PCM_STATE_PREPARED state.");
if (snd_pcm_state(alsa_handle)==SND_PCM_STATE_RUNNING)
debug(1,"Flush returns to SND_PCM_STATE_RUNNING state.");
- */
- if (!((snd_pcm_state(alsa_handle)==SND_PCM_STATE_PREPARED) || (snd_pcm_state(alsa_handle)==SND_PCM_STATE_RUNNING)))
- debug(1,"Flush returning unexpected state -- %d.",snd_pcm_state(alsa_handle));
-
+ */
+ if (!((snd_pcm_state(alsa_handle) == SND_PCM_STATE_PREPARED) ||
+ (snd_pcm_state(alsa_handle) == SND_PCM_STATE_RUNNING)))
+ debug(1, "Flush returning unexpected state -- %d.", snd_pcm_state(alsa_handle));
+
// flush also closes the device
snd_pcm_close(alsa_handle);
alsa_handle = NULL;
@@ -361,29 +370,29 @@ static void flush(void) {
}
static void stop(void) {
- if (alsa_handle!=0)
- // when we want to stop, we want the alsa device
- // to be closed immediately -- we may even be killing the thread, so we don't wish to wait
- // so we should flush first
- flush(); // flush will also close the device
- // close_alsa_device();
+ if (alsa_handle != 0)
+ // when we want to stop, we want the alsa device
+ // to be closed immediately -- we may even be killing the thread, so we don't wish to wait
+ // so we should flush first
+ flush(); // flush will also close the device
+ // close_alsa_device();
}
-static void parameters(audio_parameters* info) {
- info->has_true_mute=has_mute;
+static void parameters(audio_parameters *info) {
+ info->has_true_mute = has_mute;
info->is_muted = ((has_mute) && (set_volume == -144.0));
- info->minimum_volume_dB=alsa_mix_mindb;
- info->maximum_volume_dB=alsa_mix_maxdb;
- info->airplay_volume=set_volume;
- info->current_volume_dB=vol2attn(set_volume,alsa_mix_maxdb,alsa_mix_mindb);
+ info->minimum_volume_dB = alsa_mix_mindb;
+ info->maximum_volume_dB = alsa_mix_maxdb;
+ info->airplay_volume = set_volume;
+ info->current_volume_dB = vol2attn(set_volume, alsa_mix_maxdb, alsa_mix_mindb);
}
static void volume(double vol) {
- set_volume=vol;
- double vol_setting = vol2attn(vol,alsa_mix_maxdb,alsa_mix_mindb);
+ set_volume = vol;
+ double vol_setting = vol2attn(vol, alsa_mix_maxdb, alsa_mix_mindb);
// debug(1,"Setting volume db to %f, for volume input of %f.",vol_setting/100,vol);
if (snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol_setting, -1) != 0)
- die ("Failed to set playback dB volume");
- if (has_mute)
- snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, (vol!=-144.0));
+ die("Failed to set playback dB volume");
+ if (has_mute)
+ snd_mixer_selem_set_playback_switch_all(alsa_mix_elem, (vol != -144.0));
}
diff --git a/audio_ao.c b/audio_ao.c
index e14b13c..0acc811 100644
--- a/audio_ao.c
+++ b/audio_ao.c
@@ -24,7 +24,6 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
-
#include <stdio.h>
#include <unistd.h>
#include <memory.h>
@@ -35,102 +34,96 @@
ao_device *dev = NULL;
static void help(void) {
- printf(" -d driver set the output driver\n"
- " -o name=value set an arbitrary ao option\n"
- " -i id shorthand for -o id=<id>\n"
- " -n name shorthand for -o dev=<name> -o dsp=<name>\n"
- );
+ printf(" -d driver set the output driver\n"
+ " -o name=value set an arbitrary ao option\n"
+ " -i id shorthand for -o id=<id>\n"
+ " -n name shorthand for -o dev=<name> -o dsp=<name>\n");
}
static int init(int argc, char **argv) {
- ao_initialize();
- int driver = ao_default_driver_id();
- ao_option *ao_opts = NULL;
-
- optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
- argv--; // so we shift the arguments to satisfy getopt()
- argc++;
-
- config.audio_backend_buffer_desired_length = 44100; // one second.
- config.audio_backend_latency_offset = 0;
-
- // some platforms apparently require optreset = 1; - which?
- int opt;
- char *mid;
- while ((opt = getopt(argc, argv, "d:i:n:o:")) > 0) {
- switch (opt) {
- case 'd':
- driver = ao_driver_id(optarg);
- if (driver < 0)
- die("could not find ao driver %s", optarg);
- break;
- case 'i':
- ao_append_option(&ao_opts, "id", optarg);
- break;
- case 'n':
- ao_append_option(&ao_opts, "dev", optarg);
- // Old libao versions (for example, 0.8.8) only support
- // "dsp" instead of "dev".
- ao_append_option(&ao_opts, "dsp", optarg);
- break;
- case 'o':
- mid = strchr(optarg, '=');
- if (!mid)
- die("Expected an = in audio option %s", optarg);
- *mid = 0;
- ao_append_option(&ao_opts, optarg, mid+1);
- break;
- default:
- help();
- die("Invalid audio option -%c specified", opt);
- }
+ ao_initialize();
+ int driver = ao_default_driver_id();
+ ao_option *ao_opts = NULL;
+
+ optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
+ argv--; // so we shift the arguments to satisfy getopt()
+ argc++;
+
+ config.audio_backend_buffer_desired_length = 44100; // one second.
+ config.audio_backend_latency_offset = 0;
+
+ // some platforms apparently require optreset = 1; - which?
+ int opt;
+ char *mid;
+ while ((opt = getopt(argc, argv, "d:i:n:o:")) > 0) {
+ switch (opt) {
+ case 'd':
+ driver = ao_driver_id(optarg);
+ if (driver < 0)
+ die("could not find ao driver %s", optarg);
+ break;
+ case 'i':
+ ao_append_option(&ao_opts, "id", optarg);
+ break;
+ case 'n':
+ ao_append_option(&ao_opts, "dev", optarg);
+ // Old libao versions (for example, 0.8.8) only support
+ // "dsp" instead of "dev".
+ ao_append_option(&ao_opts, "dsp", optarg);
+ break;
+ case 'o':
+ mid = strchr(optarg, '=');
+ if (!mid)
+ die("Expected an = in audio option %s", optarg);
+ *mid = 0;
+ ao_append_option(&ao_opts, optarg, mid + 1);
+ break;
+ default:
+ help();
+ die("Invalid audio option -%c specified", opt);
}
+ }
- if (optind < argc)
- die("Invalid audio argument: %s", argv[optind]);
+ if (optind < argc)
+ die("Invalid audio argument: %s", argv[optind]);
- ao_sample_format fmt;
- memset(&fmt, 0, sizeof(fmt));
+ ao_sample_format fmt;
+ memset(&fmt, 0, sizeof(fmt));
- fmt.bits = 16;
- fmt.rate = 44100;
- fmt.channels = 2;
- fmt.byte_format = AO_FMT_NATIVE;
+ fmt.bits = 16;
+ fmt.rate = 44100;
+ fmt.channels = 2;
+ fmt.byte_format = AO_FMT_NATIVE;
- dev = ao_open_live(driver, &fmt, ao_opts);
+ dev = ao_open_live(driver, &fmt, ao_opts);
- return dev ? 0 : 1;
+ return dev ? 0 : 1;
}
static void deinit(void) {
- if (dev)
- ao_close(dev);
- dev = NULL;
- ao_shutdown();
+ if (dev)
+ ao_close(dev);
+ dev = NULL;
+ ao_shutdown();
}
static void start(int sample_rate) {
- if (sample_rate != 44100)
- die("unexpected sample rate!");
-}
-
-static void play(short buf[], int samples) {
- ao_play(dev, (char*)buf, samples*4);
-}
-
-static void stop(void) {
+ if (sample_rate != 44100)
+ die("unexpected sample rate!");
}
-audio_output audio_ao = {
- .name = "ao",
- .help = &help,
- .init = &init,
- .deinit = &deinit,
- .start = &start,
- .stop = &stop,
- .flush = NULL,
- .delay = NULL,
- .play = &play,
- .volume = NULL,
- .parameters = NULL
-};
+static void play(short buf[], int samples) { ao_play(dev, (char *)buf, samples * 4); }
+
+static void stop(void) {}
+
+audio_output audio_ao = {.name = "ao",
+ .help = &help,
+ .init = &init,
+ .deinit = &deinit,
+ .start = &start,
+ .stop = &stop,
+ .flush = NULL,
+ .delay = NULL,
+ .play = &play,
+ .volume = NULL,
+ .parameters = NULL};
diff --git a/audio_dummy.c b/audio_dummy.c
index 2fd8ebf..57fbec1 100644
--- a/audio_dummy.c
+++ b/audio_dummy.c
@@ -32,41 +32,31 @@
int Fs;
long long starttime, samples_played;
-static int init(int argc, char **argv) {
- return 0;
-}
+static int init(int argc, char **argv) { return 0; }
-static void deinit(void) {
-}
+static void deinit(void) {}
static void start(int sample_rate) {
- Fs = sample_rate;
- starttime = 0;
- samples_played = 0;
- debug(1,"dummy audio output started at Fs=%d Hz\n", sample_rate);
+ Fs = sample_rate;
+ starttime = 0;
+ samples_played = 0;
+ debug(1, "dummy audio output started at Fs=%d Hz\n", sample_rate);
}
-static void play(short buf[], int samples) {
-}
+static void play(short buf[], int samples) {}
-static void stop(void) {
- debug(1,"dummy audio stopped\n");
-}
+static void stop(void) { debug(1, "dummy audio stopped\n"); }
-static void help(void) {
- printf(" There are no options for dummy audio.\n");
-}
+static void help(void) { printf(" There are no options for dummy audio.\n"); }
-audio_output audio_dummy = {
- .name = "dummy",
- .help = &help,
- .init = &init,
- .deinit = &deinit,
- .start = &start,
- .stop = &stop,
- .flush = NULL,
- .delay = NULL,
- .play = &play,
- .volume = NULL,
- .parameters = NULL
-};
+audio_output audio_dummy = {.name = "dummy",
+ .help = &help,
+ .init = &init,
+ .deinit = &deinit,
+ .start = &start,
+ .stop = &stop,
+ .flush = NULL,
+ .delay = NULL,
+ .play = &play,
+ .volume = NULL,
+ .parameters = NULL};
diff --git a/audio_pipe.c b/audio_pipe.c
index 00921e8..12b3336 100644
--- a/audio_pipe.c
+++ b/audio_pipe.c
@@ -38,76 +38,69 @@ static int fd = -1;
char *pipename = NULL;
static void start(int sample_rate) {
- debug(1,"Pipename to start is \"%s\"",pipename);
- if (strcasecmp(pipename,"STDOUT")==0)
+ debug(1, "Pipename to start is \"%s\"", pipename);
+ if (strcasecmp(pipename, "STDOUT") == 0)
fd = STDOUT_FILENO;
else
fd = open(pipename, O_WRONLY);
}
-static void play(short buf[], int samples) {
- int ignore = write(fd, buf, samples*4);
-}
+static void play(short buf[], int samples) { int ignore = write(fd, buf, samples * 4); }
static void stop(void) {
- if (fd!=STDOUT_FILENO)
+ if (fd != STDOUT_FILENO)
close(fd);
}
static int init(int argc, char **argv) {
- config.audio_backend_buffer_desired_length = 44100; // one second.
+ config.audio_backend_buffer_desired_length = 44100; // one second.
config.audio_backend_latency_offset = 0;
- if (config.cfg!=NULL) {
- /* Get the Output Pipename. */
- const char *str;
- if(config_lookup_string(config.cfg, "pipe.name", &str)) {
- pipename = (char*)str;
- }
+ if (config.cfg != NULL) {
+ /* Get the Output Pipename. */
+ const char *str;
+ if (config_lookup_string(config.cfg, "pipe.name", &str)) {
+ pipename = (char *)str;
+ }
}
+ if ((pipename == NULL) && (argc != 1))
+ die("bad or missing argument(s) to pipe");
- if ((pipename==NULL) && (argc != 1))
- die("bad or missing argument(s) to pipe");
+ if (argc == 1)
+ pipename = strdup(argv[0]);
- if (argc==1)
- pipename = strdup(argv[0]);
-
-
- // here, create the pipe
- if (strcasecmp(pipename,"STDOUT")!=0)
- if (mkfifo(pipename, 0644) && errno != EEXIST)
- die("Could not create metadata FIFO %s", pipename);
+ // here, create the pipe
+ if (strcasecmp(pipename, "STDOUT") != 0)
+ if (mkfifo(pipename, 0644) && errno != EEXIST)
+ die("Could not create metadata FIFO %s", pipename);
-
- debug(1,"Pipename is \"%s\"",pipename);
+ debug(1, "Pipename is \"%s\"", pipename);
- // test open pipe so we error on startup if it's going to fail
- start(44100);
- stop();
+ // test open pipe so we error on startup if it's going to fail
+ start(44100);
+ stop();
- return 0;
+ return 0;
}
static void deinit(void) {
- if ((fd > 0) && (fd!=STDOUT_FILENO))
- close(fd);
+ if ((fd > 0) && (fd != STDOUT_FILENO))
+ close(fd);
}
static void help(void) {
- printf(" pipe takes 1 argument: the name of the FIFO to write to, which can be \"stdout\".\n");
+ printf(" pipe takes 1 argument: the name of the FIFO to write to, which can be \"stdout\".\n");
}
-audio_output audio_pipe = {
- .name = "pipe",
- .help = &help,
- .init = &init,
- .deinit = &deinit,
- .start = &start,
- .stop = &stop,
- .flush = NULL,
- .delay = NULL,
- .play = &play,
- .volume = NULL,
- .parameters = NULL
-};
+audio_output audio_pipe = {.name = "pipe",
+ .help = &help,
+ .init = &init,
+ .deinit = &deinit,
+ .start = &start,
+ .stop = &stop,
+ .flush = NULL,
+ .delay = NULL,
+ .play = &play,
+ .volume = NULL,
+ .parameters = NULL};
diff --git a/audio_pulse.c b/audio_pulse.c
index d76c1de..29feed8 100644
--- a/audio_pulse.c
+++ b/audio_pulse.c
@@ -24,7 +24,6 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
-
#include <stdio.h>
#include <unistd.h>
#include <memory.h>
@@ -37,121 +36,105 @@
static pa_simple *pa_dev = NULL;
static struct {
- char *server;
- char *sink;
- char *apname;
-} pulse_options = {
- .server = NULL,
- .sink = NULL,
- .apname = NULL
-};
+ char *server;
+ char *sink;
+ char *apname;
+} pulse_options = {.server = NULL, .sink = NULL, .apname = NULL};
static int pa_error;
static void pulse_connect(void);
static void help(void) {
- printf(" -a server set the server name\n"
- " -s sink set the output sink\n"
- " -n name set the application name, as seen by PulseAudio\n"
- " defaults to the access point name\n"
- );
+ printf(" -a server set the server name\n"
+ " -s sink set the output sink\n"
+ " -n name set the application name, as seen by PulseAudio\n"
+ " defaults to the access point name\n");
}
static int init(int argc, char **argv) {
- pulse_options.apname = config.apname;
-
- config.audio_backend_buffer_desired_length = 44100; // one second.
- config.audio_backend_latency_offset = 0;
-
- optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
- argv--; // so we shift the arguments to satisfy getopt()
- argc++;
-
- // some platforms apparently require optreset = 1; - which?
- int opt;
- while ((opt = getopt(argc, argv, "a:s:n:")) > 0) {
- switch (opt) {
- case 'a':
- pulse_options.server = optarg;
- break;
- case 's':
- pulse_options.sink = optarg;
- break;
- case 'n':
- pulse_options.apname = optarg;
- break;
- default:
- help();
- die("Invalid audio option -%c specified", opt);
- }
+ pulse_options.apname = config.apname;
+
+ config.audio_backend_buffer_desired_length = 44100; // one second.
+ config.audio_backend_latency_offset = 0;
+
+ optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
+ argv--; // so we shift the arguments to satisfy getopt()
+ argc++;
+
+ // some platforms apparently require optreset = 1; - which?
+ int opt;
+ while ((opt = getopt(argc, argv, "a:s:n:")) > 0) {
+ switch (opt) {
+ case 'a':
+ pulse_options.server = optarg;
+ break;
+ case 's':
+ pulse_options.sink = optarg;
+ break;
+ case 'n':
+ pulse_options.apname = optarg;
+ break;
+ default:
+ help();
+ die("Invalid audio option -%c specified", opt);
}
+ }
- if (optind < argc)
- die("Invalid audio argument: %s", argv[optind]);
+ if (optind < argc)
+ die("Invalid audio argument: %s", argv[optind]);
- pulse_connect();
+ pulse_connect();
- return 0;
+ return 0;
}
static void pulse_connect(void) {
- static const pa_sample_spec ss = {
- .format = PA_SAMPLE_S16LE,
- .rate = 44100,
- .channels = 2
- };
-
- pa_dev = pa_simple_new(pulse_options.server,
- pulse_options.apname,
- PA_STREAM_PLAYBACK,
- pulse_options.sink,
- "Shairport Stream",
- &ss, NULL, NULL,
- &pa_error);
-
- if (!pa_dev)
- die("Could not connect to pulseaudio server: %s", pa_strerror(pa_error));
+ static const pa_sample_spec ss = {.format = PA_SAMPLE_S16LE, .rate = 44100, .channels = 2};
+
+ pa_dev = pa_simple_new(pulse_options.server, pulse_options.apname, PA_STREAM_PLAYBACK,
+ pulse_options.sink, "Shairport Stream", &ss, NULL, NULL, &pa_error);
+
+ if (!pa_dev)
+ die("Could not connect to pulseaudio server: %s", pa_strerror(pa_error));
}
static void deinit(void) {
- if (pa_dev)
- pa_simple_free(pa_dev);
- pa_dev = NULL;
+ if (pa_dev)
+ pa_simple_free(pa_dev);
+ pa_dev = NULL;
}
static void start(int sample_rate) {
- if (sample_rate != 44100)
- die("unexpected sample rate!");
+ if (sample_rate != 44100)
+ die("unexpected sample rate!");
}
static void play(short buf[], int samples) {
- if( pa_simple_write(pa_dev, (char *)buf, (size_t)samples * 4, &pa_error) < 0 ) {
- fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(pa_error));
- if (pa_error == PA_ERR_CONNECTIONTERMINATED) {
- fprintf(stderr, __FILE__": reconnecting.");
- deinit();
- pulse_connect();
- }
+ if (pa_simple_write(pa_dev, (char *)buf, (size_t)samples * 4, &pa_error) < 0) {
+ fprintf(stderr, __FILE__ ": pa_simple_write() failed: %s\n", pa_strerror(pa_error));
+ if (pa_error == PA_ERR_CONNECTIONTERMINATED) {
+ fprintf(stderr, __FILE__ ": reconnecting.");
+ deinit();
+ pulse_connect();
}
+ }
}
static void stop(void) {
- if (pa_simple_drain(pa_dev, &pa_error) < 0)
- fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(pa_error));
+ if (pa_simple_drain(pa_dev, &pa_error) < 0)
+ fprintf(stderr, __FILE__ ": pa_simple_drain() failed: %s\n", pa_strerror(pa_error));
}
-audio_output audio_pulse = {
- .name = "pulse",
- .help = &help,
- .init = &init,
- .deinit = &deinit,
- .start = &start,
- .stop = &stop,
- .flush = NULL,
- .delay = NULL,
- .play = &play,
- .volume = NULL,
- .parameters = NULL
-};
+audio_output audio_pulse = {.name = "pulse",
+ .help = &help,
+ .init = &init,
+ .deinit = &deinit,
+ .start = &start,
+ .stop = &stop,
+ .flush = NULL,
+ .delay = NULL,
+ .play = &play,
+ .volume = NULL,
+ .parameters = NULL};
diff --git a/audio_sndio.c b/audio_sndio.c
index 65a4277..e59dbb8 100644
--- a/audio_sndio.c
+++ b/audio_sndio.c
@@ -24,67 +24,61 @@ static struct sio_hdl *sio;
static struct sio_par par;
static int init(int argc, char **argv) {
- sio = sio_open(SIO_DEVANY, SIO_PLAY, 0);
- if (!sio)
- die("sndio: cannot connect to sound server");
+ sio = sio_open(SIO_DEVANY, SIO_PLAY, 0);
+ if (!sio)
+ die("sndio: cannot connect to sound server");
- sio_initpar(&par);
+ sio_initpar(&par);
- par.bits = 16;
- par.rate = 44100;
- par.pchan = 2;
- par.le = SIO_LE_NATIVE;
- par.sig = 1;
+ par.bits = 16;
+ par.rate = 44100;
+ par.pchan = 2;
+ par.le = SIO_LE_NATIVE;
+ par.sig = 1;
- if (!sio_setpar(sio, &par))
- die("sndio: failed to set audio parameters");
- if (!sio_getpar(sio, &par))
- die("sndio: failed to get audio parameters");
-
- config.audio_backend_buffer_desired_length = 44100; // one second.
+ if (!sio_setpar(sio, &par))
+ die("sndio: failed to set audio parameters");
+ if (!sio_getpar(sio, &par))
+ die("sndio: failed to get audio parameters");
+
+ config.audio_backend_buffer_desired_length = 44100; // one second.
config.audio_backend_latency_offset = 0;
- return 0;
+ return 0;
}
-static void deinit(void) {
- sio_close(sio);
-}
+static void deinit(void) { sio_close(sio); }
static void start(int sample_rate) {
- if (sample_rate != par.rate)
- die("unexpected sample rate!");
- sio_start(sio);
+ if (sample_rate != par.rate)
+ die("unexpected sample rate!");
+ sio_start(sio);
}
static void play(short buf[], int samples) {
- sio_write(sio, (char *)buf, samples * par.bps * par.pchan);
+ sio_write(sio, (char *)buf, samples * par.bps * par.pchan);
}
-static void stop(void) {
- sio_stop(sio);
-}
+static void stop(void) { sio_stop(sio); }
static void help(void) {
- printf(" There are no options for sndio audio.\n");
- printf(" Use AUDIODEVICE environment variable.\n");
+ printf(" There are no options for sndio audio.\n");
+ printf(" Use AUDIODEVICE environment variable.\n");
}
static void volume(double vol) {
- unsigned int v = vol * SIO_MAXVOL;
- sio_setvol(sio, v);
+ unsigned int v = vol * SIO_MAXVOL;
+ sio_setvol(sio, v);
}
-audio_output audio_sndio = {
- .name = "sndio",
- .help = &help,
- .init = &init,
- .deinit = &deinit,
- .start = &start,
- .stop = &stop,
- .flush = NULL,
- .delay = NULL,
- .play = &play,
- .volume = &volume,
- .parameters = NULL
-};
+audio_output audio_sndio = {.name = "sndio",
+ .help = &help,
+ .init = &init,
+ .deinit = &deinit,
+ .start = &start,
+ .stop = &stop,
+ .flush = NULL,
+ .delay = NULL,
+ .play = &play,
+ .volume = &volume,
+ .parameters = NULL};
diff --git a/common.c b/common.c
index 1957ffb..fb56c50 100644
--- a/common.c
+++ b/common.c
@@ -64,12 +64,10 @@
#endif
#endif
-
-
#include "common.h"
#include <libdaemon/dlog.h>
-//true if Shairport Sync is supposed to be sending output to the output device, false otherwise
+// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
static volatile int requested_connection_state_to_output = 1;
@@ -77,99 +75,98 @@ shairport_cfg config;
int debuglev = 0;
-int get_requested_connection_state_to_output() {
- return requested_connection_state_to_output;
-}
-
-void set_requested_connection_state_to_output(int v) {
- requested_connection_state_to_output = v;
-}
+int get_requested_connection_state_to_output() { return requested_connection_state_to_output; }
+void set_requested_connection_state_to_output(int v) { requested_connection_state_to_output = v; }
void die(char *format, ...) {
- char s[1024];
- s[0]=0;
- va_list args;
- va_start(args, format);
- vsprintf(s,format,args);
- va_end(args);
- daemon_log(LOG_EMERG,"%s", s);
- shairport_shutdown();
- exit(1);
+ char s[1024];
+ s[0] = 0;
+ va_list args;
+ va_start(args, format);
+ vsprintf(s, format, args);
+ va_end(args);
+ daemon_log(LOG_EMERG, "%s", s);
+ shairport_shutdown();
+ exit(1);
}
void warn(char *format, ...) {
- char s[1024];
- s[0]=0;
- va_list args;
- va_start(args, format);
- vsprintf(s,format,args);
- va_end(args);
- daemon_log(LOG_WARNING,"%s", s);
+ char s[1024];
+ s[0] = 0;
+ va_list args;
+ va_start(args, format);
+ vsprintf(s, format, args);
+ va_end(args);
+ daemon_log(LOG_WARNING, "%s", s);
}
void debug(int level, char *format, ...) {
- if (level > debuglev)
- return;
- char s[1024];
- s[0]=0;
- va_list args;
- va_start(args, format);
- vsprintf(s,format,args);
- va_end(args);
- daemon_log(LOG_DEBUG,"%s", s);
+ if (level > debuglev)
+ return;
+ char s[1024];
+ s[0] = 0;
+ va_list args;
+ va_start(args, format);
+ vsprintf(s, format, args);
+ va_end(args);
+ daemon_log(LOG_DEBUG, "%s", s);
}
void inform(char *format, ...) {
- char s[1024];
- s[0]=0;
- va_list args;
- va_start(args, format);
- vsprintf(s,format,args);
- va_end(args);
- daemon_log(LOG_INFO,"%s", s);
+ char s[1024];
+ s[0] = 0;
+ va_list args;
+ va_start(args, format);
+ vsprintf(s, format, args);
+ va_end(args);
+ daemon_log(LOG_INFO, "%s", s);
}
#ifdef HAVE_LIBPOLARSSL
char *base64_enc(uint8_t *input, int length) {
char *buf = NULL;
size_t dlen = 0;
- int rc = base64_encode(NULL,&dlen,input,length);
- if (rc && (rc!=POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL))
- debug(1,"Error %d getting length of base64 encode.",rc);
+ int rc = base64_encode(NULL, &dlen, input, length);
+ if (rc && (rc != POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL))
+ debug(1, "Error %d getting length of base64 encode.", rc);
else {
buf = (char *)malloc(dlen);
- rc = base64_encode((unsigned char *)buf,&dlen,input,length);
- if (rc!=0)
- debug(1,"Error %d encoding base64.",rc);
+ rc = base64_encode((unsigned char *)buf, &dlen, input, length);
+ if (rc != 0)
+ debug(1, "Error %d encoding base64.", rc);
}
return buf;
}
uint8_t *base64_dec(char *input, int *outlen) {
- // slight problem here is that Apple cut the padding off their challenges. We must restore it before passing it in to the decoder, it seems
+ // slight problem here is that Apple cut the padding off their challenges. We must restore it
+ // before passing it in to the decoder, it seems
uint8_t *buf = NULL;
size_t dlen = 0;
- int inbufsize = ((strlen(input)+3)/4)*4; // this is the size of the input buffer we will send to the decoder, but we need space for 3 extra "="s and a NULL
- char *inbuf = malloc(inbufsize+4);
- if (inbuf==0)
- debug(1,"Can't malloc memory for inbuf in base64_decode.");
+ int inbufsize = ((strlen(input) + 3) / 4) * 4; // this is the size of the input buffer we will
+ // send to the decoder, but we need space for 3
+ // extra "="s and a NULL
+ char *inbuf = malloc(inbufsize + 4);
+ if (inbuf == 0)
+ debug(1, "Can't malloc memory for inbuf in base64_decode.");
else {
- strcpy(inbuf,input);
- strcat(inbuf,"===");
- // debug(1,"base64_dec called with string \"%s\", length %d, filled string: \"%s\", length %d.",input,strlen(input),inbuf,inbufsize);
- int rc = base64_decode(buf,&dlen,(unsigned char *)inbuf,inbufsize);
- if (rc && (rc!=POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL))
- debug(1,"Error %d getting decode length, result is %d.",rc,dlen);
+ strcpy(inbuf, input);
+ strcat(inbuf, "===");
+ // debug(1,"base64_dec called with string \"%s\", length %d, filled string: \"%s\", length
+ // %d.",input,strlen(input),inbuf,inbufsize);
+ int rc = base64_decode(buf, &dlen, (unsigned char *)inbuf, inbufsize);
+ if (rc && (rc != POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL))
+ debug(1, "Error %d getting decode length, result is %d.", rc, dlen);
else {
// debug(1,"Decode size is %d.",dlen);
buf = malloc(dlen);
- if (buf==0)
- debug(1,"Can't allocate memory in base64_dec.");
+ if (buf == 0)
+ debug(1, "Can't allocate memory in base64_dec.");
else {
- rc = base64_decode(buf,&dlen,(unsigned char *)inbuf,inbufsize);
- if (rc!=0)
- debug(1,"Error %d in base64_dec.",rc);
+ rc = base64_decode(buf, &dlen, (unsigned char *)inbuf, inbufsize);
+ if (rc != 0)
+ debug(1, "Error %d in base64_dec.", rc);
}
}
free(inbuf);
@@ -181,221 +178,226 @@ uint8_t *base64_dec(char *input, int *outlen) {
#ifdef HAVE_LIBSSL
char *base64_enc(uint8_t *input, int length) {
- BIO *bmem, *b64;
- BUF_MEM *bptr;
- b64 = BIO_new(BIO_f_base64());
- bmem = BIO_new(BIO_s_mem());
- b64 = BIO_push(b64, bmem);
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- BIO_write(b64, input, length);
- BIO_flush(b64);
- BIO_get_mem_ptr(b64, &bptr);
-
- char *buf = (char *)malloc(bptr->length);
- if (bptr->length) {
- memcpy(buf, bptr->data, bptr->length-1);
- buf[bptr->length-1] = 0;
- }
+ BIO *bmem, *b64;
+ BUF_MEM *bptr;
+ b64 = BIO_new(BIO_f_base64());
+ bmem = BIO_new(BIO_s_mem());
+ b64 = BIO_push(b64, bmem);
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ BIO_write(b64, input, length);
+ BIO_flush(b64);
+ BIO_get_mem_ptr(b64, &bptr);
+
+ char *buf = (char *)malloc(bptr->length);
+ if (bptr->length) {
+ memcpy(buf, bptr->data, bptr->length - 1);
+ buf[bptr->length - 1] = 0;
+ }
- BIO_free_all(bmem);
+ BIO_free_all(bmem);
- return buf;
+ return buf;
}
uint8_t *base64_dec(char *input, int *outlen) {
- BIO *bmem, *b64;
- int inlen = strlen(input);
+ BIO *bmem, *b64;
+ int inlen = strlen(input);
- b64 = BIO_new(BIO_f_base64());
- BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
- bmem = BIO_new(BIO_s_mem());
- b64 = BIO_push(b64, bmem);
+ b64 = BIO_new(BIO_f_base64());
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ bmem = BIO_new(BIO_s_mem());
+ b64 = BIO_push(b64, bmem);
- // Apple cut the padding off their challenges; restore it
- BIO_write(bmem, input, inlen);
- while (inlen++ & 3)
- BIO_write(bmem, "=", 1);
- BIO_flush(bmem);
+ // Apple cut the padding off their challenges; restore it
+ BIO_write(bmem, input, inlen);
+ while (inlen++ & 3)
+ BIO_write(bmem, "=", 1);
+ BIO_flush(bmem);
- int bufsize = strlen(input)*3/4 + 1;
- uint8_t *buf = malloc(bufsize);
- int nread;
+ int bufsize = strlen(input) * 3 / 4 + 1;
+ uint8_t *buf = malloc(bufsize);
+ int nread;
- nread = BIO_read(b64, buf, bufsize);
+ nread = BIO_read(b64, buf, bufsize);
- BIO_free_all(bmem);
+ BIO_free_all(bmem);
- *outlen = nread;
- return buf;
+ *outlen = nread;
+ return buf;
}
#endif
static char super_secret_key[] =
-"-----BEGIN RSA PRIVATE KEY-----\n"
-"MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n"
-"wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\n"
-"wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\n"
-"/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\n"
-"UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\n"
-"BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\n"
-"LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\n"
-"NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\n"
-"lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\n"
-"aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\n"
-"a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\n"
-"oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\n"
-"oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\n"
-"k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\n"
-"AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\n"
-"cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\n"
-"54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\n"
-"17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\n"
-"1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\n"
-"LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\n"
-"2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\n"
-"-----END RSA PRIVATE KEY-----\0";
+ "-----BEGIN RSA PRIVATE KEY-----\n"
+ "MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n"
+ "wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\n"
+ "wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\n"
+ "/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\n"
+ "UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\n"
+ "BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\n"
+ "LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\n"
+ "NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\n"
+ "lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\n"
+ "aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\n"
+ "a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\n"
+ "oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\n"
+ "oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\n"
+ "k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\n"
+ "AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\n"
+ "cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\n"
+ "54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\n"
+ "17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\n"
+ "1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\n"
+ "LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\n"
+ "2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\n"
+ "-----END RSA PRIVATE KEY-----\0";
#ifdef HAVE_LIBSSL
uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode) {
-static RSA *rsa = NULL;
-
- if (!rsa) {
- BIO *bmem = BIO_new_mem_buf(super_secret_key, -1);
- rsa = PEM_read_bio_RSAPrivateKey(bmem, NULL, NULL, NULL);
- BIO_free(bmem);
- }
-
- uint8_t *out = malloc(RSA_size(rsa));
- switch (mode) {
- case RSA_MODE_AUTH:
- *outlen = RSA_private_encrypt(inlen, input, out, rsa,
- RSA_PKCS1_PADDING);
- break;
- case RSA_MODE_KEY:
- *outlen = RSA_private_decrypt(inlen, input, out, rsa,
- RSA_PKCS1_OAEP_PADDING);
- break;
- default:
- die("bad rsa mode");
- }
- return out;
+ static RSA *rsa = NULL;
+
+ if (!rsa) {
+ BIO *bmem = BIO_new_mem_buf(super_secret_key, -1);
+ rsa = PEM_read_bio_RSAPrivateKey(bmem, NULL, NULL, NULL);
+ BIO_free(bmem);
+ }
+
+ uint8_t *out = malloc(RSA_size(rsa));
+ switch (mode) {
+ case RSA_MODE_AUTH:
+ *outlen = RSA_private_encrypt(inlen, input, out, rsa, RSA_PKCS1_PADDING);
+ break;
+ case RSA_MODE_KEY:
+ *outlen = RSA_private_decrypt(inlen, input, out, rsa, RSA_PKCS1_OAEP_PADDING);
+ break;
+ default:
+ die("bad rsa mode");
+ }
+ return out;
}
#endif
#ifdef HAVE_LIBPOLARSSL
uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode) {
- rsa_context trsa;
- const char *pers = "rsa_encrypt";
- int rc;
-
- entropy_context entropy;
- ctr_drbg_context ctr_drbg;
- entropy_init( &entropy );
- if( ( rc = ctr_drbg_init( &ctr_drbg, entropy_func, &entropy,(const unsigned char *) pers,strlen( pers ) ) ) != 0 )
- debug(1, "ctr_drbg_init returned %d\n", rc );
-
- rsa_init(&trsa,RSA_PKCS_V21,POLARSSL_MD_SHA1); // padding and hash id get overwritten
- // BTW, this seems to reset a lot of parameters in the rsa_context
- rc = x509parse_key(&trsa,(unsigned char *)super_secret_key,strlen(super_secret_key),NULL,0);
- if (rc!=0)
- debug(1,"Error %d reading the private key.");
-
- uint8_t *out = NULL;
-
- switch (mode) {
- case RSA_MODE_AUTH:
- trsa.padding = RSA_PKCS_V15;
- trsa.hash_id = POLARSSL_MD_NONE;
- debug(2,"rsa_apply encrypt");
- out = malloc(trsa.len);
- rc = rsa_pkcs1_encrypt( &trsa, ctr_drbg_random, &ctr_drbg, RSA_PRIVATE, inlen, input, out );
- if (rc!=0)
- debug(1,"rsa_pkcs1_encrypt error %d.",rc);
- *outlen=trsa.len;
- break;
- case RSA_MODE_KEY:
- debug(2,"rsa_apply decrypt");
- trsa.padding=RSA_PKCS_V21;
- trsa.hash_id=POLARSSL_MD_SHA1;
- out = malloc(trsa.len);
+ rsa_context trsa;
+ const char *pers = "rsa_encrypt";
+ int rc;
+
+ entropy_context entropy;
+ ctr_drbg_context ctr_drbg;
+ entropy_init(&entropy);
+ if ((rc = ctr_drbg_init(&ctr_drbg, entropy_func, &entropy, (const unsigned char *)pers,
+ strlen(pers))) != 0)
+ debug(1, "ctr_drbg_init returned %d\n", rc);
+
+ rsa_init(&trsa, RSA_PKCS_V21, POLARSSL_MD_SHA1); // padding and hash id get overwritten
+ // BTW, this seems to reset a lot of parameters in the rsa_context
+ rc = x509parse_key(&trsa, (unsigned char *)super_secret_key, strlen(super_secret_key), NULL, 0);
+ if (rc != 0)
+ debug(1, "Error %d reading the private key.");
+
+ uint8_t *out = NULL;
+
+ switch (mode) {
+ case RSA_MODE_AUTH:
+ trsa.padding = RSA_PKCS_V15;
+ trsa.hash_id = POLARSSL_MD_NONE;
+ debug(2, "rsa_apply encrypt");
+ out = malloc(trsa.len);
+ rc = rsa_pkcs1_encrypt(&trsa, ctr_drbg_random, &ctr_drbg, RSA_PRIVATE, inlen, input, out);
+ if (rc != 0)
+ debug(1, "rsa_pkcs1_encrypt error %d.", rc);
+ *outlen = trsa.len;
+ break;
+ case RSA_MODE_KEY:
+ debug(2, "rsa_apply decrypt");
+ trsa.padding = RSA_PKCS_V21;
+ trsa.hash_id = POLARSSL_MD_SHA1;
+ out = malloc(trsa.len);
#if POLARSSL_VERSION_NUMBER >= 0x01020900
- rc = rsa_pkcs1_decrypt(&trsa, ctr_drbg_random, &ctr_drbg, RSA_PRIVATE, (size_t *)outlen, input, out, trsa.len);
+ rc = rsa_pkcs1_decrypt(&trsa, ctr_drbg_random, &ctr_drbg, RSA_PRIVATE, (size_t *)outlen, input,
+ out, trsa.len);
#else
- rc = rsa_pkcs1_decrypt(&trsa, RSA_PRIVATE, outlen, input, out, trsa.len);
+ rc = rsa_pkcs1_decrypt(&trsa, RSA_PRIVATE, outlen, input, out, trsa.len);
#endif
- if (rc!=0)
- debug(1,"decrypt error %d.",rc);
- break;
- default:
- die("bad rsa mode");
- }
- rsa_free(&trsa);
- debug(2,"rsa_apply exit");
- return out;
+ if (rc != 0)
+ debug(1, "decrypt error %d.", rc);
+ break;
+ default:
+ die("bad rsa mode");
+ }
+ rsa_free(&trsa);
+ debug(2, "rsa_apply exit");
+ return out;
}
#endif
void command_start(void) {
- if (config.cmd_start) {
- /*Spawn a child to run the program.*/
- pid_t pid=fork();
- if (pid==0) { /* child process */
- int argC;
- char **argV;
- // debug(1,"on-start command found.");
- if (poptParseArgvString(config.cmd_start,&argC,(const char ***)&argV)!=0) // note that argV should be free()'d after use, but we expect this fork to exit eventually.
- debug(1,"Can't decipher on-start command arguments");
- else {
- // debug(1,"Executing on-start command %s with %d arguments.",argV[0],argC);
- execv(argV[0],argV);
- warn("Execution of on-start command failed to start");
- debug(1,"Error executing on-start command %s",config.cmd_start);
- exit(127); /* only if execv fails */
- }
- } else {
- if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for process to finish */
- pid_t rc = waitpid(pid,0,0); /* wait for child to exit */
- if (rc!=pid) {
- warn("Execution of on-start command returned an error.");
- debug(1,"on-start command %s finished with error %d",config.cmd_start,errno);
- }
- }
- // debug(1,"Continue after on-start command");
- }
- }
+ if (config.cmd_start) {
+ /*Spawn a child to run the program.*/
+ pid_t pid = fork();
+ if (pid == 0) { /* child process */
+ int argC;
+ char **argV;
+ // debug(1,"on-start command found.");
+ if (poptParseArgvString(config.cmd_start, &argC, (const char ***)&argV) !=
+ 0) // note that argV should be free()'d after use, but we expect this fork to exit
+ // eventually.
+ debug(1, "Can't decipher on-start command arguments");
+ else {
+ // debug(1,"Executing on-start command %s with %d arguments.",argV[0],argC);
+ execv(argV[0], argV);
+ warn("Execution of on-start command failed to start");
+ debug(1, "Error executing on-start command %s", config.cmd_start);
+ exit(127); /* only if execv fails */
+ }
+ } else {
+ if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for
+ process to finish */
+ pid_t rc = waitpid(pid, 0, 0); /* wait for child to exit */
+ if (rc != pid) {
+ warn("Execution of on-start command returned an error.");
+ debug(1, "on-start command %s finished with error %d", config.cmd_start, errno);
+ }
+ }
+ // debug(1,"Continue after on-start command");
+ }
+ }
}
void command_stop(void) {
- if (config.cmd_stop) {
- /*Spawn a child to run the program.*/
- pid_t pid=fork();
- if (pid==0) { /* child process */
- int argC;
- char **argV;
- // debug(1,"on-stop command found.");
- if (poptParseArgvString(config.cmd_stop,&argC,(const char ***)&argV)!=0) // note that argV should be free()'d after use, but we expect this fork to exit eventually.
- debug(1,"Can't decipher on-stop command arguments");
+ if (config.cmd_stop) {
+ /*Spawn a child to run the program.*/
+ pid_t pid = fork();
+ if (pid == 0) { /* child process */
+ int argC;
+ char **argV;
+ // debug(1,"on-stop command found.");
+ if (poptParseArgvString(config.cmd_stop, &argC, (const char ***)&argV) !=
+ 0) // note that argV should be free()'d after use, but we expect this fork to exit
+ // eventually.
+ debug(1, "Can't decipher on-stop command arguments");
else {
- // debug(1,"Executing on-stop command %s",config.cmd_stop);
- execv(argV[0],argV);
- warn("Execution of on-stop command failed to start");
- debug(1,"Error executing on-stop command %s",config.cmd_stop);
- exit(127); /* only if execv fails */
- }
- } else {
- if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for process to finish */
- pid_t rc = waitpid(pid,0,0); /* wait for child to exit */
- if (rc!=pid) {
- warn("Execution of on-stop command returned an error.");
- debug(1,"Stop command %s finished with error %d",config.cmd_stop,errno);
- }
- }
- // debug(1,"Continue after on-stop command");
- }
- }
+ // debug(1,"Executing on-stop command %s",config.cmd_stop);
+ execv(argV[0], argV);
+ warn("Execution of on-stop command failed to start");
+ debug(1, "Error executing on-stop command %s", config.cmd_stop);
+ exit(127); /* only if execv fails */
+ }
+ } else {
+ if (config.cmd_blocking) { /* pid!=0 means parent process and if blocking is true, wait for
+ process to finish */
+ pid_t rc = waitpid(pid, 0, 0); /* wait for child to exit */
+ if (rc != pid) {
+ warn("Execution of on-stop command returned an error.");
+ debug(1, "Stop command %s finished with error %d", config.cmd_stop, errno);
+ }
+ }
+ // debug(1,"Continue after on-stop command");
+ }
+ }
}
-
// this is for reading an unsigned 32 bit number, such as an RTP timestamp
uint32_t uatoi(const char *nptr) {
@@ -404,19 +406,25 @@ uint32_t uatoi(const char *nptr) {
return r;
}
-// Given a volume (0 to -30) and high and low attenuations available in the mixer in dB, return an attenuation depending on the volume and the function's transfer function
+// Given a volume (0 to -30) and high and low attenuations available in the mixer in dB, return an
+// attenuation depending on the volume and the function's transfer function
// See http://tangentsoft.net/audio/atten.html for data on good attenuators.
-// We want a smooth attenuation function, like, for example, the ALPS RK27 Potentiometer transfer functions referred to at the link above.
+// We want a smooth attenuation function, like, for example, the ALPS RK27 Potentiometer transfer
+// functions referred to at the link above.
// Note that the max_db and min_db are given as dB*100
-double vol2attn(double vol, long max_db, long min_db) {
+double vol2attn(double vol, long max_db, long min_db) {
-// We use a little coordinate geometry to build a transfer function from the volume passed in to the device's dynamic range.
+// We use a little coordinate geometry to build a transfer function from the volume passed in to the
+// device's dynamic range.
// (See the diagram in the documents folder.)
-// The x axis is the "volume in" which will be from -30 to 0. The y axis will be the "volume out" which will be from the bottom of the range to the top.
-// We build the transfer function from one or more lines. We characterise each line with two numbers:
-// the first is where on x the line starts when y=0 (x can be from 0 to -30); the second is where on y the line stops when when x is -30.
+// The x axis is the "volume in" which will be from -30 to 0. The y axis will be the "volume out"
+// which will be from the bottom of the range to the top.
+// We build the transfer function from one or more lines. We characterise each line with two
+// numbers:
+// the first is where on x the line starts when y=0 (x can be from 0 to -30); the second is where on
+// y the line stops when when x is -30.
// thus, if the line was characterised as {0,-30}, it would be an identity transfer.
// Assuming, for example, a dynamic range of lv=-60 to hv=0
// Typically we'll use three lines -- a three order transfer function
@@ -427,27 +435,33 @@ double vol2attn(double vol, long max_db, long min_db) {
#define order 3
double vol_setting = max_db;
-
- if ((vol<=0.0) && (vol>=-30.0)) {
- long range_db = max_db-min_db; // this will be a positive nunmber
+
+ if ((vol <= 0.0) && (vol >= -30.0)) {
+ long range_db = max_db - min_db; // this will be a positive nunmber
// debug(1,"Volume min %ddB, max %ddB, range %ddB.",min_db,max_db,range_db);
- //double first_slope = -3000.0; // this is the slope of the attenuation at the high end -- 30dB for the full rotation.
- double first_slope = -range_db/2; // this is the slope of the attenuation at the high end -- 30dB for the full rotation.
- if (-range_db>first_slope)
+ // double first_slope = -3000.0; // this is the slope of the attenuation at the high end -- 30dB
+ // for the full rotation.
+ double first_slope =
+ -range_db /
+ 2; // this is the slope of the attenuation at the high end -- 30dB for the full rotation.
+ if (-range_db > first_slope)
first_slope = range_db;
- double lines[order][2] = {{0,first_slope},{-5,first_slope-(range_db+first_slope)/2},{-17,-range_db}};
+ double lines[order][2] = {
+ {0, first_slope}, {-5, first_slope - (range_db + first_slope) / 2}, {-17, -range_db}};
int i;
- for (i=0;i<order;i++) {
- if (vol<=lines[i][0]) {
- double tvol = lines[i][1]*(vol-lines[i][0])/(-30-lines[i][0]);
- // debug(1,"On line %d, end point of %f, input vol %f yields output vol %f.",i,lines[i][1],vol,tvol);
- if (tvol<vol_setting)
- vol_setting=tvol;
+ for (i = 0; i < order; i++) {
+ if (vol <= lines[i][0]) {
+ double tvol = lines[i][1] * (vol - lines[i][0]) / (-30 - lines[i][0]);
+ // debug(1,"On line %d, end point of %f, input vol %f yields output vol
+ // %f.",i,lines[i][1],vol,tvol);
+ if (tvol < vol_setting)
+ vol_setting = tvol;
}
}
- vol_setting+=max_db;
- } else if (vol!=-144.0) {
- debug(1,"Volume request value %f is out of range: should be from 0.0 to -30.0 or -144.0.",vol);
+ vol_setting += max_db;
+ } else if (vol != -144.0) {
+ debug(1, "Volume request value %f is out of range: should be from 0.0 to -30.0 or -144.0.",
+ vol);
} else {
vol_setting = min_db; // for safety, return the lowest setting...
}
@@ -456,39 +470,40 @@ double vol2attn(double vol, long max_db, long min_db) {
}
uint64_t get_absolute_time_in_fp() {
- uint64_t time_now_fp;
+ uint64_t time_now_fp;
#ifdef COMPILE_FOR_LINUX
- struct timespec tn;
- clock_gettime(CLOCK_MONOTONIC,&tn);
- time_now_fp=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000;
+ struct timespec tn;
+ clock_gettime(CLOCK_MONOTONIC, &tn);
+ time_now_fp = ((uint64_t)tn.tv_sec << 32) + ((uint64_t)tn.tv_nsec << 32) / 1000000000;
#endif
#ifdef COMPILE_FOR_OSX
- uint64_t time_now_mach;
- uint64_t elapsedNano;
- static mach_timebase_info_data_t sTimebaseInfo = {0,0};
+ uint64_t time_now_mach;
+ uint64_t elapsedNano;
+ static mach_timebase_info_data_t sTimebaseInfo = {0, 0};
- time_now_mach = mach_absolute_time();
+ time_now_mach = mach_absolute_time();
- // If this is the first time we've run, get the timebase.
- // We can use denom == 0 to indicate that sTimebaseInfo is
- // uninitialised because it makes no sense to have a zero
- // denominator in a fraction.
+ // If this is the first time we've run, get the timebase.
+ // We can use denom == 0 to indicate that sTimebaseInfo is
+ // uninitialised because it makes no sense to have a zero
+ // denominator in a fraction.
- if ( sTimebaseInfo.denom == 0 ) {
- debug(1,"Mac initialise timebase info.");
- (void) mach_timebase_info(&sTimebaseInfo);
- }
+ if (sTimebaseInfo.denom == 0) {
+ debug(1, "Mac initialise timebase info.");
+ (void)mach_timebase_info(&sTimebaseInfo);
+ }
+
+ // Do the maths. We hope that the multiplication doesn't
+ // overflow; the price you pay for working in fixed point.
- // Do the maths. We hope that the multiplication doesn't
- // overflow; the price you pay for working in fixed point.
+ // this gives us nanoseconds
+ uint64_t time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom;
- // this gives us nanoseconds
- uint64_t time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom;
-
- // take the units and shift them to the upper half of the fp, and take the nanoseconds, shift them to the upper half and then divide the result to 1000000000
- time_now_fp = ((time_now_ns/1000000000)<<32) + (((time_now_ns%1000000000)<<32)/1000000000);
+ // take the units and shift them to the upper half of the fp, and take the nanoseconds, shift them
+ // to the upper half and then divide the result to 1000000000
+ time_now_fp =
+ ((time_now_ns / 1000000000) << 32) + (((time_now_ns % 1000000000) << 32) / 1000000000);
#endif
return time_now_fp;
}
-
diff --git a/common.h b/common.h
index 710eb0b..6a9436b 100644
--- a/common.h
+++ b/common.h
@@ -10,77 +10,79 @@
#include "mdns.h"
#if defined(__APPLE__) && defined(__MACH__)
- /* Apple OSX and iOS (Darwin). ------------------------------ */
+/* Apple OSX and iOS (Darwin). ------------------------------ */
#include <TargetConditionals.h>
#if TARGET_OS_MAC == 1
- /* OSX */
- #define COMPILE_FOR_OSX 1
+/* OSX */
+#define COMPILE_FOR_OSX 1
#endif
#endif
#if defined(__linux__)
- /* Linux. --------------------------------------------------- */
- #define COMPILE_FOR_LINUX 1
+/* Linux. --------------------------------------------------- */
+#define COMPILE_FOR_LINUX 1
#endif
// struct sockaddr_in6 is bigger than struct sockaddr. derp
#ifdef AF_INET6
- #define SOCKADDR struct sockaddr_storage
- #define SAFAMILY ss_family
+#define SOCKADDR struct sockaddr_storage
+#define SAFAMILY ss_family
#else
- #define SOCKADDR struct sockaddr
- #define SAFAMILY sa_family
+#define SOCKADDR struct sockaddr
+#define SAFAMILY sa_family
#endif
-
enum stuffing_type {
- ST_basic = 0,
+ ST_basic = 0,
ST_soxr,
} type;
-
-
typedef struct {
- config_t* cfg;
- char *password;
- char *apname;
+ config_t *cfg;
+ char *password;
+ char *apname;
#ifdef CONFIG_METADATA
- int metadata_enabled;
- char *metadata_pipename;
- int get_coverart;
+ int metadata_enabled;
+ char *metadata_pipename;
+ int get_coverart;
#endif
- uint8_t hw_addr[6];
- int port;
- int ignore_volume_control;
- int resyncthreshold; // if it get's out of whack my more than this, resync. Zero means never resync.
- int allow_session_interruption;
- int timeout; // while in play mode, exit if no packets of audio come in for more than this number of seconds . Zero means never exit.
- int dont_check_timeout; // this is used to maintain backward compatability with the old -t option behaviour; only set by -t 0, cleared by everything else
- char *output_name;
- audio_output *output;
- char *mdns_name;
- mdns_backend *mdns;
- int buffer_start_fill;
- uint32_t latency;
- uint32_t userSuppliedLatency; // overrides all other latencies -- use with caution
- uint32_t iTunesLatency; // supplied with --iTunesLatency option
- uint32_t AirPlayLatency; //supplied with --AirPlayLatency option
- uint32_t ForkedDaapdLatency; //supplied with --ForkedDaapdLatency option
- int daemonise;
- int statistics_requested;
- char *cmd_start, *cmd_stop;
- int cmd_blocking;
- int tolerance; // allow this much drift before attempting to correct it
- enum stuffing_type packet_stuffing;
- char *pidfile;
- char *logfile;
- char *errfile;
- char *configfile;
- uint audio_backend_buffer_desired_length; // this will be the desired number of frames in the audio backend buffer -- the DAC buffer for ALSA
- uint audio_backend_latency_offset; // this will be the offset to compensate for any fixed latency there might be in the audio
+ uint8_t hw_addr[6];
+ int port;
+ int ignore_volume_control;
+ int resyncthreshold; // if it get's out of whack my more than this, resync. Zero means never
+ // resync.
+ int allow_session_interruption;
+ int timeout; // while in play mode, exit if no packets of audio come in for more than this number
+ // of seconds . Zero means never exit.
+ int dont_check_timeout; // this is used to maintain backward compatability with the old -t option
+ // behaviour; only set by -t 0, cleared by everything else
+ char *output_name;
+ audio_output *output;
+ char *mdns_name;
+ mdns_backend *mdns;
+ int buffer_start_fill;
+ uint32_t latency;
+ uint32_t userSuppliedLatency; // overrides all other latencies -- use with caution
+ uint32_t iTunesLatency; // supplied with --iTunesLatency option
+ uint32_t AirPlayLatency; // supplied with --AirPlayLatency option
+ uint32_t ForkedDaapdLatency; // supplied with --ForkedDaapdLatency option
+ int daemonise;
+ int statistics_requested;
+ char *cmd_start, *cmd_stop;
+ int cmd_blocking;
+ int tolerance; // allow this much drift before attempting to correct it
+ enum stuffing_type packet_stuffing;
+ char *pidfile;
+ char *logfile;
+ char *errfile;
+ char *configfile;
+ uint audio_backend_buffer_desired_length; // this will be the desired number of frames in the
+ // audio backend buffer -- the DAC buffer for ALSA
+ uint audio_backend_latency_offset; // this will be the offset to compensate for any fixed latency
+ // there might be in the audio
} shairport_cfg;
-//true if Shairport Sync is supposed to be sending output to the output device, false otherwise
+// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
int get_requested_connection_state_to_output();
@@ -96,10 +98,11 @@ uint8_t *base64_dec(char *input, int *outlen);
char *base64_enc(uint8_t *input, int length);
#define RSA_MODE_AUTH (0)
-#define RSA_MODE_KEY (1)
+#define RSA_MODE_KEY (1)
uint8_t *rsa_apply(uint8_t *input, int inlen, int *outlen, int mode);
-// given a volume (0 to -30) and high and low attenuations in dB*100 (e.g. 0 to -6000 for 0 to -60 dB), return an attenuation depending on the transfer function
+// given a volume (0 to -30) and high and low attenuations in dB*100 (e.g. 0 to -6000 for 0 to -60
+// dB), return an attenuation depending on the transfer function
double vol2attn(double vol, long max_db, long min_db);
// return a monolithic (always increasing) time in nanoseconds
diff --git a/mdns.c b/mdns.c
index 53b5df7..f4491fd 100644
--- a/mdns.c
+++ b/mdns.c
@@ -24,7 +24,6 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
-
#include <memory.h>
#include <string.h>
#include <stdio.h>
@@ -47,78 +46,66 @@ extern mdns_backend mdns_tinysvcmdns;
static mdns_backend *mdns_backends[] = {
#ifdef CONFIG_AVAHI
- &mdns_avahi,
- &mdns_external_avahi,
+ &mdns_avahi, &mdns_external_avahi,
#endif
#ifdef CONFIG_HAVE_DNS_SD_H
- &mdns_dns_sd,
- &mdns_external_dns_sd,
+ &mdns_dns_sd, &mdns_external_dns_sd,
#endif
#ifdef CONFIG_TINYSVCMDNS
&mdns_tinysvcmdns,
#endif
- NULL
-};
+ NULL};
void mdns_register(void) {
- char *mdns_apname = alloca(strlen(config.apname) + 14);
- char *p = mdns_apname;
- int i;
- for (i=0; i<6; i++) {
- sprintf(p, "%02X", config.hw_addr[i]);
- p += 2;
- }
- *p++ = '@';
- strcpy(p, config.apname);
+ char *mdns_apname = alloca(strlen(config.apname) + 14);
+ char *p = mdns_apname;
+ int i;
+ for (i = 0; i < 6; i++) {
+ sprintf(p, "%02X", config.hw_addr[i]);
+ p += 2;
+ }
+ *p++ = '@';
+ strcpy(p, config.apname);
- mdns_backend **b = NULL;
-
- if (config.mdns_name != NULL)
- {
- for (b = mdns_backends; *b; b++)
- {
- if (strcmp((*b)->name, config.mdns_name) != 0) // Not the one we are looking for
- continue;
- int error = (*b)->mdns_register(mdns_apname, config.port);
- if (error >= 0)
- {
- config.mdns = *b;
- }
- break;
- }
+ mdns_backend **b = NULL;
- if (*b == NULL)
- warn("%s mDNS backend not found");
+ if (config.mdns_name != NULL) {
+ for (b = mdns_backends; *b; b++) {
+ if (strcmp((*b)->name, config.mdns_name) != 0) // Not the one we are looking for
+ continue;
+ int error = (*b)->mdns_register(mdns_apname, config.port);
+ if (error >= 0) {
+ config.mdns = *b;
+ }
+ break;
}
- else
- {
- for (b = mdns_backends; *b; b++)
- {
- int error = (*b)->mdns_register(mdns_apname, config.port);
- if (error >= 0)
- {
- config.mdns = *b;
- break;
- }
- }
+
+ if (*b == NULL)
+ warn("%s mDNS backend not found");
+ } else {
+ for (b = mdns_backends; *b; b++) {
+ int error = (*b)->mdns_register(mdns_apname, config.port);
+ if (error >= 0) {
+ config.mdns = *b;
+ break;
+ }
}
+ }
- if (config.mdns == NULL)
- die("Could not establish mDNS advertisement!");
+ if (config.mdns == NULL)
+ die("Could not establish mDNS advertisement!");
}
void mdns_unregister(void) {
- if (config.mdns) {
- config.mdns->mdns_unregister();
- }
+ if (config.mdns) {
+ config.mdns->mdns_unregister();
+ }
}
void mdns_ls_backends(void) {
- mdns_backend **b = NULL;
- printf("Available mDNS backends: \n");
- for (b = mdns_backends; *b; b++)
- {
- printf(" %s\n", (*b)->name);
- }
+ mdns_backend **b = NULL;
+ printf("Available mDNS backends: \n");
+ for (b = mdns_backends; *b; b++) {
+ printf(" %s\n", (*b)->name);
+ }
}
-
diff --git a/mdns.h b/mdns.h
index 247b9a6..a15b77f 100644
--- a/mdns.h
+++ b/mdns.h
@@ -10,9 +10,9 @@ void mdns_register(void);
void mdns_ls_backends(void);
typedef struct {
- char *name;
- int (*mdns_register)(char *apname, int port);
- void (*mdns_unregister)(void);
+ char *name;
+ int (*mdns_register)(char *apname, int port);
+ void (*mdns_unregister)(void);
} mdns_backend;
// text and progress only -- picture feed really buggy from iTunes
@@ -21,13 +21,13 @@ typedef struct {
#define METADATA_EXPRESSION config.get_coverart ? "md=0,1,2" : "md=0,2"
-#define MDNS_RECORD_WITH_METADATA "tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", METADATA_EXPRESSION , \
- "ss=16", "sr=44100", "vn=3", "txtvers=1", \
- config.password ? "pw=true" : "pw=false"
+#define MDNS_RECORD_WITH_METADATA \
+ "tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", METADATA_EXPRESSION, "ss=16", \
+ "sr=44100", "vn=3", "txtvers=1", config.password ? "pw=true" : "pw=false"
#endif
-#define MDNS_RECORD_WITHOUT_METADATA "tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", \
- "ss=16", "sr=44100", "vn=3", "txtvers=1", \
- config.password ? "pw=true" : "pw=false"
+#define MDNS_RECORD_WITHOUT_METADATA \
+ "tp=UDP", "sm=false", "ek=1", "et=0,1", "cn=0,1", "ch=2", "ss=16", "sr=44100", "vn=3", \
+ "txtvers=1", config.password ? "pw=true" : "pw=false"
#endif // _MDNS_H
diff --git a/mdns_avahi.c b/mdns_avahi.c
index 35e397d..31800b8 100644
--- a/mdns_avahi.c
+++ b/mdns_avahi.c
@@ -42,131 +42,104 @@ static AvahiThreadedPoll *tpoll = NULL;
static char *name = NULL;
static int port = 0;
-static void egroup_callback(AvahiEntryGroup *g,
- AvahiEntryGroupState state,
+static void egroup_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
AVAHI_GCC_UNUSED void *userdata) {
- if (state==AVAHI_ENTRY_GROUP_COLLISION)
- die("service name already exists on network!");
- if (state==AVAHI_ENTRY_GROUP_FAILURE)
- die("avahi entry group failure!");
+ if (state == AVAHI_ENTRY_GROUP_COLLISION)
+ die("service name already exists on network!");
+ if (state == AVAHI_ENTRY_GROUP_FAILURE)
+ die("avahi entry group failure!");
}
static void register_service(AvahiClient *c) {
- debug(1, "avahi: register_service.");
- if (!group)
- group = avahi_entry_group_new(c, egroup_callback, NULL);
- if (!group)
- die("avahi_entry_group_new failed");
+ debug(1, "avahi: register_service.");
+ if (!group)
+ group = avahi_entry_group_new(c, egroup_callback, NULL);
+ if (!group)
+ die("avahi_entry_group_new failed");
- if (!avahi_entry_group_is_empty(group))
- return;
+ if (!avahi_entry_group_is_empty(group))
+ return;
- int ret;
+ int ret;
#ifdef CONFIG_METADATA
- if (config.metadata_enabled) {
- debug(1,"Avahi with metadata");
- ret = avahi_entry_group_add_service(group,
- AVAHI_IF_UNSPEC,
- AVAHI_PROTO_UNSPEC,
- 0,
- name,
- "_raop._tcp",
- NULL,
- NULL,
- port,
- MDNS_RECORD_WITH_METADATA,
- NULL);
- }
- else {
+ if (config.metadata_enabled) {
+ debug(1, "Avahi with metadata");
+ ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name,
+ "_raop._tcp", NULL, NULL, port, MDNS_RECORD_WITH_METADATA,
+ NULL);
+ } else {
#endif
- debug(1,"Avahi without metadata");
- ret = avahi_entry_group_add_service(group,
- AVAHI_IF_UNSPEC,
- AVAHI_PROTO_UNSPEC,
- 0,
- name,
- "_raop._tcp",
- NULL,
- NULL,
- port,
- MDNS_RECORD_WITHOUT_METADATA,
- NULL);
-#ifdef CONFIG_METADATA
- }
+ debug(1, "Avahi without metadata");
+ ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name,
+ "_raop._tcp", NULL, NULL, port,
+ MDNS_RECORD_WITHOUT_METADATA, NULL);
+#ifdef CONFIG_METADATA
+ }
#endif
-
- if (ret < 0)
- die("avahi_entry_group_add_service failed");
- ret = avahi_entry_group_commit(group);
- if (ret < 0)
- die("avahi_entry_group_commit failed");
+ if (ret < 0)
+ die("avahi_entry_group_add_service failed");
+
+ ret = avahi_entry_group_commit(group);
+ if (ret < 0)
+ die("avahi_entry_group_commit failed");
}
-static void client_callback(AvahiClient *c,
- AvahiClientState state,
- AVAHI_GCC_UNUSED void * userdata) {
- switch (state) {
- case AVAHI_CLIENT_S_REGISTERING:
- if (group)
- avahi_entry_group_reset(group);
- break;
-
- case AVAHI_CLIENT_S_RUNNING:
- register_service(c);
- break;
-
- case AVAHI_CLIENT_FAILURE:
- case AVAHI_CLIENT_S_COLLISION:
- die("avahi client failure");
-
- case AVAHI_CLIENT_CONNECTING:
- break;
- }
+static void client_callback(AvahiClient *c, AvahiClientState state,
+ AVAHI_GCC_UNUSED void *userdata) {
+ switch (state) {
+ case AVAHI_CLIENT_S_REGISTERING:
+ if (group)
+ avahi_entry_group_reset(group);
+ break;
+
+ case AVAHI_CLIENT_S_RUNNING:
+ register_service(c);
+ break;
+
+ case AVAHI_CLIENT_FAILURE:
+ case AVAHI_CLIENT_S_COLLISION:
+ die("avahi client failure");
+
+ case AVAHI_CLIENT_CONNECTING:
+ break;
+ }
}
static int avahi_register(char *srvname, int srvport) {
- debug(1, "avahi: avahi_register.");
- name = strdup(srvname);
- port = srvport;
-
- int err;
- if (!(tpoll = avahi_threaded_poll_new())) {
- warn("couldn't create avahi threaded tpoll!");
- return -1;
- }
- if (!(client = avahi_client_new(avahi_threaded_poll_get(tpoll),
- 0,
- client_callback,
- NULL,
- &err))) {
- warn("couldn't create avahi client: %s!", avahi_strerror(err));
- return -1;
- }
-
- if (avahi_threaded_poll_start(tpoll) < 0) {
- warn("couldn't start avahi tpoll thread");
- return -1;
- }
-
- return 0;
+ debug(1, "avahi: avahi_register.");
+ name = strdup(srvname);
+ port = srvport;
+
+ int err;
+ if (!(tpoll = avahi_threaded_poll_new())) {
+ warn("couldn't create avahi threaded tpoll!");
+ return -1;
+ }
+ if (!(client =
+ avahi_client_new(avahi_threaded_poll_get(tpoll), 0, client_callback, NULL, &err))) {
+ warn("couldn't create avahi client: %s!", avahi_strerror(err));
+ return -1;
+ }
+
+ if (avahi_threaded_poll_start(tpoll) < 0) {
+ warn("couldn't start avahi tpoll thread");
+ return -1;
+ }
+
+ return 0;
}
static void avahi_unregister(void) {
- debug(1, "avahi: avahi_unregister.");
- if (tpoll)
- avahi_threaded_poll_stop(tpoll);
- tpoll = NULL;
-
- if (name)
- free(name);
- name = NULL;
+ debug(1, "avahi: avahi_unregister.");
+ if (tpoll)
+ avahi_threaded_poll_stop(tpoll);
+ tpoll = NULL;
+
+ if (name)
+ free(name);
+ name = NULL;
}
-mdns_backend mdns_avahi =
-{
- .name = "avahi",
- .mdns_register = avahi_register,
- .mdns_unregister = avahi_unregister
-};
-
+mdns_backend mdns_avahi = {
+ .name = "avahi", .mdns_register = avahi_register, .mdns_unregister = avahi_unregister};
diff --git a/mdns_dns_sd.c b/mdns_dns_sd.c
index 4f586b9..922c2fb 100644
--- a/mdns_dns_sd.c
+++ b/mdns_dns_sd.c
@@ -32,79 +32,62 @@
static DNSServiceRef service;
static int mdns_dns_sd_register(char *apname, int port) {
- const char *recordwithoutmetadata[] = { MDNS_RECORD_WITHOUT_METADATA, NULL };
+ const char *recordwithoutmetadata[] = {MDNS_RECORD_WITHOUT_METADATA, NULL};
#ifdef CONFIG_METADATA
- const char *recordwithmetadata[] = { MDNS_RECORD_WITH_METADATA, NULL };
-#endif
- char **record;
+ const char *recordwithmetadata[] = {MDNS_RECORD_WITH_METADATA, NULL};
+#endif
+ char **record;
#ifdef CONFIG_METADATA
- if (config.meta_dir)
- record = recordwithmetadata;
- else
+ if (config.meta_dir)
+ record = recordwithmetadata;
+ else
#endif
- record = recordwithoutmetadata;
-
- uint16_t length = 0;
- const char **field;
+ record = recordwithoutmetadata;
+
+ uint16_t length = 0;
+ const char **field;
- // Concatenate string contained i record into buf.
+ // Concatenate string contained i record into buf.
- for (field = record; *field; field ++)
- {
- length += strlen(*field) + 1; // One byte for length each time
- }
+ for (field = record; *field; field++) {
+ length += strlen(*field) + 1; // One byte for length each time
+ }
- char *buf = malloc(length * sizeof(char));
- if (buf == NULL)
- {
- warn("dns_sd: buffer record allocation failed");
- return -1;
- }
+ char *buf = malloc(length * sizeof(char));
+ if (buf == NULL) {
+ warn("dns_sd: buffer record allocation failed");
+ return -1;
+ }
- char *p = buf;
+ char *p = buf;
- for (field = record; *field; field ++)
- {
- char * newp = stpcpy(p + 1, *field);
- *p = newp - p - 1;
- p = newp;
- }
+ for (field = record; *field; field++) {
+ char *newp = stpcpy(p + 1, *field);
+ *p = newp - p - 1;
+ p = newp;
+ }
- DNSServiceErrorType error;
- error = DNSServiceRegister(&service,
- 0,
- kDNSServiceInterfaceIndexAny,
- apname,
- "_raop._tcp",
- "",
- NULL,
- htons((uint16_t)port),
- length,
- buf,
- NULL,
- NULL);
+ DNSServiceErrorType error;
+ error = DNSServiceRegister(&service, 0, kDNSServiceInterfaceIndexAny, apname, "_raop._tcp", "",
+ NULL, htons((uint16_t)port), length, buf, NULL, NULL);
- free(buf);
+ free(buf);
- if (error == kDNSServiceErr_NoError)
- return 0;
- else
- {
- warn("dns-sd: DNSServiceRegister error %d", error);
- return -1;
- }
+ if (error == kDNSServiceErr_NoError)
+ return 0;
+ else {
+ warn("dns-sd: DNSServiceRegister error %d", error);
+ return -1;
+ }
}
static void mdns_dns_sd_unregister(void) {
- if (service)
- {
- DNSServiceRefDeallocate(service);
- service = NULL;
- }
+ if (service) {
+ DNSServiceRefDeallocate(service);
+ service = NULL;
+ }
}
-mdns_backend mdns_dns_sd = {
- .name = "dns-sd",
- .mdns_register = mdns_dns_sd_register,
- .mdns_unregister = mdns_dns_sd_unregister
-};
+mdns_backend mdns_dns_sd = {.name = "dns-sd",
+ .mdns_register = mdns_dns_sd_register,
+ .mdns_unregister = mdns_dns_sd_unregister};
diff --git a/mdns_external.c b/mdns_external.c
index 48284e5..f9ba20c 100644
--- a/mdns_external.c
+++ b/mdns_external.c
@@ -41,145 +41,128 @@ int mdns_pid = 0;
* Check errno for error details.
*/
static int fork_execvp(const char *file, char *const argv[]) {
- int execpipe[2];
- int pid = 0;
- if (pipe(execpipe) < 0) {
- return -1;
- }
-
- if (fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC) < 0) {
- close(execpipe[0]);
- close(execpipe[1]);
- return -1;
- }
-
- pid = fork();
- if (pid < 0) {
- close(execpipe[0]);
- close(execpipe[1]);
- return -1;
- }
- else if(pid == 0) { // Child
- close(execpipe[0]); // Close the read end
- execvp(file, argv);
+ int execpipe[2];
+ int pid = 0;
+ if (pipe(execpipe) < 0) {
+ return -1;
+ }
- // If we reach this point then execve has failed.
- // Write erno's value into the pipe and exit.
- int ignore = write(execpipe[1], &errno, sizeof(errno));
+ if (fcntl(execpipe[1], F_SETFD, fcntl(execpipe[1], F_GETFD) | FD_CLOEXEC) < 0) {
+ close(execpipe[0]);
+ close(execpipe[1]);
+ return -1;
+ }
- _exit(-1);
- return 0; // Just to make the compiler happy.
- }
- else { // Parent
- close(execpipe[1]); // Close the write end
-
- int childErrno;
- // Block until child closes the pipe or sends errno.
- if(read(execpipe[0], &childErrno, sizeof(childErrno)) == sizeof(childErrno)) { // We received errno
- errno = childErrno;
- return -1;
- }
- else { // Child closed the pipe. execvp was successful.
- return pid;
- }
+ pid = fork();
+ if (pid < 0) {
+ close(execpipe[0]);
+ close(execpipe[1]);
+ return -1;
+ } else if (pid == 0) { // Child
+ close(execpipe[0]); // Close the read end
+ execvp(file, argv);
+
+ // If we reach this point then execve has failed.
+ // Write erno's value into the pipe and exit.
+ int ignore = write(execpipe[1], &errno, sizeof(errno));
+
+ _exit(-1);
+ return 0; // Just to make the compiler happy.
+ } else { // Parent
+ close(execpipe[1]); // Close the write end
+
+ int childErrno;
+ // Block until child closes the pipe or sends errno.
+ if (read(execpipe[0], &childErrno, sizeof(childErrno)) ==
+ sizeof(childErrno)) { // We received errno
+ errno = childErrno;
+ return -1;
+ } else { // Child closed the pipe. execvp was successful.
+ return pid;
}
+ }
}
static int mdns_external_avahi_register(char *apname, int port) {
- char mdns_port[6];
- sprintf(mdns_port, "%d", config.port);
+ char mdns_port[6];
+ sprintf(mdns_port, "%d", config.port);
- char *argvwithoutmetadata[] = {
- NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITHOUT_METADATA, NULL
- };
+ char *argvwithoutmetadata[] = {NULL, apname, "_raop._tcp", mdns_port,
+ MDNS_RECORD_WITHOUT_METADATA, NULL};
#ifdef CONFIG_METADATA
- char *argvwithmetadata[] = {
- NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITH_METADATA, NULL
- };
+ char *argvwithmetadata[] = {NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITH_METADATA,
+ NULL};
#endif
- char **argv;
+ char **argv;
#ifdef CONFIG_METADATA
- if (config.metadata_enabled)
- argv=argvwithmetadata;
- else
+ if (config.metadata_enabled)
+ argv = argvwithmetadata;
+ else
#endif
- argv=argvwithoutmetadata;
-
- argv[0] = "avahi-publish-service";
- int pid = fork_execvp(argv[0], argv);
- if (pid >= 0)
- {
- mdns_pid = pid;
- return 0;
- }
- else
- warn("Calling %s failed !", argv[0]);
-
- argv[0] = "mDNSPublish";
- pid = fork_execvp(argv[0], argv);
- if (pid >= 0)
- {
- mdns_pid = pid;
- return 0;
- }
- else
- warn("Calling %s failed !", argv[0]);
-
- // If we reach here, both execvp calls failed.
- return -1;
+ argv = argvwithoutmetadata;
+
+ argv[0] = "avahi-publish-service";
+ int pid = fork_execvp(argv[0], argv);
+ if (pid >= 0) {
+ mdns_pid = pid;
+ return 0;
+ } else
+ warn("Calling %s failed !", argv[0]);
+
+ argv[0] = "mDNSPublish";
+ pid = fork_execvp(argv[0], argv);
+ if (pid >= 0) {
+ mdns_pid = pid;
+ return 0;
+ } else
+ warn("Calling %s failed !", argv[0]);
+
+ // If we reach here, both execvp calls failed.
+ return -1;
}
static int mdns_external_dns_sd_register(char *apname, int port) {
- char mdns_port[6];
- sprintf(mdns_port, "%d", config.port);
+ char mdns_port[6];
+ sprintf(mdns_port, "%d", config.port);
- char *argvwithoutmetadata[] = {
- NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITHOUT_METADATA, NULL
- };
+ char *argvwithoutmetadata[] = {NULL, apname, "_raop._tcp", mdns_port,
+ MDNS_RECORD_WITHOUT_METADATA, NULL};
#ifdef CONFIG_METADATA
- char *argvwithmetadata[] = {
- NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITH_METADATA, NULL
- };
+ char *argvwithmetadata[] = {NULL, apname, "_raop._tcp", mdns_port, MDNS_RECORD_WITH_METADATA,
+ NULL};
#endif
- char **argv;
+ char **argv;
#ifdef CONFIG_METADATA
- if (config.metadata_enabled)
- argv=argvwithmetadata;
- else
+ if (config.metadata_enabled)
+ argv = argvwithmetadata;
+ else
#endif
- argv=argvwithoutmetadata;
+ argv = argvwithoutmetadata;
- int pid = fork_execvp(argv[0], argv);
- if (pid >= 0)
- {
- mdns_pid = pid;
- return 0;
- }
- else
- warn("Calling %s failed !", argv[0]);
+ int pid = fork_execvp(argv[0], argv);
+ if (pid >= 0) {
+ mdns_pid = pid;
+ return 0;
+ } else
+ warn("Calling %s failed !", argv[0]);
- return -1;
+ return -1;
}
static void kill_mdns_child(void) {
- if (mdns_pid)
- kill(mdns_pid, SIGTERM);
- mdns_pid = 0;
+ if (mdns_pid)
+ kill(mdns_pid, SIGTERM);
+ mdns_pid = 0;
}
-mdns_backend mdns_external_avahi = {
- .name = "external-avahi",
- .mdns_register = mdns_external_avahi_register,
- .mdns_unregister = kill_mdns_child
-};
-
-mdns_backend mdns_external_dns_sd = {
- .name = "external-dns-sd",
- .mdns_register = mdns_external_dns_sd_register,
- .mdns_unregister = kill_mdns_child
-};
+mdns_backend mdns_external_avahi = {.name = "external-avahi",
+ .mdns_register = mdns_external_avahi_register,
+ .mdns_unregister = kill_mdns_child};
+mdns_backend mdns_external_dns_sd = {.name = "external-dns-sd",
+ .mdns_register = mdns_external_dns_sd_register,
+ .mdns_unregister = kill_mdns_child};
diff --git a/mdns_tinysvcmdns.c b/mdns_tinysvcmdns.c
index 8b79d49..a3c475a 100644
--- a/mdns_tinysvcmdns.c
+++ b/mdns_tinysvcmdns.c
@@ -27,7 +27,7 @@
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <net/if.h>
+#include <net/if.h>
#include <ifaddrs.h>
#include <unistd.h>
#include <netinet/in.h>
@@ -39,128 +39,108 @@
static struct mdnsd *svr = NULL;
static int mdns_tinysvcmdns_register(char *apname, int port) {
- struct ifaddrs *ifalist;
- struct ifaddrs *ifa;
-
- svr = mdnsd_start();
- if (svr == NULL) {
- warn("tinysvcmdns: mdnsd_start() failed");
- return -1;
- }
-
- // Thanks to Paul Lietar for this
- // room for name + .local + NULL
- char hostname[100 + 6];
- gethostname(hostname, 99);
- // according to POSIX, this may be truncated without a final NULL !
- hostname[99] = 0;
-
- // will not work if the hostname doesn't end in .local
- char *hostend = hostname + strlen(hostname);
- if ((strlen(hostname) < strlen(".local")) || (strcmp(hostend - 6, ".local")!=0)) {
- strcat(hostname, ".local");
- }
-
- if (getifaddrs(&ifalist) < 0)
- {
- warn("tinysvcmdns: getifaddrs() failed");
- return -1;
+ struct ifaddrs *ifalist;
+ struct ifaddrs *ifa;
+
+ svr = mdnsd_start();
+ if (svr == NULL) {
+ warn("tinysvcmdns: mdnsd_start() failed");
+ return -1;
+ }
+
+ // Thanks to Paul Lietar for this
+ // room for name + .local + NULL
+ char hostname[100 + 6];
+ gethostname(hostname, 99);
+ // according to POSIX, this may be truncated without a final NULL !
+ hostname[99] = 0;
+
+ // will not work if the hostname doesn't end in .local
+ char *hostend = hostname + strlen(hostname);
+ if ((strlen(hostname) < strlen(".local")) || (strcmp(hostend - 6, ".local") != 0)) {
+ strcat(hostname, ".local");
+ }
+
+ if (getifaddrs(&ifalist) < 0) {
+ warn("tinysvcmdns: getifaddrs() failed");
+ return -1;
+ }
+
+ ifa = ifalist;
+
+ // Look for an ipv4/ipv6 non-loopback interface to use as the main one.
+ for (ifa = ifalist; ifa != NULL; ifa = ifa->ifa_next) {
+ if (!(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
+ uint32_t main_ip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
+
+ mdnsd_set_hostname(svr, hostname, main_ip); // TTL should be 120 seconds
+ break;
+ } else if (!(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr &&
+ ifa->ifa_addr->sa_family == AF_INET6) {
+ struct in6_addr *addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+
+ mdnsd_set_hostname_v6(svr, hostname, addr); // TTL should be 120 seconds
+ break;
}
-
- ifa = ifalist;
-
- // Look for an ipv4/ipv6 non-loopback interface to use as the main one.
- for (ifa = ifalist; ifa != NULL; ifa = ifa->ifa_next)
- {
- if (!(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr &&
- ifa->ifa_addr->sa_family == AF_INET)
- {
- uint32_t main_ip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
-
- mdnsd_set_hostname(svr, hostname, main_ip); // TTL should be 120 seconds
- break;
- }
- else if (!(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_addr &&
- ifa->ifa_addr->sa_family == AF_INET6)
- {
- struct in6_addr *addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
-
- mdnsd_set_hostname_v6(svr, hostname, addr); // TTL should be 120 seconds
- break;
- }
- }
-
- if (ifa == NULL)
- {
- warn("tinysvcmdns: no non-loopback ipv4 or ipv6 interface found");
- return -1;
+ }
+
+ if (ifa == NULL) {
+ warn("tinysvcmdns: no non-loopback ipv4 or ipv6 interface found");
+ return -1;
+ }
+
+ // Skip the first one, it was already added by set_hostname
+ for (ifa = ifa->ifa_next; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_flags & IFF_LOOPBACK) // Skip loop-back interfaces
+ continue;
+
+ switch (ifa->ifa_addr->sa_family) {
+ case AF_INET: { // ipv4
+ uint32_t ip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
+ struct rr_entry *a_e = rr_create_a(create_nlabel(hostname), ip); // TTL should be 120 seconds
+ mdnsd_add_rr(svr, a_e);
+ } break;
+ case AF_INET6: { // ipv6
+ struct in6_addr *addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+ struct rr_entry *aaaa_e =
+ rr_create_aaaa(create_nlabel(hostname), addr); // TTL should be 120 seconds
+ mdnsd_add_rr(svr, aaaa_e);
+ } break;
}
+ }
+ freeifaddrs(ifa);
- // Skip the first one, it was already added by set_hostname
- for (ifa = ifa->ifa_next; ifa != NULL; ifa = ifa->ifa_next)
- {
- if (ifa->ifa_flags & IFF_LOOPBACK) // Skip loop-back interfaces
- continue;
-
- switch (ifa->ifa_addr->sa_family)
- {
- case AF_INET: { // ipv4
- uint32_t ip = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
- struct rr_entry *a_e = rr_create_a(create_nlabel(hostname), ip); // TTL should be 120 seconds
- mdnsd_add_rr(svr, a_e);
- }
- break;
- case AF_INET6: { // ipv6
- struct in6_addr *addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
- struct rr_entry *aaaa_e = rr_create_aaaa(create_nlabel(hostname), addr); // TTL should be 120 seconds
- mdnsd_add_rr(svr, aaaa_e);
- }
- break;
- }
- }
-
- freeifaddrs(ifa);
+ char *txtwithoutmetadata[] = {MDNS_RECORD_WITHOUT_METADATA, NULL};
+#ifdef CONFIG_METADATA
+ char *txtwithmetadata[] = {MDNS_RECORD_WITH_METADATA, NULL};
+#endif
+ char **txt;
- char *txtwithoutmetadata[] = { MDNS_RECORD_WITHOUT_METADATA, NULL };
#ifdef CONFIG_METADATA
- char *txtwithmetadata[] = { MDNS_RECORD_WITH_METADATA, NULL };
-#endif
- char **txt;
-
-#ifdef CONFIG_METADATA
- if (config.meta_dir)
- txt = txtwithmetadata;
- else
+ if (config.meta_dir)
+ txt = txtwithmetadata;
+ else
#endif
- txt = txtwithoutmetadata;
+ txt = txtwithoutmetadata;
-
-
- struct mdns_service *svc = mdnsd_register_svc(svr,
- apname,
- "_raop._tcp.local",
- port,
- NULL,
- (const char **)txt); // TTL should be 75 minutes, i.e. 4500 seconds
+ struct mdns_service *svc =
+ mdnsd_register_svc(svr, apname, "_raop._tcp.local", port, NULL,
+ (const char **)txt); // TTL should be 75 minutes, i.e. 4500 seconds
- mdns_service_destroy(svc);
+ mdns_service_destroy(svc);
- return 0;
+ return 0;
}
static void mdns_tinysvcmdns_unregister(void) {
- if (svr)
- {
- mdnsd_stop(svr);
- svr = NULL;
- }
+ if (svr) {
+ mdnsd_stop(svr);
+ svr = NULL;
+ }
}
-mdns_backend mdns_tinysvcmdns = {
- .name = "tinysvcmdns",
- .mdns_register = mdns_tinysvcmdns_register,
- .mdns_unregister = mdns_tinysvcmdns_unregister
-};
-
+mdns_backend mdns_tinysvcmdns = {.name = "tinysvcmdns",
+ .mdns_register = mdns_tinysvcmdns_register,
+ .mdns_unregister = mdns_tinysvcmdns_unregister};
diff --git a/player.c b/player.c
index a7e01c9..6d28d78 100644
--- a/player.c
+++ b/player.c
@@ -72,18 +72,19 @@ static AES_KEY aes;
#endif
static int sampling_rate, frame_size;
-#define FRAME_BYTES(frame_size) (4*frame_size)
+#define FRAME_BYTES(frame_size) (4 * frame_size)
// maximal resampling shift - conservative
-#define OUTFRAME_BYTES(frame_size) (4*(frame_size+3))
+#define OUTFRAME_BYTES(frame_size) (4 * (frame_size + 3))
-#ifdef HAVE_LIBPOLARSSL
+#ifdef HAVE_LIBPOLARSSL
static aes_context dctx;
#endif
static pthread_t player_thread;
static int please_stop;
-static int connection_state_to_output; // if true, then play incoming stuff; if false drop everything
+static int
+ connection_state_to_output; // if true, then play incoming stuff; if false drop everything
static alac_file *decoder_info;
@@ -99,13 +100,13 @@ static pthread_mutex_t vol_mutex = PTHREAD_MUTEX_INITIALIZER;
// default buffer size
// needs to be a power of 2 because of the way BUFIDX(seqno) works
-#define BUFFER_FRAMES 512
-#define MAX_PACKET 2048
+#define BUFFER_FRAMES 512
+#define MAX_PACKET 2048
// DAC buffer occupancy stuff
#define DAC_BUFFER_QUEUE_MINIMUM_LENGTH 5000
-typedef struct audio_buffer_entry { // decoded audio packets
+typedef struct audio_buffer_entry { // decoded audio packets
int ready;
uint32_t timestamp;
seq_t sequence_number;
@@ -133,11 +134,11 @@ static int64_t first_packet_time_to_play; // nanoseconds
static audio_parameters audio_information;
// stats
-static uint64_t missing_packets,late_packets,too_late_packets,resend_requests;
+static uint64_t missing_packets, late_packets, too_late_packets, resend_requests;
static void ab_resync(void) {
int i;
- for (i=0; i<BUFFER_FRAMES; i++) {
+ for (i = 0; i < BUFFER_FRAMES; i++) {
audio_buffer[i].ready = 0;
audio_buffer[i].sequence_number = 0;
}
@@ -148,19 +149,19 @@ static void ab_resync(void) {
// the sequence number is a 16-bit unsigned number which wraps pretty often
// to work out if one seqno is 'after' another therefore depends whether wrap has occurred
-// this function works out the actual ordinate of the seqno, i.e. the distance up from
+// this function works out the actual ordinate of the seqno, i.e. the distance up from
// the zeroth element, at ab_read, taking due account of wrap.
static inline seq_t SUCCESSOR(seq_t x) {
uint32_t p = x & 0xffff;
- p+=1;
+ p += 1;
p = p & 0xffff;
return p;
}
static inline seq_t PREDECESSOR(seq_t x) {
- uint32_t p = (x & 0xffff)+0x10000;
- p-=1;
+ uint32_t p = (x & 0xffff) + 0x10000;
+ p -= 1;
p = p & 0xffff;
return p;
}
@@ -169,7 +170,7 @@ static inline seq_t PREDECESSOR(seq_t x) {
static inline int32_t ORDINATE(seq_t x) {
int32_t p = x & 0xffff;
int32_t q = ab_read & 0x0ffff;
- int32_t t = (p+0x10000-q) & 0xffff;
+ int32_t t = (p + 0x10000 - q) & 0xffff;
// we definitely will get a positive number in t at this point, but it might be a
// positive alias of a negative number, i.e. x might actually be "before" ab_read
// So, if the result is greater than 32767, we will assume its an
@@ -183,63 +184,60 @@ static inline int32_t ORDINATE(seq_t x) {
// wrapped number between two seq_t.
int32_t seq_diff(seq_t a, seq_t b) {
- int32_t diff = ORDINATE(b) - ORDINATE(a);
- return diff;
+ int32_t diff = ORDINATE(b) - ORDINATE(a);
+ return diff;
}
// the sequence numbers will wrap pretty often.
// this returns true if the second arg is after the first
static inline int seq_order(seq_t a, seq_t b) {
- int32_t d = ORDINATE(b) - ORDINATE(a);
- return d > 0;
+ int32_t d = ORDINATE(b) - ORDINATE(a);
+ return d > 0;
}
static inline seq_t seq_sum(seq_t a, seq_t b) {
uint32_t p = a & 0xffff;
uint32_t q = b & 0x0ffff;
- uint32_t r = (a+b) & 0xffff;
+ uint32_t r = (a + b) & 0xffff;
return r;
}
-
// now for 32-bit wrapping in timestamps
// this returns true if the second arg is strictly after the first
// on the assumption that the gap between them is never greater than (2^31)-1
// Represent a and b in 64 bits
static inline int seq32_order(uint32_t a, uint32_t b) {
- if (a==b)
- return 0;
- int64_t A = a & 0xffffffff;
- int64_t B = b & 0xffffffff;
- int64_t C = B-A;
- // if bit 31 is set, it means either b is before (i.e. less than) a or
- // b is (2^31)-1 ahead of a.
-
- // If we assume the gap between b and a should never reach 2 billion, then
- // bit 31 == 0 means b is strictly after a
+ if (a == b)
+ return 0;
+ int64_t A = a & 0xffffffff;
+ int64_t B = b & 0xffffffff;
+ int64_t C = B - A;
+ // if bit 31 is set, it means either b is before (i.e. less than) a or
+ // b is (2^31)-1 ahead of a.
+
+ // If we assume the gap between b and a should never reach 2 billion, then
+ // bit 31 == 0 means b is strictly after a
return (C & 0x80000000) == 0;
}
-
-
static void alac_decode(short *dest, uint8_t *buf, int len) {
unsigned char packet[MAX_PACKET];
unsigned char packetp[MAX_PACKET];
- assert(len<=MAX_PACKET);
+ assert(len <= MAX_PACKET);
unsigned char iv[16];
int aeslen = len & ~0xf;
memcpy(iv, aesiv, sizeof(iv));
-
+
#ifdef HAVE_LIBPOLARSSL
- aes_crypt_cbc(&dctx,AES_DECRYPT,aeslen,iv,buf, packet);
+ aes_crypt_cbc(&dctx, AES_DECRYPT, aeslen, iv, buf, packet);
#endif
#ifdef HAVE_LIBSSL
AES_cbc_encrypt(buf, packet, aeslen, &aes, iv, AES_DECRYPT);
#endif
- memcpy(packet+aeslen, buf+aeslen, len-aeslen);
+ memcpy(packet + aeslen, buf + aeslen, len - aeslen);
int outsize;
@@ -264,112 +262,115 @@ static int init_decoder(int32_t fmtp[12]) {
decoder_info = alac;
alac->setinfo_max_samples_per_frame = frame_size;
- alac->setinfo_7a = fmtp[2];
+ alac->setinfo_7a = fmtp[2];
alac->setinfo_sample_size = sample_size;
alac->setinfo_rice_historymult = fmtp[4];
alac->setinfo_rice_initialhistory = fmtp[5];
alac->setinfo_rice_kmodifier = fmtp[6];
- alac->setinfo_7f = fmtp[7];
- alac->setinfo_80 = fmtp[8];
- alac->setinfo_82 = fmtp[9];
- alac->setinfo_86 = fmtp[10];
+ alac->setinfo_7f = fmtp[7];
+ alac->setinfo_80 = fmtp[8];
+ alac->setinfo_82 = fmtp[9];
+ alac->setinfo_86 = fmtp[10];
alac->setinfo_8a_rate = fmtp[11];
alac_allocate_buffers(alac);
return 0;
}
-static void free_decoder(void) {
- alac_free(decoder_info);
-}
+static void free_decoder(void) { alac_free(decoder_info); }
static void init_buffer(void) {
int i;
- for (i=0; i<BUFFER_FRAMES; i++)
+ for (i = 0; i < BUFFER_FRAMES; i++)
audio_buffer[i].data = malloc(OUTFRAME_BYTES(frame_size));
ab_resync();
}
static void free_buffer(void) {
int i;
- for (i=0; i<BUFFER_FRAMES; i++)
+ for (i = 0; i < BUFFER_FRAMES; i++)
free(audio_buffer[i].data);
}
-void player_put_packet(seq_t seqno,uint32_t timestamp, uint8_t *data, int len) {
-
+void player_put_packet(seq_t seqno, uint32_t timestamp, uint8_t *data, int len) {
+
packet_count++;
-
+
pthread_mutex_lock(&ab_mutex);
- time_of_last_audio_packet = get_absolute_time_in_fp();
+ time_of_last_audio_packet = get_absolute_time_in_fp();
if (connection_state_to_output) { // if we are supposed to be processing these packets
-
- if ((flush_rtp_timestamp!=0) && ((timestamp==flush_rtp_timestamp) || seq32_order(timestamp,flush_rtp_timestamp))) {
- debug(2,"Dropping flushed packet in player_put_packet, seqno %u, timestamp %u, flushing to timestamp: %u.",seqno,timestamp,flush_rtp_timestamp);
- } else {
- if ((flush_rtp_timestamp!=0x0) && (!seq32_order(timestamp,flush_rtp_timestamp))) // if we have gone past the flush boundary time
- flush_rtp_timestamp=0x0;
-
- abuf_t *abuf = 0;
-
- if (!ab_synced) {
- debug(2, "syncing to seqno %u.", seqno);
- ab_write = seqno;
- ab_read = seqno;
- ab_synced = 1;
- }
- if (ab_write == seqno) { // expected packet
- abuf = audio_buffer + BUFIDX(seqno);
- ab_write = SUCCESSOR(seqno);
- } else if (seq_order(ab_write, seqno)) { // newer than expected
- //if (ORDINATE(seqno)>(BUFFER_FRAMES*7)/8)
- // debug(1,"An interval of %u frames has opened, with ab_read: %u, ab_write: %u and seqno: %u.",seq_diff(ab_read,seqno),ab_read,ab_write,seqno);
- int32_t gap = seq_diff(ab_write,PREDECESSOR(seqno))+1;
- if (gap<=0)
- debug(1,"Unexpected gap size: %d.",gap);
- int i;
- for (i=0;i<gap;i++) {
- abuf = audio_buffer + BUFIDX(seq_sum(ab_write,i));
- abuf->ready = 0; // to be sure, to be sure
- abuf->timestamp = 0;
- abuf->sequence_number = 0;
- }
- // debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write);
- abuf = audio_buffer + BUFIDX(seqno);
- rtp_request_resend(ab_write,gap);
- resend_requests++;
- ab_write = SUCCESSOR(seqno);
- } else if (seq_order(ab_read, seqno)) { // late but not yet played
- late_packets++;
- abuf = audio_buffer + BUFIDX(seqno);
- } else { // too late.
- too_late_packets++;
- /*
- if (!late_packet_message_sent) {
- debug(1, "too-late packet received: %u; ab_read: %u; ab_write: %u.", seqno, ab_read, ab_write);
- late_packet_message_sent=1;
- }
- */
- }
- // pthread_mutex_unlock(&ab_mutex);
-
- if (abuf) {
- alac_decode(abuf->data, data, len);
- abuf->ready = 1;
- abuf->timestamp = timestamp;
- abuf->sequence_number = seqno;
- }
-
- // pthread_mutex_lock(&ab_mutex);
-
+
+ if ((flush_rtp_timestamp != 0) &&
+ ((timestamp == flush_rtp_timestamp) || seq32_order(timestamp, flush_rtp_timestamp))) {
+ debug(2, "Dropping flushed packet in player_put_packet, seqno %u, timestamp %u, flushing to "
+ "timestamp: %u.",
+ seqno, timestamp, flush_rtp_timestamp);
+ } else {
+ if ((flush_rtp_timestamp != 0x0) &&
+ (!seq32_order(timestamp,
+ flush_rtp_timestamp))) // if we have gone past the flush boundary time
+ flush_rtp_timestamp = 0x0;
+
+ abuf_t *abuf = 0;
+
+ if (!ab_synced) {
+ debug(2, "syncing to seqno %u.", seqno);
+ ab_write = seqno;
+ ab_read = seqno;
+ ab_synced = 1;
+ }
+ if (ab_write == seqno) { // expected packet
+ abuf = audio_buffer + BUFIDX(seqno);
+ ab_write = SUCCESSOR(seqno);
+ } else if (seq_order(ab_write, seqno)) { // newer than expected
+ // if (ORDINATE(seqno)>(BUFFER_FRAMES*7)/8)
+ // debug(1,"An interval of %u frames has opened, with ab_read: %u, ab_write: %u and seqno:
+ // %u.",seq_diff(ab_read,seqno),ab_read,ab_write,seqno);
+ int32_t gap = seq_diff(ab_write, PREDECESSOR(seqno)) + 1;
+ if (gap <= 0)
+ debug(1, "Unexpected gap size: %d.", gap);
+ int i;
+ for (i = 0; i < gap; i++) {
+ abuf = audio_buffer + BUFIDX(seq_sum(ab_write, i));
+ abuf->ready = 0; // to be sure, to be sure
+ abuf->timestamp = 0;
+ abuf->sequence_number = 0;
+ }
+ // debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write);
+ abuf = audio_buffer + BUFIDX(seqno);
+ rtp_request_resend(ab_write, gap);
+ resend_requests++;
+ ab_write = SUCCESSOR(seqno);
+ } else if (seq_order(ab_read, seqno)) { // late but not yet played
+ late_packets++;
+ abuf = audio_buffer + BUFIDX(seqno);
+ } else { // too late.
+ too_late_packets++;
+ /*
+ if (!late_packet_message_sent) {
+ debug(1, "too-late packet received: %u; ab_read: %u; ab_write: %u.", seqno, ab_read,
+ ab_write);
+ late_packet_message_sent=1;
+ }
+ */
+ }
+ // pthread_mutex_unlock(&ab_mutex);
+
+ if (abuf) {
+ alac_decode(abuf->data, data, len);
+ abuf->ready = 1;
+ abuf->timestamp = timestamp;
+ abuf->sequence_number = seqno;
+ }
+
+ // pthread_mutex_lock(&ab_mutex);
}
int rc = pthread_cond_signal(&flowcontrol);
if (rc)
- debug(1,"Error signalling flowcontrol.");
- }
+ debug(1, "Error signalling flowcontrol.");
+ }
pthread_mutex_unlock(&ab_mutex);
}
-
static inline short lcg_rand(void) {
static unsigned long lcg_prev = 12345;
lcg_prev = lcg_prev * 69069 + 3;
@@ -387,7 +388,7 @@ static inline short dithered_vol(short sample) {
out += rand_a;
out -= rand_b;
}
- return out>>16;
+ return out >> 16;
}
// get the next frame, when available. return 0 if underrun/stream reset.
@@ -398,132 +399,161 @@ static abuf_t *buffer_get_frame(void) {
abuf_t *abuf = 0;
int i;
abuf_t *curframe;
-
+
pthread_mutex_lock(&ab_mutex);
int wait;
int32_t dac_delay = 0;
do {
// get the time
- local_time_now = get_absolute_time_in_fp();
-
- // if config.timeout (default 120) seconds have elapsed since the last audio packet was received, then we should stop.
- // config.timeout of zero means don't check..., but iTunes may be confused by a long gap followed by a resumption...
-
- if ((time_of_last_audio_packet!=0) && (shutdown_requested==0) && (config.dont_check_timeout==0)) {
- uint64_t ct = config.timeout; // go from int to 64-bit int
- if ((local_time_now>time_of_last_audio_packet) && (local_time_now-time_of_last_audio_packet>=ct<<32)) {
- debug(1,"As Yeats almost said, \"Too long a silence / can make a stone of the heart\"");
+ local_time_now = get_absolute_time_in_fp();
+
+ // if config.timeout (default 120) seconds have elapsed since the last audio packet was
+ // received, then we should stop.
+ // config.timeout of zero means don't check..., but iTunes may be confused by a long gap
+ // followed by a resumption...
+
+ if ((time_of_last_audio_packet != 0) && (shutdown_requested == 0) &&
+ (config.dont_check_timeout == 0)) {
+ uint64_t ct = config.timeout; // go from int to 64-bit int
+ if ((local_time_now > time_of_last_audio_packet) &&
+ (local_time_now - time_of_last_audio_packet >= ct << 32)) {
+ debug(1, "As Yeats almost said, \"Too long a silence / can make a stone of the heart\"");
rtsp_request_shutdown_stream();
- shutdown_requested=1;
+ shutdown_requested = 1;
}
}
int rco = get_requested_connection_state_to_output();
-
+
if (connection_state_to_output != rco) {
- connection_state_to_output=rco;
+ connection_state_to_output = rco;
// change happening
- if (connection_state_to_output==0) { //going off
- pthread_mutex_lock(&flush_mutex);
- flush_requested=1;
- pthread_mutex_unlock(&flush_mutex);
+ if (connection_state_to_output == 0) { // going off
+ pthread_mutex_lock(&flush_mutex);
+ flush_requested = 1;
+ pthread_mutex_unlock(&flush_mutex);
}
}
-
+
pthread_mutex_lock(&flush_mutex);
- if (flush_requested==1) {
+ if (flush_requested == 1) {
if (config.output->flush)
config.output->flush();
ab_resync();
first_packet_timestamp = 0;
first_packet_time_to_play = 0;
- flush_requested=0;
+ flush_requested = 0;
}
pthread_mutex_unlock(&flush_mutex);
uint32_t flush_limit = 0;
if (ab_synced) {
- do {
- curframe = audio_buffer + BUFIDX(ab_read);
- if (curframe->ready) {
-
- if (curframe->sequence_number!=ab_read) {
- // some kind of sync problem has occurred.
- if (BUFIDX(curframe->sequence_number)==BUFIDX(ab_read)) {
- // it looks like some kind of aliasing has happened
- if (seq_order(ab_read,curframe->sequence_number)) {
- ab_read=curframe->sequence_number;
- debug(1,"Aliasing of buffer index -- reset.");
- }
- } else {
- debug(1,"Inconsistent sequence numbers detected");
- }
- }
-
- if ((flush_rtp_timestamp!=0) && ((curframe->timestamp==flush_rtp_timestamp) || seq32_order(curframe->timestamp,flush_rtp_timestamp))) {
- debug(1,"Dropping flushed packet seqno %u, timestamp %u",curframe->sequence_number,curframe->timestamp);
- curframe->ready=0;
- flush_limit++;
- ab_read=SUCCESSOR(ab_read);
- }
- if ((flush_rtp_timestamp!=0) && (!seq32_order(curframe->timestamp,flush_rtp_timestamp))) // if we have gone past the flush boundary time
- flush_rtp_timestamp=0;
- }
- } while ((flush_rtp_timestamp!=0) && (flush_limit<=8820) && (curframe->ready==0));
-
- if (flush_limit==8820) {
- debug(1,"Flush hit the 8820 frame limit!");
- flush_limit=0;
- }
-
+ do {
+ curframe = audio_buffer + BUFIDX(ab_read);
+ if (curframe->ready) {
+
+ if (curframe->sequence_number != ab_read) {
+ // some kind of sync problem has occurred.
+ if (BUFIDX(curframe->sequence_number) == BUFIDX(ab_read)) {
+ // it looks like some kind of aliasing has happened
+ if (seq_order(ab_read, curframe->sequence_number)) {
+ ab_read = curframe->sequence_number;
+ debug(1, "Aliasing of buffer index -- reset.");
+ }
+ } else {
+ debug(1, "Inconsistent sequence numbers detected");
+ }
+ }
+
+ if ((flush_rtp_timestamp != 0) &&
+ ((curframe->timestamp == flush_rtp_timestamp) ||
+ seq32_order(curframe->timestamp, flush_rtp_timestamp))) {
+ debug(1, "Dropping flushed packet seqno %u, timestamp %u", curframe->sequence_number,
+ curframe->timestamp);
+ curframe->ready = 0;
+ flush_limit++;
+ ab_read = SUCCESSOR(ab_read);
+ }
+ if ((flush_rtp_timestamp != 0) &&
+ (!seq32_order(curframe->timestamp,
+ flush_rtp_timestamp))) // if we have gone past the flush boundary time
+ flush_rtp_timestamp = 0;
+ }
+ } while ((flush_rtp_timestamp != 0) && (flush_limit <= 8820) && (curframe->ready == 0));
+
+ if (flush_limit == 8820) {
+ debug(1, "Flush hit the 8820 frame limit!");
+ flush_limit = 0;
+ }
+
curframe = audio_buffer + BUFIDX(ab_read);
if (curframe->ready) {
- if (ab_buffering) { // if we are getting packets but not yet forwarding them to the player
- if (first_packet_timestamp==0) { // if this is the very first packet
- // debug(1,"First frame seen, time %u, with %d frames...",curframe->timestamp,seq_diff(ab_read, ab_write));
- uint32_t reference_timestamp;
+ if (ab_buffering) { // if we are getting packets but not yet forwarding them to the player
+ if (first_packet_timestamp == 0) { // if this is the very first packet
+ // debug(1,"First frame seen, time %u, with %d
+ // frames...",curframe->timestamp,seq_diff(ab_read, ab_write));
+ uint32_t reference_timestamp;
uint64_t reference_timestamp_time;
- get_reference_timestamp_stuff(&reference_timestamp,&reference_timestamp_time);
+ get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time);
if (reference_timestamp) { // if we have a reference time
// debug(1,"First frame seen with timestamp...");
- first_packet_timestamp=curframe->timestamp; // we will keep buffering until we are supposed to start playing this
-
- // Here, calculate when we should start playing. We need to know when to allow the packets to be sent to the player.
- // We will send packets of silence from now until that time and then we will send the first packet,
+ first_packet_timestamp = curframe->timestamp; // we will keep buffering until we are
+ // supposed to start playing this
+
+ // Here, calculate when we should start playing. We need to know when to allow the
+ // packets to be sent to the player.
+ // We will send packets of silence from now until that time and then we will send the
+ // first packet,
// which will be followed by the subsequent packets.
-
+
// we will get a fix every second or so, which will be stored as a pair consisting of
- // the time when the packet with a particular timestamp should be played, neglecting latencies, etc.
-
- // It probably won't be the timestamp of our first packet, however, so we might have to do some calculations.
-
- // To calculate when the first packet will be played, we figure out the exact time the packet should
- // be played according to its timestamp and the reference time. We then need to add the desired latency, typically 88200 frames.
-
- // Then we need to offset this by the backend latency offset. For example, if we knew that the audio back end has a latency of 100 ms, we would
- // ask for the first packet to be emitted 100 ms earlier than it should, i.e. -4410 frames, so that when it got through the audio back end,
- // if would be in sync. To do this, we would give it a latency offset of -100 ms, i.e. -4410 frames.
-
- int64_t delta = ((int64_t)first_packet_timestamp-(int64_t)reference_timestamp);
-
- first_packet_time_to_play = reference_timestamp_time+((delta+(int64_t)config.latency+(int64_t)config.audio_backend_latency_offset)<<32)/44100;
-
- if (local_time_now>=first_packet_time_to_play) {
- debug(1,"First packet is late! It should have played before now. Flushing 0.1 seconds");
- player_flush(first_packet_timestamp+4410);
+ // the time when the packet with a particular timestamp should be played, neglecting
+ // latencies, etc.
+
+ // It probably won't be the timestamp of our first packet, however, so we might have
+ // to do some calculations.
+
+ // To calculate when the first packet will be played, we figure out the exact time the
+ // packet should
+ // be played according to its timestamp and the reference time. We then need to add
+ // the desired latency, typically 88200 frames.
+
+ // Then we need to offset this by the backend latency offset. For example, if we knew
+ // that the audio back end has a latency of 100 ms, we would
+ // ask for the first packet to be emitted 100 ms earlier than it should, i.e. -4410
+ // frames, so that when it got through the audio back end,
+ // if would be in sync. To do this, we would give it a latency offset of -100 ms, i.e.
+ // -4410 frames.
+
+ int64_t delta = ((int64_t)first_packet_timestamp - (int64_t)reference_timestamp);
+
+ first_packet_time_to_play =
+ reference_timestamp_time +
+ ((delta + (int64_t)config.latency + (int64_t)config.audio_backend_latency_offset)
+ << 32) /
+ 44100;
+
+ if (local_time_now >= first_packet_time_to_play) {
+ debug(
+ 1,
+ "First packet is late! It should have played before now. Flushing 0.1 seconds");
+ player_flush(first_packet_timestamp + 4410);
}
}
- }
+ }
- if (first_packet_time_to_play!=0) {
+ if (first_packet_time_to_play != 0) {
uint32_t filler_size = frame_size;
uint32_t max_dac_delay = 4410;
filler_size = 4410; // 0.1 second -- the maximum we'll add to the DAC
- if (local_time_now>=first_packet_time_to_play) {
+ if (local_time_now >= first_packet_time_to_play) {
// we've gone past the time...
- // debug(1,"Run past the exact start time by %llu frames, with time now of %llx, fpttp of %llx and dac_delay of %d and %d packets; flush.",(((tn-first_packet_time_to_play)*44100)>>32)+dac_delay,tn,first_packet_time_to_play,dac_delay,seq_diff(ab_read, ab_write));
-
+ // debug(1,"Run past the exact start time by %llu frames, with time now of %llx, fpttp
+ // of %llx and dac_delay of %d and %d packets;
+ // flush.",(((tn-first_packet_time_to_play)*44100)>>32)+dac_delay,tn,first_packet_time_to_play,dac_delay,seq_diff(ab_read,
+ // ab_write));
+
if (config.output->flush)
config.output->flush();
ab_resync();
@@ -532,40 +562,49 @@ static abuf_t *buffer_get_frame(void) {
} else {
if (config.output->delay) {
dac_delay = config.output->delay();
- if (dac_delay==-1) {
- debug(1,"Error getting dac_delay in buffer_get_frame.");
- dac_delay=0;
+ if (dac_delay == -1) {
+ debug(1, "Error getting dac_delay in buffer_get_frame.");
+ dac_delay = 0;
}
} else
- dac_delay=0;
- uint64_t gross_frame_gap = ((first_packet_time_to_play-local_time_now)*44100)>>32;
- int64_t exact_frame_gap = gross_frame_gap-dac_delay;
- if (exact_frame_gap<=0) {
+ dac_delay = 0;
+ uint64_t gross_frame_gap =
+ ((first_packet_time_to_play - local_time_now) * 44100) >> 32;
+ int64_t exact_frame_gap = gross_frame_gap - dac_delay;
+ if (exact_frame_gap <= 0) {
// we've gone past the time...
- // debug(1,"Run a bit past the exact start time by %lld frames, with time now of %llx, fpttp of %llx and dac_delay of %d and %d packets; flush.",-exact_frame_gap,tn,first_packet_time_to_play,dac_delay,seq_diff(ab_read, ab_write));
+ // debug(1,"Run a bit past the exact start time by %lld frames, with time now of
+ // %llx, fpttp of %llx and dac_delay of %d and %d packets;
+ // flush.",-exact_frame_gap,tn,first_packet_time_to_play,dac_delay,seq_diff(ab_read,
+ // ab_write));
if (config.output->flush)
config.output->flush();
ab_resync();
first_packet_timestamp = 0;
first_packet_time_to_play = 0;
} else {
- uint32_t fs=filler_size;
- if (fs>(max_dac_delay-dac_delay))
- fs=max_dac_delay-dac_delay;
- if ((exact_frame_gap<=fs) || (exact_frame_gap<=frame_size*2)) {
- fs=exact_frame_gap;
- // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d, with %d packets, ab_read is %04x, ab_write is %04x.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read, ab_write),ab_read,ab_write);
+ uint32_t fs = filler_size;
+ if (fs > (max_dac_delay - dac_delay))
+ fs = max_dac_delay - dac_delay;
+ if ((exact_frame_gap <= fs) || (exact_frame_gap <= frame_size * 2)) {
+ fs = exact_frame_gap;
+ // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d,
+ // with %d packets, ab_read is %04x, ab_write is
+ // %04x.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read,
+ // ab_write),ab_read,ab_write);
ab_buffering = 0;
}
signed short *silence;
silence = malloc(FRAME_BYTES(fs));
memset(silence, 0, FRAME_BYTES(fs));
- // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d, with %d packets.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read, ab_write));
+ // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d,
+ // with %d packets.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read, ab_write));
config.output->play(silence, fs);
free(silence);
#ifdef CONFIG_METADATA
- if (ab_buffering==0) {
- send_ssnc_metadata('prsm',NULL,0,0); // "resume", but don't wait if the queue is locked
+ if (ab_buffering == 0) {
+ send_ssnc_metadata('prsm', NULL, 0,
+ 0); // "resume", but don't wait if the queue is locked
}
#endif
}
@@ -574,74 +613,79 @@ static abuf_t *buffer_get_frame(void) {
}
}
}
-
+
// Here, we work out whether to release a packet or wait
// We release a buffer when the time is right.
-
- // To work out when the time is right, we need to take account of (1) the actual time the packet should be released,
- // (2) the latency requested, (3) the audio backend latency offset and (4) the desired length of the audio backend's buffer
-
+
+ // To work out when the time is right, we need to take account of (1) the actual time the packet
+ // should be released,
+ // (2) the latency requested, (3) the audio backend latency offset and (4) the desired length of
+ // the audio backend's buffer
+
// The time is right if the current time is later or the same as
// The packet time + (latency + latency offset - backend_buffer_length).
// Note: the last three items are expressed in frames and must be converted to time.
- int do_wait = 1;
- if ((ab_synced) && (curframe) && (curframe->ready) && (curframe->timestamp)) {
- uint32_t reference_timestamp;
- uint64_t reference_timestamp_time;
- get_reference_timestamp_stuff(&reference_timestamp,&reference_timestamp_time);
- if (reference_timestamp) { // if we have a reference time
- uint32_t packet_timestamp=curframe->timestamp;
- int64_t delta = ((int64_t)packet_timestamp-(int64_t)reference_timestamp);
- int64_t offset = (int64_t)config.latency+config.audio_backend_latency_offset-(int64_t)config.audio_backend_buffer_desired_length;
- int64_t net_offset = delta+offset;
- int64_t time_to_play = reference_timestamp_time;
- int64_t net_offset_fp_sec;
- if (net_offset>=0) {
- net_offset_fp_sec = (net_offset<<32)/44100;
- time_to_play+=net_offset_fp_sec; // using the latency requested...
- // debug(2,"Net Offset: %lld, adjusted: %lld.",net_offset,net_offset_fp_sec);
- } else {
- net_offset_fp_sec = ((-net_offset)<<32)/44100;
- time_to_play-=net_offset_fp_sec;
- // debug(2,"Net Offset: %lld, adjusted: -%lld.",net_offset,net_offset_fp_sec);
- }
-
- if (local_time_now>=time_to_play) {
- do_wait = 0;
- }
- }
- }
- wait = (ab_buffering || (do_wait!=0) || (!ab_synced)) && (!please_stop);
+ int do_wait = 1;
+ if ((ab_synced) && (curframe) && (curframe->ready) && (curframe->timestamp)) {
+ uint32_t reference_timestamp;
+ uint64_t reference_timestamp_time;
+ get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time);
+ if (reference_timestamp) { // if we have a reference time
+ uint32_t packet_timestamp = curframe->timestamp;
+ int64_t delta = ((int64_t)packet_timestamp - (int64_t)reference_timestamp);
+ int64_t offset = (int64_t)config.latency + config.audio_backend_latency_offset -
+ (int64_t)config.audio_backend_buffer_desired_length;
+ int64_t net_offset = delta + offset;
+ int64_t time_to_play = reference_timestamp_time;
+ int64_t net_offset_fp_sec;
+ if (net_offset >= 0) {
+ net_offset_fp_sec = (net_offset << 32) / 44100;
+ time_to_play += net_offset_fp_sec; // using the latency requested...
+ // debug(2,"Net Offset: %lld, adjusted: %lld.",net_offset,net_offset_fp_sec);
+ } else {
+ net_offset_fp_sec = ((-net_offset) << 32) / 44100;
+ time_to_play -= net_offset_fp_sec;
+ // debug(2,"Net Offset: %lld, adjusted: -%lld.",net_offset,net_offset_fp_sec);
+ }
+
+ if (local_time_now >= time_to_play) {
+ do_wait = 0;
+ }
+ }
+ }
+ wait = (ab_buffering || (do_wait != 0) || (!ab_synced)) && (!please_stop);
if (wait) {
- uint64_t time_to_wait_for_wakeup_fp = ((uint64_t)1<<32)/44100; // this is time period of one frame
- time_to_wait_for_wakeup_fp *= 4*352; // four full 352-frame packets
- time_to_wait_for_wakeup_fp /= 3; //four thirds of a packet time
-
+ uint64_t time_to_wait_for_wakeup_fp =
+ ((uint64_t)1 << 32) / 44100; // this is time period of one frame
+ time_to_wait_for_wakeup_fp *= 4 * 352; // four full 352-frame packets
+ time_to_wait_for_wakeup_fp /= 3; // four thirds of a packet time
+
#ifdef COMPILE_FOR_LINUX
- uint64_t time_of_wakeup_fp = local_time_now+time_to_wait_for_wakeup_fp;
- uint64_t sec = time_of_wakeup_fp>>32;
- uint64_t nsec = ((time_of_wakeup_fp&0xffffffff)*1000000000)>>32;
-
+ uint64_t time_of_wakeup_fp = local_time_now + time_to_wait_for_wakeup_fp;
+ uint64_t sec = time_of_wakeup_fp >> 32;
+ uint64_t nsec = ((time_of_wakeup_fp & 0xffffffff) * 1000000000) >> 32;
+
struct timespec time_of_wakeup;
time_of_wakeup.tv_sec = sec;
time_of_wakeup.tv_nsec = nsec;
-
- pthread_cond_timedwait(&flowcontrol,&ab_mutex,&time_of_wakeup);
- // int rc = pthread_cond_timedwait(&flowcontrol,&ab_mutex,&time_of_wakeup);
- // if (rc!=0)
- // debug(1,"pthread_cond_timedwait returned error code %d.",rc);
+
+ pthread_cond_timedwait(&flowcontrol, &ab_mutex, &time_of_wakeup);
+// int rc = pthread_cond_timedwait(&flowcontrol,&ab_mutex,&time_of_wakeup);
+// if (rc!=0)
+// debug(1,"pthread_cond_timedwait returned error code %d.",rc);
#endif
#ifdef COMPILE_FOR_OSX
- uint64_t sec = time_to_wait_for_wakeup_fp>>32;;
- uint64_t nsec = ((time_to_wait_for_wakeup_fp&0xffffffff)*1000000000)>>32;
+ uint64_t sec = time_to_wait_for_wakeup_fp >> 32;
+ ;
+ uint64_t nsec = ((time_to_wait_for_wakeup_fp & 0xffffffff) * 1000000000) >> 32;
struct timespec time_to_wait;
time_to_wait.tv_sec = sec;
time_to_wait.tv_nsec = nsec;
- pthread_cond_timedwait_relative_np(&flowcontrol,&ab_mutex,&time_to_wait);
+ pthread_cond_timedwait_relative_np(&flowcontrol, &ab_mutex, &time_to_wait);
#endif
- }
+ }
} while (wait);
if (please_stop) {
@@ -653,11 +697,10 @@ static abuf_t *buffer_get_frame(void) {
// check if t+8, t+16, t+32, t+64, t+128, ... (buffer_start_fill / 2)
// packets have arrived... last-chance resend
-
-
+
if (!ab_buffering) {
- for (i = 8; i < (seq_diff(ab_read,ab_write) / 2); i = (i * 2)) {
- seq_t next = seq_sum(ab_read,i);
+ for (i = 8; i < (seq_diff(ab_read, ab_write) / 2); i = (i * 2)) {
+ seq_t next = seq_sum(ab_read, i);
abuf = audio_buffer + BUFIDX(next);
if (!abuf->ready) {
rtp_request_resend(next, 1);
@@ -666,15 +709,15 @@ static abuf_t *buffer_get_frame(void) {
}
}
}
-
+
if (!curframe->ready) {
// debug(1, " %d. Supplying a silent frame.", read);
missing_packets++;
memset(curframe->data, 0, FRAME_BYTES(frame_size));
- curframe->timestamp=0;
+ curframe->timestamp = 0;
}
curframe->ready = 0;
- ab_read=SUCCESSOR(ab_read);
+ ab_read = SUCCESSOR(ab_read);
pthread_mutex_unlock(&ab_mutex);
return curframe;
}
@@ -682,65 +725,66 @@ static abuf_t *buffer_get_frame(void) {
static inline short shortmean(short a, short b) {
long al = (long)a;
long bl = (long)b;
- long longmean = (al+bl)/2;
+ long longmean = (al + bl) / 2;
short r = (short)longmean;
- if (r!=longmean)
- debug(1,"Error calculating average of two shorts");
+ if (r != longmean)
+ debug(1, "Error calculating average of two shorts");
return r;
}
// stuff: 1 means add 1; 0 means do nothing; -1 means remove 1
static int stuff_buffer_basic(short *inptr, short *outptr, int stuff) {
- if ((stuff>1) || (stuff<-1)) {
- debug(1,"Stuff argument to stuff_buffer must be from -1 to +1.");
- return frame_size;
+ if ((stuff > 1) || (stuff < -1)) {
+ debug(1, "Stuff argument to stuff_buffer must be from -1 to +1.");
+ return frame_size;
+ }
+ int i;
+ int stuffsamp = frame_size;
+ if (stuff)
+ // stuffsamp = rand() % (frame_size - 1);
+ stuffsamp =
+ (rand() % (frame_size - 2)) + 1; // ensure there's always a sample before and after the item
+
+ pthread_mutex_lock(&vol_mutex);
+ for (i = 0; i < stuffsamp; i++) { // the whole frame, if no stuffing
+ *outptr++ = dithered_vol(*inptr++);
+ *outptr++ = dithered_vol(*inptr++);
+ };
+ if (stuff) {
+ if (stuff == 1) {
+ debug(3, "+++++++++");
+ // interpolate one sample
+ //*outptr++ = dithered_vol(((long)inptr[-2] + (long)inptr[0]) >> 1);
+ //*outptr++ = dithered_vol(((long)inptr[-1] + (long)inptr[1]) >> 1);
+ *outptr++ = dithered_vol(shortmean(inptr[-2], inptr[0]));
+ *outptr++ = dithered_vol(shortmean(inptr[-1], inptr[1]));
+ } else if (stuff == -1) {
+ debug(3, "---------");
+ inptr++;
+ inptr++;
}
- int i;
- int stuffsamp = frame_size;
- if (stuff)
-// stuffsamp = rand() % (frame_size - 1);
- stuffsamp = (rand() % (frame_size-2))+1; // ensure there's always a sample before and after the item
-
- pthread_mutex_lock(&vol_mutex);
- for (i=0; i<stuffsamp; i++) { // the whole frame, if no stuffing
- *outptr++ = dithered_vol(*inptr++);
- *outptr++ = dithered_vol(*inptr++);
- };
- if (stuff) {
- if (stuff==1) {
- debug(3, "+++++++++");
- // interpolate one sample
- //*outptr++ = dithered_vol(((long)inptr[-2] + (long)inptr[0]) >> 1);
- //*outptr++ = dithered_vol(((long)inptr[-1] + (long)inptr[1]) >> 1);
- *outptr++ = dithered_vol(shortmean(inptr[-2],inptr[0]));
- *outptr++ = dithered_vol(shortmean(inptr[-1],inptr[1]));
- } else if (stuff==-1) {
- debug(3, "---------");
- inptr++;
- inptr++;
- }
- for (i=stuffsamp; i<frame_size + stuff; i++) {
- *outptr++ = dithered_vol(*inptr++);
- *outptr++ = dithered_vol(*inptr++);
- }
+ for (i = stuffsamp; i < frame_size + stuff; i++) {
+ *outptr++ = dithered_vol(*inptr++);
+ *outptr++ = dithered_vol(*inptr++);
}
- pthread_mutex_unlock(&vol_mutex);
+ }
+ pthread_mutex_unlock(&vol_mutex);
- return frame_size + stuff;
+ return frame_size + stuff;
}
#ifdef HAVE_LIBSOXR
// stuff: 1 means add 1; 0 means do nothing; -1 means remove 1
static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) {
- if ((stuff>1) || (stuff<-1)) {
- debug(1,"Stuff argument to sox_stuff_buffer must be from -1 to +1.");
+ if ((stuff > 1) || (stuff < -1)) {
+ debug(1, "Stuff argument to sox_stuff_buffer must be from -1 to +1.");
return frame_size;
}
int i;
- short *ip,*op;
- ip=inptr;
- op=outptr;
-
+ short *ip, *op;
+ ip = inptr;
+ op = outptr;
+
if (stuff) {
// debug(1,"Stuff %d.",stuff);
soxr_io_spec_t io_spec;
@@ -753,10 +797,10 @@ static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) {
size_t odone;
soxr_error_t error = soxr_oneshot(frame_size, frame_size + stuff, 2, /* Rates and # of chans. */
- inptr, frame_size, NULL, /* Input. */
- outptr, frame_size + stuff, &odone, /* Output. */
- &io_spec, /* Input, output and transfer spec. */
- NULL, NULL); /* Default configuration.*/
+ inptr, frame_size, NULL, /* Input. */
+ outptr, frame_size + stuff, &odone, /* Output. */
+ &io_spec, /* Input, output and transfer spec. */
+ NULL, NULL); /* Default configuration.*/
if (error)
die("soxr error: %s\n", "error: %s\n", soxr_strerror(error));
@@ -765,26 +809,26 @@ static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) {
die("odone = %d!\n", odone);
const int gpm = 5;
-
+
// keep the first (dpm) samples, to mitigate the Gibbs phenomenon
- for (i=0;i<gpm;i++) {
+ for (i = 0; i < gpm; i++) {
*op++ = *ip++;
*op++ = *ip++;
}
// keep the last (dpm) samples, to mitigate the Gibbs phenomenon
- op=outptr+(frame_size+stuff-gpm)*sizeof(short);
- ip=inptr+(frame_size-gpm)*sizeof(short);
- for (i=0;i<gpm;i++) {
+ op = outptr + (frame_size + stuff - gpm) * sizeof(short);
+ ip = inptr + (frame_size - gpm) * sizeof(short);
+ for (i = 0; i < gpm; i++) {
*op++ = *ip++;
*op++ = *ip++;
}
// finally, adjust the volume, if necessary
- if (software_mixer_volume!=1.0) {
+ if (software_mixer_volume != 1.0) {
// pthread_mutex_lock(&vol_mutex);
- op=outptr;
- for (i=0; i<frame_size+stuff; i++) {
+ op = outptr;
+ for (i = 0; i < frame_size + stuff; i++) {
*op = dithered_vol(*op);
op++;
*op = dithered_vol(*op);
@@ -792,11 +836,11 @@ static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) {
};
// pthread_mutex_unlock(&vol_mutex);
}
-
+
} else { // the whole frame, if no stuffing
-
+
// pthread_mutex_lock(&vol_mutex);
- for (i=0; i<frame_size; i++) {
+ for (i = 0; i < frame_size; i++) {
*op++ = dithered_vol(*ip++);
*op++ = dithered_vol(*ip++);
};
@@ -807,24 +851,25 @@ static int stuff_buffer_soxr(short *inptr, short *outptr, int stuff) {
#endif
typedef struct stats { // statistics for running averages
- int64_t sync_error,correction,drift;
+ int64_t sync_error, correction, drift;
} stats_t;
static void *player_thread_func(void *arg) {
connection_state_to_output = get_requested_connection_state_to_output();
-//this is about half a minute
+// this is about half a minute
#define trend_interval 3758
stats_t statistics[trend_interval];
- int number_of_statistics,oldest_statistic,newest_statistic;
+ int number_of_statistics, oldest_statistic, newest_statistic;
int at_least_one_frame_seen = 0;
- int64_t tsum_of_sync_errors,tsum_of_corrections,tsum_of_insertions_and_deletions,tsum_of_drifts;
- int64_t previous_sync_error,previous_correction;
+ int64_t tsum_of_sync_errors, tsum_of_corrections, tsum_of_insertions_and_deletions,
+ tsum_of_drifts;
+ int64_t previous_sync_error, previous_correction;
int64_t minimum_dac_queue_size = 1000000;
int32_t minimum_buffer_occupancy = BUFFER_FRAMES;
int32_t maximum_buffer_occupancy = 0;
-
- audio_information.valid=0;
-
+
+ audio_information.valid = 0;
+
int play_samples;
int64_t current_delay;
int play_number = 0;
@@ -834,19 +879,22 @@ static void *player_thread_func(void *arg) {
tsum_of_sync_errors = tsum_of_corrections = tsum_of_insertions_and_deletions = tsum_of_drifts = 0;
const int print_interval = trend_interval; // don't ask...
- // I think it's useful to keep this prime to prevent it from falling into a pattern with some other process.
-
- char rnstate[256];
- initstate(time(NULL),rnstate,256);
-
+ // I think it's useful to keep this prime to prevent it from falling into a pattern with some
+ // other process.
+
+ char rnstate[256];
+ initstate(time(NULL), rnstate, 256);
+
signed short *inbuf, *outbuf, *silence;
outbuf = malloc(OUTFRAME_BYTES(frame_size));
silence = malloc(OUTFRAME_BYTES(frame_size));
memset(silence, 0, OUTFRAME_BYTES(frame_size));
- late_packet_message_sent=0;
- missing_packets=late_packets=too_late_packets=resend_requests=0;
- flush_rtp_timestamp=0; // it seems this number has a special significance -- it seems to be used as a null operand, so we'll use it like that too
- int sync_error_out_of_bounds = 0; // number of times in a row that there's been a serious sync error
+ late_packet_message_sent = 0;
+ missing_packets = late_packets = too_late_packets = resend_requests = 0;
+ flush_rtp_timestamp = 0; // it seems this number has a special significance -- it seems to be used
+ // as a null operand, so we'll use it like that too
+ int sync_error_out_of_bounds =
+ 0; // number of times in a row that there's been a serious sync error
while (!please_stop) {
abuf_t *inframe = buffer_get_frame();
if (inframe) {
@@ -854,223 +902,246 @@ static void *player_thread_func(void *arg) {
if (inbuf) {
play_number++;
// if it's a supplied silent frame, let us know...
- if (inframe->timestamp==0) {
+ if (inframe->timestamp == 0) {
// debug(1,"Player has a supplied silent frame.");
- last_seqno_read = (SUCCESSOR(last_seqno_read)&0xffff); //manage the packet out of sequence minder
+ last_seqno_read =
+ (SUCCESSOR(last_seqno_read) & 0xffff); // manage the packet out of sequence minder
config.output->play(inbuf, frame_size);
} else {
- // We have a frame of data. We need to see if we want to add or remove a frame from it to keep in sync.
+ // We have a frame of data. We need to see if we want to add or remove a frame from it to
+ // keep in sync.
// So we calculate the timing error for the first frame in the DAC.
// If it's ahead of time, we add one audio frame to this frame to delay a subsequent frame
- // If it's late, we remove an audio frame from this frame to bring a subsequent frame forward in time
-
- at_least_one_frame_seen = 1;
-
- uint32_t reference_timestamp;
+ // If it's late, we remove an audio frame from this frame to bring a subsequent frame
+ // forward in time
+
+ at_least_one_frame_seen = 1;
+
+ uint32_t reference_timestamp;
uint64_t reference_timestamp_time;
- get_reference_timestamp_stuff(&reference_timestamp,&reference_timestamp_time);
+ get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time);
- int64_t rt,nt;
+ int64_t rt, nt;
rt = reference_timestamp;
nt = inframe->timestamp;
-
+
uint64_t local_time_now = get_absolute_time_in_fp();
- //struct timespec tn;
- //clock_gettime(CLOCK_MONOTONIC,&tn);
- //uint64_t local_time_now=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000;
+ // struct timespec tn;
+ // clock_gettime(CLOCK_MONOTONIC,&tn);
+ // uint64_t
+ // local_time_now=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000;
int64_t td_in_frames;
- int64_t td = local_time_now-reference_timestamp_time;
- // debug(1,"td is %lld.",td);
- if (td>=0) {
- td_in_frames = (td*44100)>>32;
+ int64_t td = local_time_now - reference_timestamp_time;
+ // debug(1,"td is %lld.",td);
+ if (td >= 0) {
+ td_in_frames = (td * 44100) >> 32;
} else {
- td_in_frames = -((-td*44100)>>32);
+ td_in_frames = -((-td * 44100) >> 32);
}
-
+
// This is the timing error for the next audio frame in the DAC, if applicable
- int64_t sync_error =0;
-
+ int64_t sync_error = 0;
+
int amount_to_stuff = 0;
-
+
// check sequencing
- if (last_seqno_read==-1)
- last_seqno_read=inframe->sequence_number;
+ if (last_seqno_read == -1)
+ last_seqno_read = inframe->sequence_number;
else {
- last_seqno_read = (SUCCESSOR(last_seqno_read) & 0xffff);
- if (inframe->sequence_number!=last_seqno_read) {
- debug(1,"Player: packets out of sequence: expected: %d, got: %d, sync error: %d frames.",last_seqno_read,inframe->sequence_number,sync_error);
- last_seqno_read=inframe->sequence_number; // reset warning...
+ last_seqno_read = (SUCCESSOR(last_seqno_read) & 0xffff);
+ if (inframe->sequence_number != last_seqno_read) {
+ debug(
+ 1,
+ "Player: packets out of sequence: expected: %d, got: %d, sync error: %d frames.",
+ last_seqno_read, inframe->sequence_number, sync_error);
+ last_seqno_read = inframe->sequence_number; // reset warning...
}
}
-
if (config.output->delay) {
current_delay = config.output->delay();
- if (current_delay==-1) {
- debug(1,"Delay error when checking running latency.");
- current_delay=0;
+ if (current_delay == -1) {
+ debug(1, "Delay error when checking running latency.");
+ current_delay = 0;
}
- if (current_delay<minimum_dac_queue_size)
- minimum_dac_queue_size=current_delay;
-
- uint32_t bo = seq_diff(ab_read,ab_write);
-
- if (bo<minimum_buffer_occupancy)
- minimum_buffer_occupancy=bo;
-
- if (bo>maximum_buffer_occupancy)
- maximum_buffer_occupancy=bo;
-
- // this is the actual delay, including the latency we actually want, which will fluctuate a good bit about a potentially rising or falling trend.
- int64_t delay = td_in_frames+rt-(nt-current_delay);
-
+ if (current_delay < minimum_dac_queue_size)
+ minimum_dac_queue_size = current_delay;
+
+ uint32_t bo = seq_diff(ab_read, ab_write);
+
+ if (bo < minimum_buffer_occupancy)
+ minimum_buffer_occupancy = bo;
+
+ if (bo > maximum_buffer_occupancy)
+ maximum_buffer_occupancy = bo;
+
+ // this is the actual delay, including the latency we actually want, which will
+ // fluctuate a good bit about a potentially rising or falling trend.
+ int64_t delay = td_in_frames + rt - (nt - current_delay);
+
// This is the timing error for the next audio frame in the DAC.
- sync_error = delay-config.latency;
-
+ sync_error = delay - config.latency;
+
// before we finally commit to this frame, check its sequencing and timing
-
+
// require a certain error before bothering to fix it...
- if (sync_error>config.tolerance) {
+ if (sync_error > config.tolerance) {
amount_to_stuff = -1;
}
- if (sync_error<-config.tolerance) {
+ if (sync_error < -config.tolerance) {
amount_to_stuff = 1;
}
-
+
// only allow stuffing if there is enough time to do it -- check DAC buffer...
- if (current_delay<DAC_BUFFER_QUEUE_MINIMUM_LENGTH) {
+ if (current_delay < DAC_BUFFER_QUEUE_MINIMUM_LENGTH) {
// debug(1,"DAC buffer too short to allow stuffing.");
- amount_to_stuff=0;
+ amount_to_stuff = 0;
}
// try to keep the corrections definitely below 1 in 1000 audio frames
if (amount_to_stuff) {
- uint32_t x = random()%1000;
- if (x>352)
- amount_to_stuff=0;
+ uint32_t x = random() % 1000;
+ if (x > 352)
+ amount_to_stuff = 0;
}
-
- if ((amount_to_stuff==0) && (fix_volume==0x10000)) {
+
+ if ((amount_to_stuff == 0) && (fix_volume == 0x10000)) {
// if no stuffing needed and no volume adjustment, then
- // don't send to stuff_buffer_* and don't copy to outbuf; just send directly to the output device...
+ // don't send to stuff_buffer_* and don't copy to outbuf; just send directly to the
+ // output device...
config.output->play(inbuf, frame_size);
} else {
#ifdef HAVE_LIBSOXR
switch (config.packet_stuffing) {
- case ST_basic:
-// if (amount_to_stuff) debug(1,"Basic stuff...");
- play_samples = stuff_buffer_basic(inbuf, outbuf,amount_to_stuff);
- break;
- case ST_soxr:
-// if (amount_to_stuff) debug(1,"Soxr stuff...");
- play_samples = stuff_buffer_soxr(inbuf, outbuf,amount_to_stuff);
- break;
- }
+ case ST_basic:
+ // if (amount_to_stuff) debug(1,"Basic stuff...");
+ play_samples = stuff_buffer_basic(inbuf, outbuf, amount_to_stuff);
+ break;
+ case ST_soxr:
+ // if (amount_to_stuff) debug(1,"Soxr stuff...");
+ play_samples = stuff_buffer_soxr(inbuf, outbuf, amount_to_stuff);
+ break;
+ }
#else
-// if (amount_to_stuff) debug(1,"Standard stuff...");
- play_samples = stuff_buffer_basic(inbuf, outbuf,amount_to_stuff);
+ // if (amount_to_stuff) debug(1,"Standard stuff...");
+ play_samples = stuff_buffer_basic(inbuf, outbuf, amount_to_stuff);
#endif
- /*
- {
- int co;
- int is_silent=1;
- short *p = outbuf;
- for (co=0;co<play_samples;co++) {
- if (*p!=0)
- is_silent=0;
- p++;
- }
- if (is_silent)
- debug(1,"Silence!");
- }
- */
+ /*
+ {
+ int co;
+ int is_silent=1;
+ short *p = outbuf;
+ for (co=0;co<play_samples;co++) {
+ if (*p!=0)
+ is_silent=0;
+ p++;
+ }
+ if (is_silent)
+ debug(1,"Silence!");
+ }
+ */
- config.output->play(outbuf, play_samples);
+ config.output->play(outbuf, play_samples);
}
-
+
// check for loss of sync
// timestamp of zero means an inserted silent frame in place of a missing frame
- if ((inframe->timestamp!=0) && (!please_stop) && (config.resyncthreshold!=0) && (abs(sync_error)>config.resyncthreshold)) {
+ if ((inframe->timestamp != 0) && (!please_stop) && (config.resyncthreshold != 0) &&
+ (abs(sync_error) > config.resyncthreshold)) {
sync_error_out_of_bounds++;
- // debug(1,"Sync error out of bounds: Error: %lld; previous error: %lld; DAC: %lld; timestamp: %llx, time now %llx",sync_error,previous_sync_error,current_delay,inframe->timestamp,local_time_now);
- if (sync_error_out_of_bounds>3) {
- debug(1,"Lost sync with source for %d consecutive packets -- flushing and resyncing. Error: %lld.",sync_error_out_of_bounds,sync_error);
+ // debug(1,"Sync error out of bounds: Error: %lld; previous error: %lld; DAC: %lld;
+ // timestamp: %llx, time now
+ // %llx",sync_error,previous_sync_error,current_delay,inframe->timestamp,local_time_now);
+ if (sync_error_out_of_bounds > 3) {
+ debug(1, "Lost sync with source for %d consecutive packets -- flushing and "
+ "resyncing. Error: %lld.",
+ sync_error_out_of_bounds, sync_error);
sync_error_out_of_bounds = 0;
player_flush(nt);
- }
+ }
} else {
sync_error_out_of_bounds = 0;
}
} else {
// if there is no delay procedure, there can be no synchronising
- if (fix_volume==0x10000)
+ if (fix_volume == 0x10000)
config.output->play(inbuf, frame_size);
else {
- play_samples = stuff_buffer_basic(inbuf, outbuf,0);
+ play_samples = stuff_buffer_basic(inbuf, outbuf, 0);
config.output->play(outbuf, frame_size);
}
}
- // mark the frame as finished
- inframe->timestamp=0;
+ // mark the frame as finished
+ inframe->timestamp = 0;
inframe->sequence_number = 0;
-
+
// debug(1,"Sync error %lld frames. Amount to stuff %d." ,sync_error,amount_to_stuff);
-
- // new stats calculation. We want a running average of sync error, drift, adjustment, number of additions+subtractions
-
- if (number_of_statistics==trend_interval) {
+
+ // new stats calculation. We want a running average of sync error, drift, adjustment,
+ // number of additions+subtractions
+
+ if (number_of_statistics == trend_interval) {
// here we remove the oldest statistical data and take it from the summaries as well
- tsum_of_sync_errors-=statistics[oldest_statistic].sync_error;
+ tsum_of_sync_errors -= statistics[oldest_statistic].sync_error;
tsum_of_drifts -= statistics[oldest_statistic].drift;
- if (statistics[oldest_statistic].correction>0)
- tsum_of_insertions_and_deletions-=statistics[oldest_statistic].correction;
+ if (statistics[oldest_statistic].correction > 0)
+ tsum_of_insertions_and_deletions -= statistics[oldest_statistic].correction;
else
- tsum_of_insertions_and_deletions+=statistics[oldest_statistic].correction;
- tsum_of_corrections-=statistics[oldest_statistic].correction;
- oldest_statistic=(oldest_statistic+1)%trend_interval;
- number_of_statistics--;
+ tsum_of_insertions_and_deletions += statistics[oldest_statistic].correction;
+ tsum_of_corrections -= statistics[oldest_statistic].correction;
+ oldest_statistic = (oldest_statistic + 1) % trend_interval;
+ number_of_statistics--;
}
-
+
statistics[newest_statistic].sync_error = sync_error;
statistics[newest_statistic].correction = amount_to_stuff;
- if (number_of_statistics==0)
+ if (number_of_statistics == 0)
statistics[newest_statistic].drift = 0;
else
- statistics[newest_statistic].drift = sync_error-previous_sync_error-previous_correction;
+ statistics[newest_statistic].drift =
+ sync_error - previous_sync_error - previous_correction;
previous_sync_error = sync_error;
previous_correction = amount_to_stuff;
tsum_of_sync_errors += sync_error;
tsum_of_drifts += statistics[newest_statistic].drift;
- if (amount_to_stuff>0)
- tsum_of_insertions_and_deletions+=amount_to_stuff;
+ if (amount_to_stuff > 0)
+ tsum_of_insertions_and_deletions += amount_to_stuff;
else
- tsum_of_insertions_and_deletions-=amount_to_stuff;
- tsum_of_corrections+=amount_to_stuff;
-
- newest_statistic=(newest_statistic+1)%trend_interval;
+ tsum_of_insertions_and_deletions -= amount_to_stuff;
+ tsum_of_corrections += amount_to_stuff;
+
+ newest_statistic = (newest_statistic + 1) % trend_interval;
number_of_statistics++;
-
}
- if (play_number%print_interval==0) {
- // we can now calculate running averages for sync error (frames), corrections (ppm), insertions plus deletions (ppm), drift (ppm)
- double moving_average_sync_error = (1.0*tsum_of_sync_errors)/number_of_statistics;
- double moving_average_correction = (1.0*tsum_of_corrections)/number_of_statistics;
- double moving_average_insertions_plus_deletions = (1.0*tsum_of_insertions_and_deletions)/number_of_statistics;
- double moving_average_drift = (1.0*tsum_of_drifts)/number_of_statistics;
+ if (play_number % print_interval == 0) {
+ // we can now calculate running averages for sync error (frames), corrections (ppm),
+ // insertions plus deletions (ppm), drift (ppm)
+ double moving_average_sync_error = (1.0 * tsum_of_sync_errors) / number_of_statistics;
+ double moving_average_correction = (1.0 * tsum_of_corrections) / number_of_statistics;
+ double moving_average_insertions_plus_deletions =
+ (1.0 * tsum_of_insertions_and_deletions) / number_of_statistics;
+ double moving_average_drift = (1.0 * tsum_of_drifts) / number_of_statistics;
// if ((play_number/print_interval)%20==0)
if (config.statistics_requested)
if (at_least_one_frame_seen)
- inform("Sync error: %.1f (frames); net correction: %.1f (ppm); corrections: %.1f (ppm); missing packets %llu; late packets %llu; too late packets %llu; resend requests %llu; min DAC queue size %lli, min and max buffer occupancy %u and %u.", moving_average_sync_error, moving_average_correction*1000000/352, moving_average_insertions_plus_deletions*1000000/352,missing_packets,late_packets,too_late_packets,resend_requests,minimum_dac_queue_size,minimum_buffer_occupancy,maximum_buffer_occupancy);
+ inform("Sync error: %.1f (frames); net correction: %.1f (ppm); corrections: %.1f "
+ "(ppm); missing packets %llu; late packets %llu; too late packets %llu; "
+ "resend requests %llu; min DAC queue size %lli, min and max buffer occupancy "
+ "%u and %u.",
+ moving_average_sync_error, moving_average_correction * 1000000 / 352,
+ moving_average_insertions_plus_deletions * 1000000 / 352, missing_packets,
+ late_packets, too_late_packets, resend_requests, minimum_dac_queue_size,
+ minimum_buffer_occupancy, maximum_buffer_occupancy);
else
inform("No frames received in the last sampling interval.");
- minimum_dac_queue_size=1000000; // hack reset
- maximum_buffer_occupancy = 0; // can't be less than this
+ minimum_dac_queue_size = 1000000; // hack reset
+ maximum_buffer_occupancy = 0; // can't be less than this
minimum_buffer_occupancy = BUFFER_FRAMES; // can't be more than this
at_least_one_frame_seen = 0;
}
@@ -1082,29 +1153,33 @@ static void *player_thread_func(void *arg) {
return 0;
}
-
// takes the volume as specified by the airplay protocol
void player_volume(double f) {
-// The volume ranges -144.0 (mute) or -30 -- 0. See http://git.zx2c4.com/Airtunes2/about/#setting-volume
-// By examination, the -30 -- 0 range is linear on the slider; i.e. the slider is calibrated in 30 equal increments
-// So, we will pass this on without any weighting if we have a hardware mixer, as we expect the mixer to be calibrated in dB.
-
-// Here, we ask for an attenuation we will apply in software. The dB range of a value from 1 to 65536 is about 48.1 dB (log10 of 65536 is 4.8164).
-// Thus, we ask our vol2attn function for an appropriate dB between -48.1 and 0 dB and translate it back to a number.
-
- double scaled_volume = vol2attn(f,0,-4810);
- double linear_volume = pow(10,scaled_volume/1000);
-
- if(f == -144.0)
+ // The volume ranges -144.0 (mute) or -30 -- 0. See
+ // http://git.zx2c4.com/Airtunes2/about/#setting-volume
+ // By examination, the -30 -- 0 range is linear on the slider; i.e. the slider is calibrated in 30
+ // equal increments
+ // So, we will pass this on without any weighting if we have a hardware mixer, as we expect the
+ // mixer to be calibrated in dB.
+
+ // Here, we ask for an attenuation we will apply in software. The dB range of a value from 1 to
+ // 65536 is about 48.1 dB (log10 of 65536 is 4.8164).
+ // Thus, we ask our vol2attn function for an appropriate dB between -48.1 and 0 dB and translate
+ // it back to a number.
+
+ double scaled_volume = vol2attn(f, 0, -4810);
+ double linear_volume = pow(10, scaled_volume / 1000);
+
+ if (f == -144.0)
linear_volume = 0.0;
-
+
if (config.output->volume) {
- config.output->volume(f); // volume will be sent as metadata by the config.output device
- linear_volume=1.0; // no attenuation needed -- this value is used as a flag to avoid calculations
+ config.output->volume(f); // volume will be sent as metadata by the config.output device
+ linear_volume =
+ 1.0; // no attenuation needed -- this value is used as a flag to avoid calculations
}
-
-
+
if (config.output->parameters)
config.output->parameters(&audio_information);
else {
@@ -1115,42 +1190,44 @@ void player_volume(double f) {
audio_information.has_true_mute = 0;
audio_information.is_muted = 0;
}
- audio_information.valid=1;
+ audio_information.valid = 1;
pthread_mutex_lock(&vol_mutex);
- software_mixer_volume = linear_volume;
- fix_volume = 65536.0 * software_mixer_volume;
+ software_mixer_volume = linear_volume;
+ fix_volume = 65536.0 * software_mixer_volume;
pthread_mutex_unlock(&vol_mutex);
#ifdef CONFIG_METADATA
char *dv = malloc(64); // will be freed in the metadata thread
if (dv) {
- memset(dv,0,64);
- snprintf(dv,63,"%.2f,%.2f,%.2f,%.2f",audio_information.airplay_volume,audio_information.current_volume_dB/100.0,audio_information.minimum_volume_dB/100.0,audio_information.maximum_volume_dB/100.0);
- send_ssnc_metadata('pvol',dv,strlen(dv),1);
+ memset(dv, 0, 64);
+ snprintf(dv, 63, "%.2f,%.2f,%.2f,%.2f", audio_information.airplay_volume,
+ audio_information.current_volume_dB / 100.0,
+ audio_information.minimum_volume_dB / 100.0,
+ audio_information.maximum_volume_dB / 100.0);
+ send_ssnc_metadata('pvol', dv, strlen(dv), 1);
}
#endif
-
}
void player_flush(uint32_t timestamp) {
- // debug(1,"Flush requested up to %u. It seems as if 0 is special.",timestamp);
+ // debug(1,"Flush requested up to %u. It seems as if 0 is special.",timestamp);
pthread_mutex_lock(&flush_mutex);
- flush_requested=1;
- //if (timestamp!=0)
- flush_rtp_timestamp=timestamp; // flush all packets up to (and including?) this
+ flush_requested = 1;
+ // if (timestamp!=0)
+ flush_rtp_timestamp = timestamp; // flush all packets up to (and including?) this
pthread_mutex_unlock(&flush_mutex);
#ifdef CONFIG_METADATA
- send_ssnc_metadata('pfls',NULL,0,1);
+ send_ssnc_metadata('pfls', NULL, 0, 1);
#endif
}
int player_play(stream_cfg *stream) {
packet_count = 0;
if (config.buffer_start_fill > BUFFER_FRAMES)
- die("specified buffer starting fill %d > buffer size %d",
- config.buffer_start_fill, BUFFER_FRAMES);
+ die("specified buffer starting fill %d > buffer size %d", config.buffer_start_fill,
+ BUFFER_FRAMES);
#ifdef HAVE_LIBPOLARSSL
- memset(&dctx,0,sizeof(aes_context));
+ memset(&dctx, 0, sizeof(aes_context));
aes_setkey_dec(&dctx, stream->aeskey, 128);
#endif
@@ -1165,21 +1242,21 @@ int player_play(stream_cfg *stream) {
please_stop = 0;
command_start();
#ifdef CONFIG_METADATA
- send_ssnc_metadata('pbeg',NULL,0,1);
+ send_ssnc_metadata('pbeg', NULL, 0, 1);
#endif
-
- // set the flowcontrol condition variable to wait on a monotonic clock
+
+// set the flowcontrol condition variable to wait on a monotonic clock
#ifdef COMPILE_FOR_LINUX
pthread_condattr_t attr;
pthread_condattr_init(&attr);
- pthread_condattr_setclock( &attr, CLOCK_MONOTONIC); // can't do this in OS X, and don't need it.
- int rc = pthread_cond_init(&flowcontrol,&attr);
+ pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); // can't do this in OS X, and don't need it.
+ int rc = pthread_cond_init(&flowcontrol, &attr);
#endif
#ifdef COMPILE_FOR_OSX
- int rc = pthread_cond_init(&flowcontrol,NULL);
+ int rc = pthread_cond_init(&flowcontrol, NULL);
#endif
if (rc)
- debug(1,"Error initialising condition variable.");
+ debug(1, "Error initialising condition variable.");
config.output->start(sampling_rate);
pthread_create(&player_thread, NULL, player_thread_func, NULL);
@@ -1191,7 +1268,7 @@ void player_stop(void) {
pthread_cond_signal(&flowcontrol); // tell it to give up
pthread_join(player_thread, NULL);
#ifdef CONFIG_METADATA
- send_ssnc_metadata('pend',NULL,0,1);
+ send_ssnc_metadata('pend', NULL, 0, 1);
#endif
config.output->stop();
command_stop();
@@ -1199,5 +1276,5 @@ void player_stop(void) {
free_decoder();
int rc = pthread_cond_destroy(&flowcontrol);
if (rc)
- debug(1,"Error destroying condition variable.");
+ debug(1, "Error destroying condition variable.");
}
diff --git a/player.h b/player.h
index 0a9f82a..9f4b93d 100644
--- a/player.h
+++ b/player.h
@@ -4,8 +4,8 @@
#include "audio.h"
typedef struct {
- uint8_t aesiv[16], aeskey[16];
- int32_t fmtp[12];
+ uint8_t aesiv[16], aeskey[16];
+ int32_t fmtp[12];
} stream_cfg;
typedef uint16_t seq_t;
@@ -19,6 +19,6 @@ void player_stop(void);
void player_volume(double f);
void player_flush(uint32_t timestamp);
-void player_put_packet(seq_t seqno,uint32_t timestamp, uint8_t *data, int len);
+void player_put_packet(seq_t seqno, uint32_t timestamp, uint8_t *data, int len);
#endif //_PLAYER_H
diff --git a/rtp.c b/rtp.c
index dea71c3..5590e64 100644
--- a/rtp.c
+++ b/rtp.c
@@ -41,10 +41,9 @@
#include "player.h"
#include "rtp.h"
-
typedef struct {
- uint32_t seconds;
- uint32_t fraction;
+ uint32_t seconds;
+ uint32_t fraction;
} ntp_timestamp;
typedef struct time_ping_record {
@@ -57,14 +56,14 @@ static int running = 0;
static int please_shutdown;
static char client_ip_string[INET6_ADDRSTRLEN]; // the ip string pointing to the client
-static short client_ip_family; // AF_INET / AF_INET6
-static uint32_t client_active_remote; // used when you want to control the client...
+static short client_ip_family; // AF_INET / AF_INET6
+static uint32_t client_active_remote; // used when you want to control the client...
static SOCKADDR rtp_client_control_socket; // a socket pointing to the control port of the client
-static SOCKADDR rtp_client_timing_socket; // a socket pointing to the timing port of the client
-static int audio_socket; // our local [server] audio socket
-static int control_socket; // our local [server] control socket
-static int timing_socket; // local timing socket
+static SOCKADDR rtp_client_timing_socket; // a socket pointing to the timing port of the client
+static int audio_socket; // our local [server] audio socket
+static int control_socket; // our local [server] control socket
+static int timing_socket; // local timing socket
static pthread_t rtp_audio_thread, rtp_control_thread, rtp_timing_thread;
static uint32_t reference_timestamp;
@@ -73,168 +72,175 @@ static uint64_t reference_timestamp_time;
// debug variables
static int request_sent;
-#define time_ping_history 8
-#define time_ping_fudge_factor 100000
+#define time_ping_history 8
+#define time_ping_fudge_factor 100000
static uint8_t time_ping_count;
struct time_ping_record time_pings[time_ping_history];
-//static struct timespec dtt; // dangerous -- this assumes that there will never be two timing request in flight at the same time
-static uint64_t departure_time; // dangerous -- this assumes that there will never be two timing request in flight at the same time
+// static struct timespec dtt; // dangerous -- this assumes that there will never be two timing
+// request in flight at the same time
+static uint64_t departure_time; // dangerous -- this assumes that there will never be two timing
+ // request in flight at the same time
static pthread_mutex_t reference_time_mutex = PTHREAD_MUTEX_INITIALIZER;
uint64_t static local_to_remote_time_difference; // used to switch between local and remote clocks
static void *rtp_audio_receiver(void *arg) {
- // we inherit the signal mask (SIGUSR1)
-
- int32_t last_seqno = -1;
- uint8_t packet[2048], *pktp;
-
- ssize_t nread;
- while (1) {
- if (please_shutdown)
- break;
- nread = recv(audio_socket, packet, sizeof(packet), 0);
- if (nread < 0)
- break;
-
- ssize_t plen = nread;
- uint8_t type = packet[1] & ~0x80;
- if (type == 0x60 || type == 0x56) { // audio data / resend
- pktp = packet;
- if (type==0x56) {
- pktp += 4;
- plen -= 4;
- }
- seq_t seqno = ntohs(*(unsigned short *)(pktp+2));
- // increment last_seqno and see if it's the same as the incoming seqno
-
- if (last_seqno==-1)
- last_seqno=seqno;
- else {
- last_seqno = (last_seqno+1)&0xffff;
- if (seqno!=last_seqno)
- debug(2,"RTP: Packets out of sequence: expected: %d, got %d.",last_seqno,seqno);
- last_seqno=seqno; // reset warning...
- }
- uint32_t timestamp = ntohl(*(unsigned long *)(pktp+4));
-
- //if (packet[1]&0x10)
- // debug(1,"Audio packet Extension bit set.");
-
- pktp += 12;
- plen -= 12;
-
- // check if packet contains enough content to be reasonable
- if (plen >= 16) {
- player_put_packet(seqno,timestamp, pktp, plen);
- continue;
- }
- if (type == 0x56 && seqno == 0) {
- debug(2, "resend-related request packet received, ignoring.");
- continue;
- }
- debug(1, "Audio receiver -- Unknown RTP packet of type 0x%02X length %d seqno %d", type, nread, seqno);
- }
- warn("Audio receiver -- Unknown RTP packet of type 0x%02X length %d.", type, nread);
+ // we inherit the signal mask (SIGUSR1)
+
+ int32_t last_seqno = -1;
+ uint8_t packet[2048], *pktp;
+
+ ssize_t nread;
+ while (1) {
+ if (please_shutdown)
+ break;
+ nread = recv(audio_socket, packet, sizeof(packet), 0);
+ if (nread < 0)
+ break;
+
+ ssize_t plen = nread;
+ uint8_t type = packet[1] & ~0x80;
+ if (type == 0x60 || type == 0x56) { // audio data / resend
+ pktp = packet;
+ if (type == 0x56) {
+ pktp += 4;
+ plen -= 4;
+ }
+ seq_t seqno = ntohs(*(unsigned short *)(pktp + 2));
+ // increment last_seqno and see if it's the same as the incoming seqno
+
+ if (last_seqno == -1)
+ last_seqno = seqno;
+ else {
+ last_seqno = (last_seqno + 1) & 0xffff;
+ if (seqno != last_seqno)
+ debug(2, "RTP: Packets out of sequence: expected: %d, got %d.", last_seqno, seqno);
+ last_seqno = seqno; // reset warning...
+ }
+ uint32_t timestamp = ntohl(*(unsigned long *)(pktp + 4));
+
+ // if (packet[1]&0x10)
+ // debug(1,"Audio packet Extension bit set.");
+
+ pktp += 12;
+ plen -= 12;
+
+ // check if packet contains enough content to be reasonable
+ if (plen >= 16) {
+ player_put_packet(seqno, timestamp, pktp, plen);
+ continue;
+ }
+ if (type == 0x56 && seqno == 0) {
+ debug(2, "resend-related request packet received, ignoring.");
+ continue;
+ }
+ debug(1, "Audio receiver -- Unknown RTP packet of type 0x%02X length %d seqno %d", type,
+ nread, seqno);
}
+ warn("Audio receiver -- Unknown RTP packet of type 0x%02X length %d.", type, nread);
+ }
- debug(1, "Audio receiver -- Server RTP thread interrupted. terminating.");
- close(audio_socket);
+ debug(1, "Audio receiver -- Server RTP thread interrupted. terminating.");
+ close(audio_socket);
- return NULL;
+ return NULL;
}
static void *rtp_control_receiver(void *arg) {
- // we inherit the signal mask (SIGUSR1)
- reference_timestamp=0; // nothing valid received yet
- uint8_t packet[2048];
- struct timespec tn;
- uint64_t remote_time_of_sync,local_time_now, remote_time_now;
- uint32_t sync_rtp_timestamp,rtp_timestamp_less_latency;
- ssize_t nread;
- while (1) {
- if (please_shutdown)
- break;
- nread = recv(control_socket, packet, sizeof(packet), 0);
- local_time_now=get_absolute_time_in_fp();
-// clock_gettime(CLOCK_MONOTONIC,&tn);
-// local_time_now=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000;
-
- if (nread < 0)
- break;
-
- ssize_t plen = nread;
- if (packet[1] == 0xd4) { // sync data
- /*
- char obf[4096];
- char *obfp = obf;
- int obfc;
- for (obfc=0;obfc<plen;obfc++) {
- sprintf(obfp,"%02X",packet[obfc]);
- obfp+=2;
- };
- *obfp=0;
- debug(1,"Sync Packet Received: \"%s\"",obf);
- */
- if (local_to_remote_time_difference) { // need a time packet to be interchanged first...
-
- remote_time_of_sync = (uint64_t)ntohl(*((uint32_t*)&packet[8]))<<32;
- remote_time_of_sync += ntohl(*((uint32_t*)&packet[12]));
-
- // debug(1,"Remote Sync Time: %0llx.",remote_time_of_sync);
-
- rtp_timestamp_less_latency = ntohl(*((uint32_t*)&packet[4]));
- sync_rtp_timestamp = ntohl(*((uint32_t*)&packet[16]));
-
- if (packet[0]&0x10) {
- // if it's a packet right after a flush or resume
- sync_rtp_timestamp += 352; // add frame_size -- can't see a reference to this anywhere, but it seems to get everything into sync.
- // it's as if the first sync after a flush or resume is the timing of the next packet after the one whose RTP is given. Weird.
- }
- pthread_mutex_lock(&reference_time_mutex);
- reference_timestamp_time = remote_time_of_sync-local_to_remote_time_difference;
- reference_timestamp = sync_rtp_timestamp;
- pthread_mutex_unlock(&reference_time_mutex);
- // debug(1,"New Reference timestamp and timestamp time...");
- // get estimated remote time now
- remote_time_now = local_time_now+local_to_remote_time_difference;
-
- //debug(1,"Sync Time is %lld us late (remote times).",((remote_time_now-remote_time_of_sync)*1000000)>>32);
- //debug(1,"Sync Time is %lld us late (local times).",((local_time_now-reference_timestamp_time)*1000000)>>32);
- } else {
- debug(1,"Sync packet received before we got a timing packet back.");
- }
- } else
- debug(1,"Control Port -- Unknown RTP packet of type 0x%02X length %d.", packet[1], nread);
- }
+ // we inherit the signal mask (SIGUSR1)
+ reference_timestamp = 0; // nothing valid received yet
+ uint8_t packet[2048];
+ struct timespec tn;
+ uint64_t remote_time_of_sync, local_time_now, remote_time_now;
+ uint32_t sync_rtp_timestamp, rtp_timestamp_less_latency;
+ ssize_t nread;
+ while (1) {
+ if (please_shutdown)
+ break;
+ nread = recv(control_socket, packet, sizeof(packet), 0);
+ local_time_now = get_absolute_time_in_fp();
+ // clock_gettime(CLOCK_MONOTONIC,&tn);
+ // local_time_now=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000;
+
+ if (nread < 0)
+ break;
+
+ ssize_t plen = nread;
+ if (packet[1] == 0xd4) { // sync data
+ /*
+ char obf[4096];
+ char *obfp = obf;
+ int obfc;
+ for (obfc=0;obfc<plen;obfc++) {
+ sprintf(obfp,"%02X",packet[obfc]);
+ obfp+=2;
+ };
+ *obfp=0;
+ debug(1,"Sync Packet Received: \"%s\"",obf);
+ */
+ if (local_to_remote_time_difference) { // need a time packet to be interchanged first...
+
+ remote_time_of_sync = (uint64_t)ntohl(*((uint32_t *)&packet[8])) << 32;
+ remote_time_of_sync += ntohl(*((uint32_t *)&packet[12]));
+
+ // debug(1,"Remote Sync Time: %0llx.",remote_time_of_sync);
+
+ rtp_timestamp_less_latency = ntohl(*((uint32_t *)&packet[4]));
+ sync_rtp_timestamp = ntohl(*((uint32_t *)&packet[16]));
+
+ if (packet[0] & 0x10) {
+ // if it's a packet right after a flush or resume
+ sync_rtp_timestamp += 352; // add frame_size -- can't see a reference to this anywhere,
+ // but it seems to get everything into sync.
+ // it's as if the first sync after a flush or resume is the timing of the next packet
+ // after the one whose RTP is given. Weird.
+ }
+ pthread_mutex_lock(&reference_time_mutex);
+ reference_timestamp_time = remote_time_of_sync - local_to_remote_time_difference;
+ reference_timestamp = sync_rtp_timestamp;
+ pthread_mutex_unlock(&reference_time_mutex);
+ // debug(1,"New Reference timestamp and timestamp time...");
+ // get estimated remote time now
+ remote_time_now = local_time_now + local_to_remote_time_difference;
+
+ // debug(1,"Sync Time is %lld us late (remote
+ // times).",((remote_time_now-remote_time_of_sync)*1000000)>>32);
+ // debug(1,"Sync Time is %lld us late (local
+ // times).",((local_time_now-reference_timestamp_time)*1000000)>>32);
+ } else {
+ debug(1, "Sync packet received before we got a timing packet back.");
+ }
+ } else
+ debug(1, "Control Port -- Unknown RTP packet of type 0x%02X length %d.", packet[1], nread);
+ }
- debug(1, "Control RTP thread interrupted. terminating.");
- close(control_socket);
+ debug(1, "Control RTP thread interrupted. terminating.");
+ close(control_socket);
- return NULL;
+ return NULL;
}
static void *rtp_timing_sender(void *arg) {
- struct timing_request {
+ struct timing_request {
char leader;
char type;
uint16_t seqno;
uint32_t filler;
- uint64_t origin,receive,transmit;
+ uint64_t origin, receive, transmit;
};
-
- uint64_t request_number=0;
-
- struct timing_request req; // *not* a standard RTCP NACK
-
+
+ uint64_t request_number = 0;
+
+ struct timing_request req; // *not* a standard RTCP NACK
+
req.leader = 0x80;
- req.type = 0xd2; // Timing request
+ req.type = 0xd2; // Timing request
req.filler = 0;
- req.seqno=htons(7);
-
+ req.seqno = htons(7);
+
time_ping_count = 0;
// we inherit the signal mask (SIGUSR1)
@@ -245,392 +251,404 @@ static void *rtp_timing_sender(void *arg) {
if (!running)
die("rtp_timing_sender called without active stream!");
- //debug(1, "Requesting ntp timestamp exchange.");
+ // debug(1, "Requesting ntp timestamp exchange.");
req.filler = 0;
- req.origin = req.receive = req.transmit=0;
+ req.origin = req.receive = req.transmit = 0;
-// clock_gettime(CLOCK_MONOTONIC,&dtt);
- departure_time=get_absolute_time_in_fp();
- socklen_t msgsize = sizeof(struct sockaddr_in);
+ // clock_gettime(CLOCK_MONOTONIC,&dtt);
+ departure_time = get_absolute_time_in_fp();
+ socklen_t msgsize = sizeof(struct sockaddr_in);
#ifdef AF_INET6
- if (rtp_client_timing_socket.SAFAMILY==AF_INET6) {
- msgsize = sizeof(struct sockaddr_in6);
- }
+ if (rtp_client_timing_socket.SAFAMILY == AF_INET6) {
+ msgsize = sizeof(struct sockaddr_in6);
+ }
#endif
- if (sendto(timing_socket, &req, sizeof(req), 0, (struct sockaddr*)&rtp_client_timing_socket, msgsize)==-1) {
- perror("Error sendto-ing to timing socket");
- }
+ if (sendto(timing_socket, &req, sizeof(req), 0, (struct sockaddr *)&rtp_client_timing_socket,
+ msgsize) == -1) {
+ perror("Error sendto-ing to timing socket");
+ }
request_number++;
- if (request_number<=4)
+ if (request_number <= 4)
usleep(500000);
else
sleep(3);
}
- debug(1, "rtp_timing_sender thread interrupted. terminating.");
+ debug(1, "rtp_timing_sender thread interrupted. terminating.");
return NULL;
}
static void *rtp_timing_receiver(void *arg) {
- // we inherit the signal mask (SIGUSR1)
- uint8_t packet[2048], *pktp;
- ssize_t nread;
- pthread_t timer_requester;
- pthread_create(&timer_requester, NULL, &rtp_timing_sender, NULL);
-// struct timespec att;
- uint64_t distant_receive_time,distant_transmit_time,arrival_time,return_time,transit_time,processing_time;
- local_to_remote_time_jitters = 0;
- local_to_remote_time_jitters_count = 0;
- uint64_t l2rtd=0;
- while (1) {
- if (please_shutdown)
- break;
- nread = recv(timing_socket, packet, sizeof(packet), 0);
- arrival_time=get_absolute_time_in_fp();
-// clock_gettime(CLOCK_MONOTONIC,&att);
-
- if (nread < 0)
- break;
-
- ssize_t plen = nread;
- //debug(1,"Packet Received on Timing Port.");
- if (packet[1] == 0xd3) { // timing reply
- /*
- char obf[4096];
- char *obfp = obf;
- int obfc;
- for (obfc=0;obfc<plen;obfc++) {
- sprintf(obfp,"%02X",packet[obfc]);
- obfp+=2;
- };
- *obfp=0;
- //debug(1,"Timing Packet Received: \"%s\"",obf);
- */
-
- //arrival_time = ((uint64_t)att.tv_sec<<32)+((uint64_t)att.tv_nsec<<32)/1000000000;
- //departure_time = ((uint64_t)dtt.tv_sec<<32)+((uint64_t)dtt.tv_nsec<<32)/1000000000;
-
- return_time = arrival_time-departure_time;
-
- // uint64_t rtus = (return_time*1000000)>>32; debug(1,"Time ping turnaround time: %lld us.",rtus);
-
- //distant_receive_time = ((uint64_t)ntohl(*((uint32_t*)&packet[16])))<<32+ntohl(*((uint32_t*)&packet[20]));
-
- distant_receive_time = (uint64_t)ntohl(*((uint32_t*)&packet[16]))<<32;
- distant_receive_time += ntohl(*((uint32_t*)&packet[20]));
-
- //distant_transmit_time = ((uint64_t)ntohl(*((uint32_t*)&packet[24])))<<32+ntohl(*((uint32_t*)&packet[28]));
-
- distant_transmit_time = (uint64_t)ntohl(*((uint32_t*)&packet[24]))<<32;
- distant_transmit_time += ntohl(*((uint32_t*)&packet[28]));
-
- processing_time = distant_transmit_time-distant_receive_time;
-
- // debug(1,"Return trip time: %lluuS, remote processing time: %lluuS.",(return_time*1000000)>>32,(processing_time*1000000)>>32);
-
- uint64_t local_time_by_remote_clock = distant_transmit_time+return_time/2;
-
- unsigned int cc;
- for (cc=time_ping_history-1;cc>0;cc--) {
- time_pings[cc]=time_pings[cc-1];
- time_pings[cc].dispersion = (time_pings[cc].dispersion*133)/100; // make the dispersions 'age' by this rational factor
- }
- time_pings[0].local_to_remote_difference = local_time_by_remote_clock-arrival_time;
- time_pings[0].dispersion = return_time;
- if (time_ping_count<time_ping_history)
- time_ping_count++;
-
-
- // now pick the timestamp with the lowest dispersion
- uint64_t l2rtd = time_pings[0].local_to_remote_difference;
- uint64_t tld = time_pings[0].dispersion;
- for (cc=1;cc<time_ping_count;cc++)
- if (time_pings[cc].dispersion<tld) {
- l2rtd=time_pings[cc].local_to_remote_difference;
- tld=time_pings[cc].dispersion;
- }
- int64_t ji;
-
- if (time_ping_count>1) {
- if (l2rtd>local_to_remote_time_difference) {
- local_to_remote_time_jitters=local_to_remote_time_jitters+l2rtd-local_to_remote_time_difference;
- ji = l2rtd-local_to_remote_time_difference;
- } else {
- local_to_remote_time_jitters=local_to_remote_time_jitters+local_to_remote_time_difference-l2rtd;
- ji = - (local_to_remote_time_difference-l2rtd);
- }
- local_to_remote_time_jitters_count+=1;
+ // we inherit the signal mask (SIGUSR1)
+ uint8_t packet[2048], *pktp;
+ ssize_t nread;
+ pthread_t timer_requester;
+ pthread_create(&timer_requester, NULL, &rtp_timing_sender, NULL);
+ // struct timespec att;
+ uint64_t distant_receive_time, distant_transmit_time, arrival_time, return_time, transit_time,
+ processing_time;
+ local_to_remote_time_jitters = 0;
+ local_to_remote_time_jitters_count = 0;
+ uint64_t l2rtd = 0;
+ while (1) {
+ if (please_shutdown)
+ break;
+ nread = recv(timing_socket, packet, sizeof(packet), 0);
+ arrival_time = get_absolute_time_in_fp();
+ // clock_gettime(CLOCK_MONOTONIC,&att);
+
+ if (nread < 0)
+ break;
+
+ ssize_t plen = nread;
+ // debug(1,"Packet Received on Timing Port.");
+ if (packet[1] == 0xd3) { // timing reply
+ /*
+ char obf[4096];
+ char *obfp = obf;
+ int obfc;
+ for (obfc=0;obfc<plen;obfc++) {
+ sprintf(obfp,"%02X",packet[obfc]);
+ obfp+=2;
+ };
+ *obfp=0;
+ //debug(1,"Timing Packet Received: \"%s\"",obf);
+ */
+
+ // arrival_time = ((uint64_t)att.tv_sec<<32)+((uint64_t)att.tv_nsec<<32)/1000000000;
+ // departure_time = ((uint64_t)dtt.tv_sec<<32)+((uint64_t)dtt.tv_nsec<<32)/1000000000;
+
+ return_time = arrival_time - departure_time;
+
+ // uint64_t rtus = (return_time*1000000)>>32; debug(1,"Time ping turnaround time: %lld
+ // us.",rtus);
+
+ // distant_receive_time =
+ // ((uint64_t)ntohl(*((uint32_t*)&packet[16])))<<32+ntohl(*((uint32_t*)&packet[20]));
+
+ distant_receive_time = (uint64_t)ntohl(*((uint32_t *)&packet[16])) << 32;
+ distant_receive_time += ntohl(*((uint32_t *)&packet[20]));
+
+ // distant_transmit_time =
+ // ((uint64_t)ntohl(*((uint32_t*)&packet[24])))<<32+ntohl(*((uint32_t*)&packet[28]));
+
+ distant_transmit_time = (uint64_t)ntohl(*((uint32_t *)&packet[24])) << 32;
+ distant_transmit_time += ntohl(*((uint32_t *)&packet[28]));
+
+ processing_time = distant_transmit_time - distant_receive_time;
+
+ // debug(1,"Return trip time: %lluuS, remote processing time:
+ // %lluuS.",(return_time*1000000)>>32,(processing_time*1000000)>>32);
+
+ uint64_t local_time_by_remote_clock = distant_transmit_time + return_time / 2;
+
+ unsigned int cc;
+ for (cc = time_ping_history - 1; cc > 0; cc--) {
+ time_pings[cc] = time_pings[cc - 1];
+ time_pings[cc].dispersion = (time_pings[cc].dispersion * 133) /
+ 100; // make the dispersions 'age' by this rational factor
+ }
+ time_pings[0].local_to_remote_difference = local_time_by_remote_clock - arrival_time;
+ time_pings[0].dispersion = return_time;
+ if (time_ping_count < time_ping_history)
+ time_ping_count++;
+
+ // now pick the timestamp with the lowest dispersion
+ uint64_t l2rtd = time_pings[0].local_to_remote_difference;
+ uint64_t tld = time_pings[0].dispersion;
+ for (cc = 1; cc < time_ping_count; cc++)
+ if (time_pings[cc].dispersion < tld) {
+ l2rtd = time_pings[cc].local_to_remote_difference;
+ tld = time_pings[cc].dispersion;
}
- // uncomment below to print jitter between client's clock and oour clock
- // int64_t rtus = (tld*1000000)>>32; ji = (ji*1000000)>>32; debug(1,"Choosing time difference with dispersion of %lld us with delta of %lld us",rtus,ji);
+ int64_t ji;
- local_to_remote_time_difference=l2rtd;
+ if (time_ping_count > 1) {
+ if (l2rtd > local_to_remote_time_difference) {
+ local_to_remote_time_jitters =
+ local_to_remote_time_jitters + l2rtd - local_to_remote_time_difference;
+ ji = l2rtd - local_to_remote_time_difference;
} else {
- debug(1, "Timing port -- Unknown RTP packet of type 0x%02X length %d.", packet[1], nread);
+ local_to_remote_time_jitters =
+ local_to_remote_time_jitters + local_to_remote_time_difference - l2rtd;
+ ji = -(local_to_remote_time_difference - l2rtd);
+ }
+ local_to_remote_time_jitters_count += 1;
}
+ // uncomment below to print jitter between client's clock and oour clock
+ // int64_t rtus = (tld*1000000)>>32; ji = (ji*1000000)>>32; debug(1,"Choosing time difference
+ // with dispersion of %lld us with delta of %lld us",rtus,ji);
+
+ local_to_remote_time_difference = l2rtd;
+ } else {
+ debug(1, "Timing port -- Unknown RTP packet of type 0x%02X length %d.", packet[1], nread);
}
+ }
- debug(1, "Timing RTP thread interrupted. terminating.");
- void *retval;
- pthread_kill(timer_requester, SIGUSR1);
- pthread_join(timer_requester, &retval);
- debug(1,"Closed and terminated timer requester thread.");
- debug(1, "Timing RTP thread terminated.");
- close(timing_socket);
+ debug(1, "Timing RTP thread interrupted. terminating.");
+ void *retval;
+ pthread_kill(timer_requester, SIGUSR1);
+ pthread_join(timer_requester, &retval);
+ debug(1, "Closed and terminated timer requester thread.");
+ debug(1, "Timing RTP thread terminated.");
+ close(timing_socket);
- return NULL;
+ return NULL;
}
-static int bind_port(SOCKADDR *remote,int *sock, int desired_port ) {
- struct
- addrinfo hints, *info;
+static int bind_port(SOCKADDR *remote, int *sock, int desired_port) {
+ struct addrinfo hints, *info;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = remote->SAFAMILY;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = remote->SAFAMILY;
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_flags = AI_PASSIVE;
-
- char buffer[10];
- snprintf(buffer, 10, "%d", desired_port);
-
- int ret = getaddrinfo(NULL,buffer, &hints, &info);
+ char buffer[10];
+ snprintf(buffer, 10, "%d", desired_port);
- if (ret < 0)
- die("failed to get usable addrinfo?! %s.", gai_strerror(ret));
+ int ret = getaddrinfo(NULL, buffer, &hints, &info);
- *sock = socket(remote->SAFAMILY, SOCK_DGRAM, IPPROTO_UDP);
- ret = bind(*sock, info->ai_addr, info->ai_addrlen);
+ if (ret < 0)
+ die("failed to get usable addrinfo?! %s.", gai_strerror(ret));
- freeaddrinfo(info);
+ *sock = socket(remote->SAFAMILY, SOCK_DGRAM, IPPROTO_UDP);
+ ret = bind(*sock, info->ai_addr, info->ai_addrlen);
- if (ret < 0)
- die("could not bind a UDP port!");
+ freeaddrinfo(info);
- int sport;
- SOCKADDR local;
- socklen_t local_len = sizeof(local);
- getsockname(*sock, (struct sockaddr*)&local, &local_len);
+ if (ret < 0)
+ die("could not bind a UDP port!");
+
+ int sport;
+ SOCKADDR local;
+ socklen_t local_len = sizeof(local);
+ getsockname(*sock, (struct sockaddr *)&local, &local_len);
#ifdef AF_INET6
- if (local.SAFAMILY == AF_INET6) {
- struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)&local;
- sport = ntohs(sa6->sin6_port);
- } else
+ if (local.SAFAMILY == AF_INET6) {
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&local;
+ sport = ntohs(sa6->sin6_port);
+ } else
#endif
- {
- struct sockaddr_in *sa = (struct sockaddr_in*)&local;
- sport = ntohs(sa->sin_port);
- }
+ {
+ struct sockaddr_in *sa = (struct sockaddr_in *)&local;
+ sport = ntohs(sa->sin_port);
+ }
- return sport;
+ return sport;
}
+void rtp_setup(SOCKADDR *remote, int cport, int tport, uint32_t active_remote, int *lsport,
+ int *lcport, int *ltport) {
+ if (running)
+ die("rtp_setup called with active stream!");
-void rtp_setup(SOCKADDR *remote, int cport, int tport, uint32_t active_remote, int *lsport, int *lcport, int *ltport) {
- if (running)
- die("rtp_setup called with active stream!");
-
- debug(2, "rtp_setup: cport=%d tport=%d.", cport, tport);
-
- client_active_remote = active_remote;
-
- // print out what we know about the client
- void *addr;
- char *ipver;
- int port;
- char portstr[20];
- client_ip_family = remote->SAFAMILY; // keep information about the kind of ip of the client
+ debug(2, "rtp_setup: cport=%d tport=%d.", cport, tport);
+
+ client_active_remote = active_remote;
+
+ // print out what we know about the client
+ void *addr;
+ char *ipver;
+ int port;
+ char portstr[20];
+ client_ip_family = remote->SAFAMILY; // keep information about the kind of ip of the client
#ifdef AF_INET6
- if (remote->SAFAMILY == AF_INET6) {
- struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)remote;
- addr = &(sa6->sin6_addr);
- port = ntohs(sa6->sin6_port);
- ipver = "IPv6";
- }
+ if (remote->SAFAMILY == AF_INET6) {
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)remote;
+ addr = &(sa6->sin6_addr);
+ port = ntohs(sa6->sin6_port);
+ ipver = "IPv6";
+ }
#endif
- if (remote->SAFAMILY == AF_INET) {
- struct sockaddr_in *sa4 = (struct sockaddr_in*)remote;
- addr = &(sa4->sin_addr);
- port = ntohs(sa4->sin_port);
- ipver = "IPv4";
- }
- inet_ntop(remote->SAFAMILY,addr,client_ip_string,sizeof(client_ip_string)); // keep the client's ip number
- debug(1,"Connection from %s: %s:%d",ipver,client_ip_string,port);
-
-
-
- // set up a the record of the remote's control socket
- struct addrinfo hints;
- struct addrinfo *servinfo;
-
- memset(&rtp_client_control_socket,0,sizeof(rtp_client_control_socket));
- memset(&hints,0, sizeof hints);
- hints.ai_family = remote->SAFAMILY;
- hints.ai_socktype = SOCK_DGRAM;
- snprintf(portstr, 20, "%d", cport);
- if (getaddrinfo(client_ip_string,portstr,&hints,&servinfo)!=0)
- die("Can't get address of client's control port");
+ if (remote->SAFAMILY == AF_INET) {
+ struct sockaddr_in *sa4 = (struct sockaddr_in *)remote;
+ addr = &(sa4->sin_addr);
+ port = ntohs(sa4->sin_port);
+ ipver = "IPv4";
+ }
+ inet_ntop(remote->SAFAMILY, addr, client_ip_string,
+ sizeof(client_ip_string)); // keep the client's ip number
+ debug(1, "Connection from %s: %s:%d", ipver, client_ip_string, port);
+
+ // set up a the record of the remote's control socket
+ struct addrinfo hints;
+ struct addrinfo *servinfo;
+
+ memset(&rtp_client_control_socket, 0, sizeof(rtp_client_control_socket));
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = remote->SAFAMILY;
+ hints.ai_socktype = SOCK_DGRAM;
+ snprintf(portstr, 20, "%d", cport);
+ if (getaddrinfo(client_ip_string, portstr, &hints, &servinfo) != 0)
+ die("Can't get address of client's control port");
#ifdef AF_INET6
- if (servinfo->ai_family == AF_INET6)
- memcpy(&rtp_client_control_socket,servinfo->ai_addr,sizeof(struct sockaddr_in6));
- else
+ if (servinfo->ai_family == AF_INET6)
+ memcpy(&rtp_client_control_socket, servinfo->ai_addr, sizeof(struct sockaddr_in6));
+ else
#endif
- memcpy(&rtp_client_control_socket,servinfo->ai_addr,sizeof(struct sockaddr_in));
- freeaddrinfo(servinfo);
-
- // set up a the record of the remote's timing socket
- memset(&rtp_client_timing_socket,0,sizeof(rtp_client_timing_socket));
- memset(&hints,0, sizeof hints);
- hints.ai_family = remote->SAFAMILY;
- hints.ai_socktype = SOCK_DGRAM;
- snprintf(portstr, 20, "%d", tport);
- if (getaddrinfo(client_ip_string,portstr,&hints,&servinfo)!=0)
- die("Can't get address of client's timing port");
+ memcpy(&rtp_client_control_socket, servinfo->ai_addr, sizeof(struct sockaddr_in));
+ freeaddrinfo(servinfo);
+
+ // set up a the record of the remote's timing socket
+ memset(&rtp_client_timing_socket, 0, sizeof(rtp_client_timing_socket));
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = remote->SAFAMILY;
+ hints.ai_socktype = SOCK_DGRAM;
+ snprintf(portstr, 20, "%d", tport);
+ if (getaddrinfo(client_ip_string, portstr, &hints, &servinfo) != 0)
+ die("Can't get address of client's timing port");
#ifdef AF_INET6
- if (servinfo->ai_family == AF_INET6)
- memcpy(&rtp_client_timing_socket,servinfo->ai_addr,sizeof(struct sockaddr_in6));
- else
+ if (servinfo->ai_family == AF_INET6)
+ memcpy(&rtp_client_timing_socket, servinfo->ai_addr, sizeof(struct sockaddr_in6));
+ else
#endif
- memcpy(&rtp_client_timing_socket,servinfo->ai_addr,sizeof(struct sockaddr_in));
- freeaddrinfo(servinfo);
-
- // 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);
-
- debug(2, "listening for audio, control and timing on ports %d, %d, %d.", *lsport, *lcport, *ltport);
-
- please_shutdown = 0;
- reference_timestamp=0;
- pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, NULL);
- pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, NULL);
- pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, NULL);
-
- running = 1;
- request_sent=0;
+ memcpy(&rtp_client_timing_socket, servinfo->ai_addr, sizeof(struct sockaddr_in));
+ freeaddrinfo(servinfo);
+
+ // 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);
+
+ debug(2, "listening for audio, control and timing on ports %d, %d, %d.", *lsport, *lcport,
+ *ltport);
+
+ please_shutdown = 0;
+ reference_timestamp = 0;
+ pthread_create(&rtp_audio_thread, NULL, &rtp_audio_receiver, NULL);
+ pthread_create(&rtp_control_thread, NULL, &rtp_control_receiver, NULL);
+ pthread_create(&rtp_timing_thread, NULL, &rtp_timing_receiver, NULL);
+
+ running = 1;
+ request_sent = 0;
}
-void get_reference_timestamp_stuff(uint32_t *timestamp,uint64_t *timestamp_time) {
+void get_reference_timestamp_stuff(uint32_t *timestamp, uint64_t *timestamp_time) {
pthread_mutex_lock(&reference_time_mutex);
- *timestamp=reference_timestamp;
- *timestamp_time = reference_timestamp_time;
+ *timestamp = reference_timestamp;
+ *timestamp_time = reference_timestamp_time;
pthread_mutex_unlock(&reference_time_mutex);
}
void clear_reference_timestamp(void) {
pthread_mutex_lock(&reference_time_mutex);
- reference_timestamp=0;
- reference_timestamp_time=0;
+ reference_timestamp = 0;
+ reference_timestamp_time = 0;
pthread_mutex_unlock(&reference_time_mutex);
}
void rtp_shutdown(void) {
- if (!running)
- die("rtp_shutdown called without active stream!");
-
- debug(2, "shutting down RTP thread");
- please_shutdown = 1;
- void *retval;
- reference_timestamp=0;
- pthread_kill(rtp_audio_thread, SIGUSR1);
- pthread_join(rtp_audio_thread, &retval);
- pthread_kill(rtp_control_thread, SIGUSR1);
- pthread_join(rtp_control_thread, &retval);
- pthread_kill(rtp_timing_thread, SIGUSR1);
- pthread_join(rtp_timing_thread, &retval);
- running = 0;
+ if (!running)
+ die("rtp_shutdown called without active stream!");
+
+ debug(2, "shutting down RTP thread");
+ please_shutdown = 1;
+ void *retval;
+ reference_timestamp = 0;
+ pthread_kill(rtp_audio_thread, SIGUSR1);
+ pthread_join(rtp_audio_thread, &retval);
+ pthread_kill(rtp_control_thread, SIGUSR1);
+ pthread_join(rtp_control_thread, &retval);
+ pthread_kill(rtp_timing_thread, SIGUSR1);
+ pthread_join(rtp_timing_thread, &retval);
+ running = 0;
}
void rtp_request_resend(seq_t first, uint32_t count) {
- if (running) {
- if (!request_sent) {
- debug(2, "requesting resend on %d packets starting at %u.",count,first);
- request_sent=1;
- }
+ if (running) {
+ if (!request_sent) {
+ debug(2, "requesting resend on %d packets starting at %u.", count, first);
+ request_sent = 1;
+ }
- char req[8]; // *not* a standard RTCP NACK
- req[0] = 0x80;
- req[1] = 0x55|0x80; // Apple 'resend'
- *(unsigned short *)(req+2) = htons(1); // our seqnum
- *(unsigned short *)(req+4) = htons(first); // missed seqnum
- *(unsigned short *)(req+6) = htons(count); // count
- socklen_t msgsize = sizeof(struct sockaddr_in);
+ char req[8]; // *not* a standard RTCP NACK
+ req[0] = 0x80;
+ req[1] = 0x55 | 0x80; // Apple 'resend'
+ *(unsigned short *)(req + 2) = htons(1); // our seqnum
+ *(unsigned short *)(req + 4) = htons(first); // missed seqnum
+ *(unsigned short *)(req + 6) = htons(count); // count
+ socklen_t msgsize = sizeof(struct sockaddr_in);
#ifdef AF_INET6
- if (rtp_client_timing_socket.SAFAMILY==AF_INET6) {
- msgsize = sizeof(struct sockaddr_in6);
- }
+ if (rtp_client_timing_socket.SAFAMILY == AF_INET6) {
+ msgsize = sizeof(struct sockaddr_in6);
+ }
#endif
- if (sendto(audio_socket, req, sizeof(req), 0, (struct sockaddr*)&rtp_client_control_socket,msgsize)==-1) {
- perror("Error sendto-ing to audio socket");
- }
- } else {
- if (!request_sent) {
- debug(2,"rtp_request_resend called without active stream!");
- request_sent=1;
- }
+ if (sendto(audio_socket, req, sizeof(req), 0, (struct sockaddr *)&rtp_client_control_socket,
+ msgsize) == -1) {
+ perror("Error sendto-ing to audio socket");
+ }
+ } else {
+ if (!request_sent) {
+ debug(2, "rtp_request_resend called without active stream!");
+ request_sent = 1;
}
+ }
}
void rtp_request_client_pause() {
if (running) {
- if (client_active_remote==0) {
- debug(1,"Can't request a client pause: no valid active remote.");
- } else {
- // debug(1,"Send a client pause request to %s:3689 with active remote %u.",client_ip_string,client_active_remote);
-
-
- struct addrinfo hints, *res;
- int sockfd;
-
- char message[1000] , server_reply[2000];
-
- // first, load up address structs with getaddrinfo():
-
- memset(&hints, 0, sizeof hints);
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
-
- getaddrinfo(client_ip_string, "3689", &hints, &res);
-
- // make a socket:
-
- sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-
- if (sockfd == -1) {
- die("Could not create socket");
- }
- // debug(1,"Socket created");
-
- // connect!
-
- if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
- die("connect failed. Error");
- }
- // debug(1,"Connect successful");
-
- sprintf(message,"GET /ctrl-int/1/pause HTTP/1.1\r\nHost: %s:3689\r\nActive-Remote: %u\r\n\r\n",client_ip_string,client_active_remote);
- // debug(1,"Sending this message: \"%s\".",message);
-
- //Send some data
- if( send(sockfd , message , strlen(message) , 0) < 0) {
- debug(1,"Send failed");
- }
-
- //Receive a reply from the server
- if( recv(sockfd , server_reply , 2000 , 0) < 0) {
- debug(1,"recv failed");
- }
-
- // debug(1,"Server replied: \"%s\".",server_reply);
-
- if (strstr(server_reply,"HTTP/1.1 204 No Content")!=server_reply)
- debug(1,"Client pause request failed.");
- // debug(1,"Client pause request failed: \"%s\".",server_reply);
- close(sockfd);
+ if (client_active_remote == 0) {
+ debug(1, "Can't request a client pause: no valid active remote.");
+ } else {
+ // debug(1,"Send a client pause request to %s:3689 with active remote
+ // %u.",client_ip_string,client_active_remote);
+
+ struct addrinfo hints, *res;
+ int sockfd;
+
+ char message[1000], server_reply[2000];
+
+ // first, load up address structs with getaddrinfo():
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ getaddrinfo(client_ip_string, "3689", &hints, &res);
+
+ // make a socket:
+
+ sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+
+ if (sockfd == -1) {
+ die("Could not create socket");
+ }
+ // debug(1,"Socket created");
+
+ // connect!
+
+ if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
+ die("connect failed. Error");
+ }
+ // debug(1,"Connect successful");
+
+ sprintf(message,
+ "GET /ctrl-int/1/pause HTTP/1.1\r\nHost: %s:3689\r\nActive-Remote: %u\r\n\r\n",
+ client_ip_string, client_active_remote);
+ // debug(1,"Sending this message: \"%s\".",message);
+
+ // Send some data
+ if (send(sockfd, message, strlen(message), 0) < 0) {
+ debug(1, "Send failed");
+ }
+
+ // Receive a reply from the server
+ if (recv(sockfd, server_reply, 2000, 0) < 0) {
+ debug(1, "recv failed");
+ }
+
+ // debug(1,"Server replied: \"%s\".",server_reply);
+
+ if (strstr(server_reply, "HTTP/1.1 204 No Content") != server_reply)
+ debug(1, "Client pause request failed.");
+ // debug(1,"Client pause request failed: \"%s\".",server_reply);
+ close(sockfd);
}
} else {
- debug(1,"Request to pause non-existent play stream -- ignored.");
+ debug(1, "Request to pause non-existent play stream -- ignored.");
}
}
diff --git a/rtp.h b/rtp.h
index 5b679a6..5fd6798 100644
--- a/rtp.h
+++ b/rtp.h
@@ -5,13 +5,14 @@
#include "player.h"
-void rtp_setup(SOCKADDR *remote, int controlport, int timingport, uint32_t active_remote, int *local_server_port, int *local_control_port, int *local_timing_port);
+void rtp_setup(SOCKADDR *remote, int controlport, int timingport, uint32_t active_remote,
+ int *local_server_port, int *local_control_port, int *local_timing_port);
void rtp_shutdown(void);
void rtp_request_resend(seq_t first, uint32_t count);
void rtp_request_client_pause(void); // ask the client to pause
-void get_reference_timestamp_stuff(uint32_t *timestamp,uint64_t *timestamp_time);
-void clear_reference_timestamp(void);
+void get_reference_timestamp_stuff(uint32_t *timestamp, uint64_t *timestamp_time);
+void clear_reference_timestamp(void);
uint64_t static local_to_remote_time_jitters;
uint64_t static local_to_remote_time_jitters_count;
diff --git a/rtsp.c b/rtsp.c
index 8bb7f15..0f9b122 100644
--- a/rtsp.c
+++ b/rtsp.c
@@ -76,7 +76,6 @@ static pthread_mutex_t play_lock = PTHREAD_MUTEX_INITIALIZER;
// if a reference count is read as zero, it means the it's being deallocated.
static pthread_mutex_t reference_counter_lock = PTHREAD_MUTEX_INITIALIZER;
-
// only one thread is allowed to use the player at once.
// it monitors the request variable (at least when interrupted)
static pthread_mutex_t playing_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -84,11 +83,11 @@ static int please_shutdown = 0;
static pthread_t playing_thread = 0;
typedef struct {
- int fd;
- stream_cfg stream;
- SOCKADDR remote;
- int running;
- pthread_t thread;
+ int fd;
+ stream_cfg stream;
+ SOCKADDR remote;
+ int running;
+ pthread_t thread;
} rtsp_conn_info;
#ifdef CONFIG_METADATA
@@ -96,17 +95,17 @@ typedef struct {
pthread_mutex_t pc_queue_lock;
pthread_cond_t pc_queue_item_added_signal;
pthread_cond_t pc_queue_item_removed_signal;
- size_t item_size; // number of bytes in each item
- uint32_t count; // number of items in the queue
+ size_t item_size; // number of bytes in each item
+ uint32_t count; // number of items in the queue
uint32_t capacity; // maximum number of items
- uint32_t toq; // first item to take
- uint32_t eoq; // free space at end of queue
- void *items; // a pointer to where the items are actually stored
- } pc_queue; // producer-consumer queue
+ uint32_t toq; // first item to take
+ uint32_t eoq; // free space at end of queue
+ void *items; // a pointer to where the items are actually stored
+} pc_queue; // producer-consumer queue
#endif
typedef struct {
- uint32_t referenceCount; // we might start using this...
+ uint32_t referenceCount; // we might start using this...
int nheaders;
char *name[16];
char *value[16];
@@ -127,10 +126,10 @@ typedef struct {
uint32_t code;
char *data;
uint32_t length;
- rtsp_message* carrier;
+ rtsp_message *carrier;
} metadata_package;
-void pc_queue_init(pc_queue* the_queue, char* items, size_t item_size, uint32_t number_of_items) {
+void pc_queue_init(pc_queue *the_queue, char *items, size_t item_size, uint32_t number_of_items) {
the_queue->item_size = item_size;
the_queue->items = items;
the_queue->count = 0;
@@ -139,85 +138,86 @@ void pc_queue_init(pc_queue* the_queue, char* items, size_t item_size, uint32_t
the_queue->eoq = 0;
}
-int send_metadata(uint32_t type,uint32_t code,char *data,uint32_t length,rtsp_message* carrier,int block);
+int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier,
+ int block);
-int send_ssnc_metadata(uint32_t code,char *data,uint32_t length,int block) {
- return send_metadata('ssnc',code,data,length,NULL,block);
+int send_ssnc_metadata(uint32_t code, char *data, uint32_t length, int block) {
+ return send_metadata('ssnc', code, data, length, NULL, block);
}
-int pc_queue_add_item(pc_queue* the_queue,const void* the_stuff, int block) {
+int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) {
int rc;
if (the_queue) {
- if (block==0) {
+ if (block == 0) {
rc = pthread_mutex_trylock(&the_queue->pc_queue_lock);
- if (rc==EBUSY)
+ if (rc == EBUSY)
return EBUSY;
} else
rc = pthread_mutex_lock(&the_queue->pc_queue_lock);
if (rc)
- debug(1,"Error locking for pc_queue_add_item");
- while(the_queue->count==the_queue->capacity) {
- rc = pthread_cond_wait(&the_queue->pc_queue_item_removed_signal,&the_queue->pc_queue_lock);
+ debug(1, "Error locking for pc_queue_add_item");
+ while (the_queue->count == the_queue->capacity) {
+ rc = pthread_cond_wait(&the_queue->pc_queue_item_removed_signal, &the_queue->pc_queue_lock);
if (rc)
- debug(1,"Error waiting for item to be removed");
+ debug(1, "Error waiting for item to be removed");
}
uint32_t i = the_queue->eoq;
- void * p = the_queue->items + the_queue->item_size*i;
-// void * p = &the_queue->qbase + the_queue->item_size*the_queue->eoq;
- memcpy(p,the_stuff,the_queue->item_size);
-
+ void *p = the_queue->items + the_queue->item_size * i;
+ // void * p = &the_queue->qbase + the_queue->item_size*the_queue->eoq;
+ memcpy(p, the_stuff, the_queue->item_size);
+
// update the pointer
i++;
- if (i==the_queue->capacity)
+ if (i == the_queue->capacity)
// fold pointer if necessary
- i=0;
+ i = 0;
the_queue->eoq = i;
the_queue->count++;
- if (the_queue->count==the_queue->capacity)
- debug(1,"pc_queue is full!");
+ if (the_queue->count == the_queue->capacity)
+ debug(1, "pc_queue is full!");
rc = pthread_cond_signal(&the_queue->pc_queue_item_added_signal);
if (rc)
- debug(1,"Error signalling after pc_queue_add_item");
+ debug(1, "Error signalling after pc_queue_add_item");
rc = pthread_mutex_unlock(&the_queue->pc_queue_lock);
if (rc)
- debug(1,"Error unlocking for pc_queue_add_item");
+ debug(1, "Error unlocking for pc_queue_add_item");
} else {
- debug(1,"Adding an item to a NULL queue");
+ debug(1, "Adding an item to a NULL queue");
}
return 0;
}
-int pc_queue_get_item(pc_queue* the_queue,void* the_stuff) {
+int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) {
int rc;
if (the_queue) {
rc = pthread_mutex_lock(&the_queue->pc_queue_lock);
if (rc)
- debug(1,"Error locking for pc_queue_get_item");
- while(the_queue->count==0) {
- rc = pthread_cond_wait(&the_queue->pc_queue_item_added_signal,&the_queue->pc_queue_lock);
+ debug(1, "Error locking for pc_queue_get_item");
+ while (the_queue->count == 0) {
+ rc = pthread_cond_wait(&the_queue->pc_queue_item_added_signal, &the_queue->pc_queue_lock);
if (rc)
- debug(1,"Error waiting for item to be added");
+ debug(1, "Error waiting for item to be added");
}
uint32_t i = the_queue->toq;
-// void * p = &the_queue->qbase + the_queue->item_size*the_queue->toq;
- void * p = the_queue->items + the_queue->item_size*i;
- memcpy(the_stuff,p,the_queue->item_size);
-
+ // void * p = &the_queue->qbase + the_queue->item_size*the_queue->toq;
+ void *p = the_queue->items + the_queue->item_size * i;
+ memcpy(the_stuff, p, the_queue->item_size);
+
// update the pointer
i++;
- if (i==the_queue->capacity)
+ if (i == the_queue->capacity)
// fold pointer if necessary
- i=0;
+ i = 0;
the_queue->toq = i;
the_queue->count--;
rc = pthread_cond_signal(&the_queue->pc_queue_item_removed_signal);
if (rc)
- debug(1,"Error signalling after pc_queue_removed_item");
+ debug(1, "Error signalling after pc_queue_removed_item");
rc = pthread_mutex_unlock(&the_queue->pc_queue_lock);
if (rc)
- debug(1,"Error unlocking for pc_queue_get_item");
+ debug(1, "Error unlocking for pc_queue_get_item");
} else {
- debug(1,"Removing an item from a NULL queue");
+ debug(1, "Removing an item from a NULL queue");
}
return 0;
}
@@ -226,12 +226,12 @@ int pc_queue_get_item(pc_queue* the_queue,void* the_stuff) {
// determine if we are the currently playing thread
static inline int rtsp_playing(void) {
- if (pthread_mutex_trylock(&playing_mutex)) {
- return pthread_equal(playing_thread, pthread_self());
- } else {
- pthread_mutex_unlock(&playing_mutex);
- return 0;
- }
+ if (pthread_mutex_trylock(&playing_mutex)) {
+ return pthread_equal(playing_thread, pthread_self());
+ } else {
+ pthread_mutex_unlock(&playing_mutex);
+ return 0;
+ }
}
void rtsp_request_shutdown_stream(void) {
@@ -239,569 +239,578 @@ void rtsp_request_shutdown_stream(void) {
pthread_kill(playing_thread, SIGUSR1);
}
-
static void rtsp_take_player(void) {
- if (rtsp_playing())
- return;
+ if (rtsp_playing())
+ return;
- if (pthread_mutex_trylock(&playing_mutex)) {
- debug(1, "shutting down playing thread.");
- // XXX minor race condition between please_shutdown and signal delivery
- please_shutdown = 1;
- pthread_kill(playing_thread, SIGUSR1);
- pthread_mutex_lock(&playing_mutex);
- }
- playing_thread = pthread_self(); // make us the currently-playing thread (why?)
+ if (pthread_mutex_trylock(&playing_mutex)) {
+ debug(1, "shutting down playing thread.");
+ // XXX minor race condition between please_shutdown and signal delivery
+ please_shutdown = 1;
+ pthread_kill(playing_thread, SIGUSR1);
+ pthread_mutex_lock(&playing_mutex);
+ }
+ playing_thread = pthread_self(); // make us the currently-playing thread (why?)
}
void rtsp_shutdown_stream(void) {
- rtsp_take_player();
- pthread_mutex_unlock(&playing_mutex);
+ rtsp_take_player();
+ pthread_mutex_unlock(&playing_mutex);
}
// keep track of the threads we have spawned so we can join() them
static rtsp_conn_info **conns = NULL;
static int nconns = 0;
static void track_thread(rtsp_conn_info *conn) {
- conns = realloc(conns, sizeof(rtsp_conn_info*) * (nconns + 1));
- conns[nconns] = conn;
- nconns++;
+ conns = realloc(conns, sizeof(rtsp_conn_info *) * (nconns + 1));
+ conns[nconns] = conn;
+ nconns++;
}
static void cleanup_threads(void) {
- void *retval;
- int i;
- debug(2, "culling threads.");
- for (i=0; i<nconns; ) {
- if (conns[i]->running == 0) {
- pthread_join(conns[i]->thread, &retval);
- free(conns[i]);
- debug(2, "one joined...");
- nconns--;
- if (nconns)
- conns[i] = conns[nconns];
- } else {
- i++;
- }
+ void *retval;
+ int i;
+ debug(2, "culling threads.");
+ for (i = 0; i < nconns;) {
+ if (conns[i]->running == 0) {
+ pthread_join(conns[i]->thread, &retval);
+ free(conns[i]);
+ debug(2, "one joined...");
+ nconns--;
+ if (nconns)
+ conns[i] = conns[nconns];
+ } else {
+ i++;
}
+ }
}
// park a null at the line ending, and return the next line pointer
// accept \r, \n, or \r\n
static char *nextline(char *in, int inbuf) {
- char *out = NULL;
- while (inbuf) {
- if (*in == '\r') {
- *in++ = 0;
- out = in;
- }
- if (*in == '\n') {
- *in++ = 0;
- out = in;
- }
+ char *out = NULL;
+ while (inbuf) {
+ if (*in == '\r') {
+ *in++ = 0;
+ out = in;
+ }
+ if (*in == '\n') {
+ *in++ = 0;
+ out = in;
+ }
- if (out)
- break;
+ if (out)
+ break;
- in++;
- inbuf--;
- }
- return out;
+ in++;
+ inbuf--;
+ }
+ return out;
}
-static void msg_retain(rtsp_message * msg) {
+static void msg_retain(rtsp_message *msg) {
if (msg) {
- int rc = pthread_mutex_lock(&reference_counter_lock);
- if (rc)
- debug(1,"Error %d locking reference counter lock");
- msg->referenceCount++;
- rc = pthread_mutex_unlock(&reference_counter_lock);
- if (rc)
- debug(1,"Error %d unlocking reference counter lock");
+ int rc = pthread_mutex_lock(&reference_counter_lock);
+ if (rc)
+ debug(1, "Error %d locking reference counter lock");
+ msg->referenceCount++;
+ rc = pthread_mutex_unlock(&reference_counter_lock);
+ if (rc)
+ debug(1, "Error %d unlocking reference counter lock");
} else {
- debug(1,"null rtsp_message pointer passed to retain");
+ debug(1, "null rtsp_message pointer passed to retain");
}
}
-static rtsp_message * msg_init(void) {
- rtsp_message *msg = malloc(sizeof(rtsp_message));
- memset(msg, 0, sizeof(rtsp_message));
- msg->referenceCount = 1; // from now on, any access to this must be protected with the lock
- return msg;
+static rtsp_message *msg_init(void) {
+ rtsp_message *msg = malloc(sizeof(rtsp_message));
+ memset(msg, 0, sizeof(rtsp_message));
+ msg->referenceCount = 1; // from now on, any access to this must be protected with the lock
+ return msg;
}
static int msg_add_header(rtsp_message *msg, char *name, char *value) {
- if (msg->nheaders >= sizeof(msg->name)/sizeof(char*)) {
- warn("too many headers?!");
- return 1;
- }
+ if (msg->nheaders >= sizeof(msg->name) / sizeof(char *)) {
+ warn("too many headers?!");
+ return 1;
+ }
- msg->name[msg->nheaders] = strdup(name);
- msg->value[msg->nheaders] = strdup(value);
- msg->nheaders++;
+ msg->name[msg->nheaders] = strdup(name);
+ msg->value[msg->nheaders] = strdup(value);
+ msg->nheaders++;
- return 0;
+ return 0;
}
static char *msg_get_header(rtsp_message *msg, char *name) {
- int i;
- for (i=0; i<msg->nheaders; i++)
- if (!strcasecmp(msg->name[i], name))
- return msg->value[i];
- return NULL;
+ int i;
+ for (i = 0; i < msg->nheaders; i++)
+ if (!strcasecmp(msg->name[i], name))
+ return msg->value[i];
+ return NULL;
}
static void msg_print_debug_headers(rtsp_message *msg) {
int i;
- for (i=0; i<msg->nheaders; i++) {
- debug(1," Type: \"%s\", content: \"%s\"",msg->name[i],msg->value[i]);
+ for (i = 0; i < msg->nheaders; i++) {
+ debug(1, " Type: \"%s\", content: \"%s\"", msg->name[i], msg->value[i]);
}
}
static void msg_free(rtsp_message *msg) {
-
+
if (msg) {
int rc = pthread_mutex_lock(&reference_counter_lock);
if (rc)
- debug(1,"Error %d locking reference counter lock during msg_free()",rc);
+ debug(1, "Error %d locking reference counter lock during msg_free()", rc);
msg->referenceCount--;
rc = pthread_mutex_unlock(&reference_counter_lock);
if (rc)
- debug(1,"Error %d unlocking reference counter lock during msg_free()",rc);
- if (msg->referenceCount==0) {
+ debug(1, "Error %d unlocking reference counter lock during msg_free()", rc);
+ if (msg->referenceCount == 0) {
int i;
- for (i=0; i<msg->nheaders; i++) {
- free(msg->name[i]);
- free(msg->value[i]);
+ for (i = 0; i < msg->nheaders; i++) {
+ free(msg->name[i]);
+ free(msg->value[i]);
}
if (msg->content)
free(msg->content);
free(msg);
} // else {
// debug(1,"rtsp_message reference count non-zero: %d!",msg->referenceCount);
- //}
+ //}
} else {
- debug(1,"null rtsp_message pointer passed to msg_free()");
- }
+ debug(1, "null rtsp_message pointer passed to msg_free()");
+ }
}
-
static int msg_handle_line(rtsp_message **pmsg, char *line) {
- rtsp_message *msg = *pmsg;
+ rtsp_message *msg = *pmsg;
- if (!msg) {
- msg = msg_init();
- *pmsg = msg;
- char *sp, *p;
+ if (!msg) {
+ msg = msg_init();
+ *pmsg = msg;
+ char *sp, *p;
- // debug(1, "received request: %s", line);
+ // debug(1, "received request: %s", line);
- p = strtok_r(line, " ", &sp);
- if (!p)
- goto fail;
- strncpy(msg->method, p, sizeof(msg->method)-1);
+ p = strtok_r(line, " ", &sp);
+ if (!p)
+ goto fail;
+ strncpy(msg->method, p, sizeof(msg->method) - 1);
- p = strtok_r(NULL, " ", &sp);
- if (!p)
- goto fail;
+ p = strtok_r(NULL, " ", &sp);
+ if (!p)
+ goto fail;
- p = strtok_r(NULL, " ", &sp);
- if (!p)
- goto fail;
- if (strcmp(p, "RTSP/1.0"))
- goto fail;
+ p = strtok_r(NULL, " ", &sp);
+ if (!p)
+ goto fail;
+ if (strcmp(p, "RTSP/1.0"))
+ goto fail;
- return -1;
- }
+ return -1;
+ }
- if (strlen(line)) {
- char *p;
- p = strstr(line, ": ");
- if (!p) {
- warn("bad header: >>%s<<", line);
- goto fail;
- }
- *p = 0;
- p += 2;
- msg_add_header(msg, line, p);
- debug(2, " %s: %s.", line, p);
- return -1;
- } else {
- char *cl = msg_get_header(msg, "Content-Length");
- if (cl)
- return atoi(cl);
- else
- return 0;
+ if (strlen(line)) {
+ char *p;
+ p = strstr(line, ": ");
+ if (!p) {
+ warn("bad header: >>%s<<", line);
+ goto fail;
}
+ *p = 0;
+ p += 2;
+ msg_add_header(msg, line, p);
+ debug(2, " %s: %s.", line, p);
+ return -1;
+ } else {
+ char *cl = msg_get_header(msg, "Content-Length");
+ if (cl)
+ return atoi(cl);
+ else
+ return 0;
+ }
fail:
- *pmsg = NULL;
- msg_free(msg);
- return 0;
+ *pmsg = NULL;
+ msg_free(msg);
+ return 0;
}
-static enum rtsp_read_request_response rtsp_read_request(int fd, rtsp_message** the_packet) {
- enum rtsp_read_request_response reply=rtsp_read_request_response_ok;
- ssize_t buflen = 512;
- char *buf = malloc(buflen+1);
-
- rtsp_message *msg = NULL;
-
- ssize_t nread;
- ssize_t inbuf = 0;
- int msg_size = -1;
-
- while (msg_size < 0) {
- if (please_shutdown) {
- debug(1, "RTSP shutdown requested.");
- reply = rtsp_read_request_response_shutdown_requested;
- goto shutdown;
- }
- nread = read(fd, buf+inbuf, buflen - inbuf);
- if (!nread) {
- debug(1, "RTSP connection closed.");
- reply = rtsp_read_request_response_shutdown_requested;
- goto shutdown;
- }
- if (nread < 0) {
- if (errno==EINTR)
- continue;
- perror("read failure");
- reply = rtsp_read_request_response_error;
- goto shutdown;
- }
- inbuf += nread;
+static enum rtsp_read_request_response rtsp_read_request(int fd, rtsp_message **the_packet) {
+ enum rtsp_read_request_response reply = rtsp_read_request_response_ok;
+ ssize_t buflen = 512;
+ char *buf = malloc(buflen + 1);
- char *next;
- while (msg_size < 0 && (next = nextline(buf, inbuf))) {
- msg_size = msg_handle_line(&msg, buf);
+ rtsp_message *msg = NULL;
- if (!msg) {
- warn("no RTSP header received");
- reply = rtsp_read_request_response_bad_packet;
- goto shutdown;
- }
+ ssize_t nread;
+ ssize_t inbuf = 0;
+ int msg_size = -1;
- inbuf -= next-buf;
- if (inbuf)
- memmove(buf, next, inbuf);
- }
+ while (msg_size < 0) {
+ if (please_shutdown) {
+ debug(1, "RTSP shutdown requested.");
+ reply = rtsp_read_request_response_shutdown_requested;
+ goto shutdown;
+ }
+ nread = read(fd, buf + inbuf, buflen - inbuf);
+ if (!nread) {
+ debug(1, "RTSP connection closed.");
+ reply = rtsp_read_request_response_shutdown_requested;
+ goto shutdown;
}
+ if (nread < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("read failure");
+ reply = rtsp_read_request_response_error;
+ goto shutdown;
+ }
+ inbuf += nread;
- if (msg_size > buflen) {
- buf = realloc(buf, msg_size);
- if (!buf) {
- warn("too much content");
- reply = rtsp_read_request_response_error;
- goto shutdown;
- }
- buflen = msg_size;
+ char *next;
+ while (msg_size < 0 && (next = nextline(buf, inbuf))) {
+ msg_size = msg_handle_line(&msg, buf);
+
+ if (!msg) {
+ warn("no RTSP header received");
+ reply = rtsp_read_request_response_bad_packet;
+ goto shutdown;
+ }
+
+ inbuf -= next - buf;
+ if (inbuf)
+ memmove(buf, next, inbuf);
+ }
+ }
+
+ if (msg_size > buflen) {
+ buf = realloc(buf, msg_size);
+ if (!buf) {
+ warn("too much content");
+ reply = rtsp_read_request_response_error;
+ goto shutdown;
}
+ buflen = msg_size;
+ }
+
+ uint64_t threshold_time =
+ get_absolute_time_in_fp() + ((uint64_t)5 << 32); // i.e. five seconds from now
+ int warning_message_sent = 0;
+
+ const size_t max_read_chunk = 50000;
+ while (inbuf < msg_size) {
+
+ // we are going to read the stream in chunks and time how long it takes to do so.
+ // If it's taking too long, (and we find out about it), we will send an error message as
+ // metadata
- uint64_t threshold_time = get_absolute_time_in_fp() + ((uint64_t)5<<32); // i.e. five seconds from now
- int warning_message_sent = 0;
-
- const size_t max_read_chunk = 50000;
- while (inbuf < msg_size) {
-
- // we are going to read the stream in chunks and time how long it takes to do so.
- // If it's taking too long, (and we find out about it), we will send an error message as metadata
-
- if (warning_message_sent==0) {
- uint64_t time_now = get_absolute_time_in_fp();
- if (time_now>threshold_time) { // it's taking too long
- debug(1,"Error receiving metadata from source -- transmission seems to be stalled.");
+ if (warning_message_sent == 0) {
+ uint64_t time_now = get_absolute_time_in_fp();
+ if (time_now > threshold_time) { // it's taking too long
+ debug(1, "Error receiving metadata from source -- transmission seems to be stalled.");
#ifdef CONFIG_METADATA
- send_ssnc_metadata('stal',NULL,0,1);
+ send_ssnc_metadata('stal', NULL, 0, 1);
#endif
- warning_message_sent = 1;
- }
- }
- ssize_t read_chunk = msg_size-inbuf;
- if (read_chunk > max_read_chunk)
- read_chunk = max_read_chunk;
- nread = read(fd, buf+inbuf, read_chunk);
- if (!nread) {
- reply = rtsp_read_request_response_error;
- goto shutdown;
+ warning_message_sent = 1;
}
- if (nread==EINTR)
- continue;
- if (nread < 0) {
- perror("read failure");
- reply = rtsp_read_request_response_error;
- goto shutdown;
- }
- inbuf += nread;
}
+ ssize_t read_chunk = msg_size - inbuf;
+ if (read_chunk > max_read_chunk)
+ read_chunk = max_read_chunk;
+ nread = read(fd, buf + inbuf, read_chunk);
+ if (!nread) {
+ reply = rtsp_read_request_response_error;
+ goto shutdown;
+ }
+ if (nread == EINTR)
+ continue;
+ if (nread < 0) {
+ perror("read failure");
+ reply = rtsp_read_request_response_error;
+ goto shutdown;
+ }
+ inbuf += nread;
+ }
- msg->contentlength = inbuf;
- msg->content = buf;
- *the_packet = msg;
- return reply;
+ msg->contentlength = inbuf;
+ msg->content = buf;
+ *the_packet = msg;
+ return reply;
shutdown:
- if (msg) {
- msg_free(msg); // which will free the content and everything else
- }
- // in case the message wasn't formed or wasn't fully initialised
- if ((msg) && (msg-> content == NULL) || (!msg))
- free(buf);
- *the_packet = NULL;
- return reply;
+ if (msg) {
+ msg_free(msg); // which will free the content and everything else
+ }
+ // in case the message wasn't formed or wasn't fully initialised
+ if ((msg) && (msg->content == NULL) || (!msg))
+ free(buf);
+ *the_packet = NULL;
+ return reply;
}
static void msg_write_response(int fd, rtsp_message *resp) {
- char pkt[1024];
- int pktfree = sizeof(pkt);
- char *p = pkt;
- int i, n;
-
- n = snprintf(p, pktfree,
- "RTSP/1.0 %d %s\r\n", resp->respcode,
- resp->respcode==200 ? "OK" : "Error");
- // debug(1, "sending response: %s", pkt);
+ char pkt[1024];
+ int pktfree = sizeof(pkt);
+ char *p = pkt;
+ int i, n;
+
+ n = snprintf(p, pktfree, "RTSP/1.0 %d %s\r\n", resp->respcode,
+ resp->respcode == 200 ? "OK" : "Error");
+ // debug(1, "sending response: %s", pkt);
+ pktfree -= n;
+ p += n;
+
+ for (i = 0; i < resp->nheaders; i++) {
+ debug(2, " %s: %s.", resp->name[i], resp->value[i]);
+ n = snprintf(p, pktfree, "%s: %s\r\n", resp->name[i], resp->value[i]);
pktfree -= n;
p += n;
+ if (pktfree <= 0)
+ die("Attempted to write overlong RTSP packet");
+ }
- for (i=0; i<resp->nheaders; i++) {
- debug(2, " %s: %s.", resp->name[i], resp->value[i]);
- n = snprintf(p, pktfree, "%s: %s\r\n", resp->name[i], resp->value[i]);
- pktfree -= n;
- p += n;
- if (pktfree <= 0)
- die("Attempted to write overlong RTSP packet");
- }
-
- if (pktfree < 3)
- die("Attempted to write overlong RTSP packet");
+ if (pktfree < 3)
+ die("Attempted to write overlong RTSP packet");
- strcpy(p, "\r\n");
- int ignore = write(fd, pkt, p-pkt+2);
+ strcpy(p, "\r\n");
+ int ignore = write(fd, pkt, p - pkt + 2);
}
-static void handle_record(rtsp_conn_info *conn,
- rtsp_message *req, rtsp_message *resp) {
+static void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
resp->respcode = 200;
- msg_add_header(resp, "Audio-Latency","88200");
+ msg_add_header(resp, "Audio-Latency", "88200");
}
-static void handle_options(rtsp_conn_info *conn,
- rtsp_message *req, rtsp_message *resp) {
- resp->respcode = 200;
- msg_add_header(resp, "Public",
- "ANNOUNCE, SETUP, RECORD, "
- "PAUSE, FLUSH, TEARDOWN, "
- "OPTIONS, GET_PARAMETER, SET_PARAMETER");
+static void handle_options(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
+ resp->respcode = 200;
+ msg_add_header(resp, "Public", "ANNOUNCE, SETUP, RECORD, "
+ "PAUSE, FLUSH, TEARDOWN, "
+ "OPTIONS, GET_PARAMETER, SET_PARAMETER");
}
-static void handle_teardown(rtsp_conn_info *conn,
- rtsp_message *req, rtsp_message *resp) {
- if (!rtsp_playing())
- return;
- resp->respcode = 200;
- msg_add_header(resp, "Connection", "close");
- please_shutdown = 1;
+static void handle_teardown(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
+ if (!rtsp_playing())
+ return;
+ resp->respcode = 200;
+ msg_add_header(resp, "Connection", "close");
+ please_shutdown = 1;
}
-static void handle_flush(rtsp_conn_info *conn,
- rtsp_message *req, rtsp_message *resp) {
- if (!rtsp_playing())
- return;
- char *p;
- uint32_t rtptime=0;
- char * hdr = msg_get_header(req,"RTP-Info");
-
- if (hdr) {
- // debug(1,"FLUSH message received: \"%s\".",hdr);
- // get the rtp timestamp
- p = strstr(hdr, "rtptime=");
- if (p) {
- p = strchr(p, '=') + 1;
- if (p)
- rtptime = uatoi(p); // unsigned integer -- up to 2^32-1
- }
+static void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
+ if (!rtsp_playing())
+ return;
+ char *p;
+ uint32_t rtptime = 0;
+ char *hdr = msg_get_header(req, "RTP-Info");
+
+ if (hdr) {
+ // debug(1,"FLUSH message received: \"%s\".",hdr);
+ // get the rtp timestamp
+ p = strstr(hdr, "rtptime=");
+ if (p) {
+ p = strchr(p, '=') + 1;
+ if (p)
+ rtptime = uatoi(p); // unsigned integer -- up to 2^32-1
}
- // debug(1,"RTSP Flush Requested: %u.",rtptime);
- player_flush(rtptime);
- resp->respcode = 200;
+ }
+ // debug(1,"RTSP Flush Requested: %u.",rtptime);
+ player_flush(rtptime);
+ resp->respcode = 200;
}
-static void handle_setup(rtsp_conn_info *conn,
- rtsp_message *req, rtsp_message *resp) {
- int cport, tport;
- int lsport,lcport,ltport;
- uint32_t active_remote=0;
-
- char * ar = msg_get_header(req,"Active-Remote");
- if (ar) {
- // debug(1,"Active-Remote string seen: \"%s\".",ar);
- // get the active remote
- char *p;
- active_remote = strtoul(ar,&p,10);
- // debug(1,"Active Remote is %u.",active_remote);
- }
-
- // select latency
- // if iTunes V10 or later is detected, use the iTunes latency setting
- // if AirPlay is detected, use the AirPlay latency setting
- // for everything else, use the general latency setting, if given, or
- // else use the default latency setting
-
- config.latency=88200;
-
- if (config.userSuppliedLatency)
- config.latency=config.userSuppliedLatency;
-
- char * ua = msg_get_header(req,"User-Agent");
- if (ua==0) {
- debug(1,"No User-Agent string found in the SETUP message. Using latency of %d frames.",config.latency);
- } else {
- if (strstr(ua,"iTunes")==ua) {
- int iTunesVersion=0;
- // now check it's version 10 or later
- char *pp = strchr(ua,'/') + 1;
- if (pp)
- iTunesVersion=atoi(pp);
- else
- debug(2,"iTunes Version Number not found.");
- if (iTunesVersion>=10) {
- debug(2,"User-Agent is iTunes 10 or better, (actual version is %d); selecting the iTunes latency of %d frames.",iTunesVersion,config.iTunesLatency);
- config.latency=config.iTunesLatency;
- }
- } else if (strstr(ua,"AirPlay")==ua) {
- debug(2,"User-Agent is AirPlay; selecting the AirPlay latency of %d frames.",config.AirPlayLatency);
- config.latency=config.AirPlayLatency;
- } else if (strstr(ua,"forked-daapd")==ua) {
- debug(2,"User-Agent is forked-daapd; selecting the forked-daapd latency of %d frames.",config.ForkedDaapdLatency);
- config.latency=config.ForkedDaapdLatency;
- } else {
- debug(2,"Unrecognised User-Agent. Using latency of %d frames.",config.latency);
- }
- }
- char *hdr = msg_get_header(req, "Transport");
- if (!hdr)
- goto error;
+static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
+ int cport, tport;
+ int lsport, lcport, ltport;
+ uint32_t active_remote = 0;
+ char *ar = msg_get_header(req, "Active-Remote");
+ if (ar) {
+ // debug(1,"Active-Remote string seen: \"%s\".",ar);
+ // get the active remote
char *p;
- p = strstr(hdr, "control_port=");
- if (!p)
- goto error;
- p = strchr(p, '=') + 1;
- cport = atoi(p);
+ active_remote = strtoul(ar, &p, 10);
+ // debug(1,"Active Remote is %u.",active_remote);
+ }
- p = strstr(hdr, "timing_port=");
- if (!p)
- goto error;
- p = strchr(p, '=') + 1;
- tport = atoi(p);
-
- rtsp_take_player();
- rtp_setup(&conn->remote, cport, tport, active_remote, &lsport,&lcport,&ltport);
- if (!lsport)
- goto error;
- char *q;
- p = strstr(hdr,"control_port=");
- if (p) {
- q = strchr(p,';'); // get past the control port entry
- *p++=0;
- if (q++)
- strcat(hdr,q); // should unsplice the control port entry
- }
- p = strstr(hdr,"timing_port=");
- if (p) {
- q = strchr(p,';'); // get past the timing port entry
- *p++=0;
- if (q++)
- strcat(hdr,q); // should unsplice the timing port entry
+ // select latency
+ // if iTunes V10 or later is detected, use the iTunes latency setting
+ // if AirPlay is detected, use the AirPlay latency setting
+ // for everything else, use the general latency setting, if given, or
+ // else use the default latency setting
+
+ config.latency = 88200;
+
+ if (config.userSuppliedLatency)
+ config.latency = config.userSuppliedLatency;
+
+ char *ua = msg_get_header(req, "User-Agent");
+ if (ua == 0) {
+ debug(1, "No User-Agent string found in the SETUP message. Using latency of %d frames.",
+ config.latency);
+ } else {
+ if (strstr(ua, "iTunes") == ua) {
+ int iTunesVersion = 0;
+ // now check it's version 10 or later
+ char *pp = strchr(ua, '/') + 1;
+ if (pp)
+ iTunesVersion = atoi(pp);
+ else
+ debug(2, "iTunes Version Number not found.");
+ if (iTunesVersion >= 10) {
+ debug(2, "User-Agent is iTunes 10 or better, (actual version is %d); selecting the iTunes "
+ "latency of %d frames.",
+ iTunesVersion, config.iTunesLatency);
+ config.latency = config.iTunesLatency;
+ }
+ } else if (strstr(ua, "AirPlay") == ua) {
+ debug(2, "User-Agent is AirPlay; selecting the AirPlay latency of %d frames.",
+ config.AirPlayLatency);
+ config.latency = config.AirPlayLatency;
+ } else if (strstr(ua, "forked-daapd") == ua) {
+ debug(2, "User-Agent is forked-daapd; selecting the forked-daapd latency of %d frames.",
+ config.ForkedDaapdLatency);
+ config.latency = config.ForkedDaapdLatency;
+ } else {
+ debug(2, "Unrecognised User-Agent. Using latency of %d frames.", config.latency);
}
-
- player_play(&conn->stream);
+ }
+ char *hdr = msg_get_header(req, "Transport");
+ if (!hdr)
+ goto error;
+
+ char *p;
+ p = strstr(hdr, "control_port=");
+ if (!p)
+ goto error;
+ p = strchr(p, '=') + 1;
+ cport = atoi(p);
+
+ p = strstr(hdr, "timing_port=");
+ if (!p)
+ goto error;
+ p = strchr(p, '=') + 1;
+ tport = atoi(p);
+
+ rtsp_take_player();
+ rtp_setup(&conn->remote, cport, tport, active_remote, &lsport, &lcport, &ltport);
+ if (!lsport)
+ goto error;
+ char *q;
+ p = strstr(hdr, "control_port=");
+ if (p) {
+ q = strchr(p, ';'); // get past the control port entry
+ *p++ = 0;
+ if (q++)
+ strcat(hdr, q); // should unsplice the control port entry
+ }
+ p = strstr(hdr, "timing_port=");
+ if (p) {
+ q = strchr(p, ';'); // get past the timing port entry
+ *p++ = 0;
+ if (q++)
+ strcat(hdr, q); // should unsplice the timing port entry
+ }
- char *resphdr = alloca(200);
- *resphdr=0;
- sprintf(resphdr, "RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=%d;timing_port=%d;server_port=%d", lcport, ltport, lsport);
+ player_play(&conn->stream);
- msg_add_header(resp, "Transport", resphdr);
+ char *resphdr = alloca(200);
+ *resphdr = 0;
+ sprintf(resphdr, "RTP/AVP/"
+ "UDP;unicast;interleaved=0-1;mode=record;control_port=%d;timing_port=%d;server_"
+ "port=%d",
+ lcport, ltport, lsport);
- msg_add_header(resp, "Session", "1");
+ msg_add_header(resp, "Transport", resphdr);
- resp->respcode = 200;
- return;
+ msg_add_header(resp, "Session", "1");
+
+ resp->respcode = 200;
+ return;
error:
- warn("Error in setup request.");
- pthread_mutex_unlock(&play_lock);
- resp->respcode = 451; // invalid arguments
+ warn("Error in setup request.");
+ pthread_mutex_unlock(&play_lock);
+ resp->respcode = 451; // invalid arguments
}
-static void handle_ignore(rtsp_conn_info *conn,
- rtsp_message *req, rtsp_message *resp) {
- resp->respcode = 200;
+static void handle_ignore(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
+ resp->respcode = 200;
}
-static void handle_set_parameter_parameter(rtsp_conn_info *conn,
- rtsp_message *req, rtsp_message *resp) {
- char *cp = req->content;
- int cp_left = req->contentlength;
- char *next;
- while (cp_left && cp) {
- next = nextline(cp, cp_left);
- cp_left -= next-cp;
-
- if (!strncmp(cp, "volume: ", 8)) {
- if (config.ignore_volume_control==0) {
- float volume = atof(cp + 8);
- debug(2, "volume: %f\n", volume);
- player_volume(volume);
- }
- } else
-#ifdef CONFIG_METADATA
- if(!strncmp(cp, "progress: ", 10)) {
- char *progress = cp + 10;
- debug(2, "progress: \"%s\"\n", progress); // rtpstampstart/rtpstampnow/rtpstampend 44100 per second
- send_ssnc_metadata('prgr',strdup(progress),strlen(progress),1);
- } else
-#endif
- {
- debug(1, "unrecognised parameter: \"%s\" (%d)\n", cp, strlen(cp));
- }
- cp = next;
+static void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *req,
+ rtsp_message *resp) {
+ char *cp = req->content;
+ int cp_left = req->contentlength;
+ char *next;
+ while (cp_left && cp) {
+ next = nextline(cp, cp_left);
+ cp_left -= next - cp;
+
+ if (!strncmp(cp, "volume: ", 8)) {
+ if (config.ignore_volume_control == 0) {
+ float volume = atof(cp + 8);
+ debug(2, "volume: %f\n", volume);
+ player_volume(volume);
+ }
+ } else
+#ifdef CONFIG_METADATA
+ if (!strncmp(cp, "progress: ", 10)) {
+ char *progress = cp + 10;
+ debug(2, "progress: \"%s\"\n",
+ progress); // rtpstampstart/rtpstampnow/rtpstampend 44100 per second
+ send_ssnc_metadata('prgr', strdup(progress), strlen(progress), 1);
+ } else
+#endif
+ {
+ debug(1, "unrecognised parameter: \"%s\" (%d)\n", cp, strlen(cp));
}
+ cp = next;
+ }
}
-
#ifdef CONFIG_METADATA
// Metadata is not used by shairport-sync.
-// Instead we send all metadata to a fifo pipe, so that other apps can listen to the pipe and use the metadata.
+// Instead we send all metadata to a fifo pipe, so that other apps can listen to the pipe and use
+// the metadata.
-// We use two 4-character codes to identify each piece of data and we send the data itself, if any, in base64 form.
+// We use two 4-character codes to identify each piece of data and we send the data itself, if any,
+// in base64 form.
// The first 4-character code, called the "type", is either:
-// 'core' for all the regular metadadata coming from iTunes, etc., or
-// 'ssnc' (for 'shairport-sync') for all metadata coming from Shairport Sync itself, such as start/end delimiters, etc.
+// 'core' for all the regular metadadata coming from iTunes, etc., or
+// 'ssnc' (for 'shairport-sync') for all metadata coming from Shairport Sync itself, such as
+// start/end delimiters, etc.
-// For 'core' metadata, the second 4-character code is the 4-character metadata code coming from iTunes etc.
+// For 'core' metadata, the second 4-character code is the 4-character metadata code coming from
+// iTunes etc.
// For 'ssnc' metadata, the second 4-character code is used to distinguish the messages.
-// Cover art is not tagged in the same way as other metadata, it seems, so is sent as an 'ssnc' type metadata message with the code 'PICT'
+// Cover art is not tagged in the same way as other metadata, it seems, so is sent as an 'ssnc' type
+// metadata message with the code 'PICT'
// Here are the 'ssnc' codes defined so far:
-// 'PICT' -- the payload is a picture, either a JPEG or a PNG. Check the first few bytes to see which.
+// 'PICT' -- the payload is a picture, either a JPEG or a PNG. Check the first few bytes to see
+// which.
// 'pbeg' -- play stream begin. No arguments
// 'pend' -- play stream end. No arguments
// 'pfls' -- play stream flush. No arguments
// 'prsm' -- play stream resume. No arguments
-// 'pvol' -- play volume. The volume is sent as a string -- "airplay_volume,volume,lowest_volume,highest_volume,has_true_mute,is_muted"
+// 'pvol' -- play volume. The volume is sent as a string --
+// "airplay_volume,volume,lowest_volume,highest_volume,has_true_mute,is_muted"
// volume, lowest_volume and highest_volume are given in dB
-// is_muted is 1 if [true] mute is enabled, 0 otherwise.
-// The "airplay_volume" is what's sent to the player, and is from 0.00 down to -30.00, with -144.00 meaning mute.
+// is_muted is 1 if [true] mute is enabled, 0 otherwise.
+// The "airplay_volume" is what's sent to the player, and is from 0.00 down to -30.00,
+// with -144.00 meaning mute.
// This is linear on the volume control slider of iTunes or iOS AirPLay
-// 'prgr' -- progress -- this is metadata from AirPlay consisting of RTP timestamps for the start of the current play sequence, the current play point and the end of the play sequence.
+// 'prgr' -- progress -- this is metadata from AirPlay consisting of RTP timestamps for the start
+// of the current play sequence, the current play point and the end of the play sequence.
// I guess the timestamps wrap at 2^32.
// 'mdst' -- a sequence of metadata is about to start
// 'mden' -- a sequence of metadata has ended
// 'snam' -- the name of the originator -- e.g. "Joe's iPhone" or "iTunes...".
-//
+//
// including a simple base64 encoder to minimise malloc/free activity
// From Stack Overflow, with thanks:
@@ -812,49 +821,45 @@ static void handle_set_parameter_parameter(rtsp_conn_info *conn,
// add _so to end of name to avoid confusion with polarssl's implementation
-static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
- 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
- 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
- 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
- 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
- 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
- 'w', 'x', 'y', 'z', '0', '1', '2', '3',
- '4', '5', '6', '7', '8', '9', '+', '/'};
+static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
static int mod_table[] = {0, 2, 1};
-// pass in a pointer to the data, its length, a pointer to the output buffer and a pointer to an int containing its maximum length
+// pass in a pointer to the data, its length, a pointer to the output buffer and a pointer to an int
+// containing its maximum length
// the actual length will be returned.
-char *base64_encode_so(const unsigned char *data,
- size_t input_length,
- char *encoded_data,
- size_t *output_length) {
-
- size_t calculated_output_length = 4 * ((input_length + 2) / 3);
- if (calculated_output_length> *output_length)
- return(NULL);
- *output_length = calculated_output_length;
-
- int i,j;
- for (i = 0, j = 0; i < input_length;) {
-
- uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
- uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
- uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
-
- uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
-
- encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
- encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
- encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
- encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
- }
+char *base64_encode_so(const unsigned char *data, size_t input_length, char *encoded_data,
+ size_t *output_length) {
+
+ size_t calculated_output_length = 4 * ((input_length + 2) / 3);
+ if (calculated_output_length > *output_length)
+ return (NULL);
+ *output_length = calculated_output_length;
+
+ int i, j;
+ for (i = 0, j = 0; i < input_length;) {
- for (i = 0; i < mod_table[input_length % 3]; i++)
- encoded_data[*output_length - 1 - i] = '=';
+ uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
+ uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
+ uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
+
+ uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
+
+ encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
+ encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
+ encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
+ encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
+ }
- return encoded_data;
+ for (i = 0; i < mod_table[input_length % 3]; i++)
+ encoded_data[*output_length - 1 - i] = '=';
+
+ return encoded_data;
}
// with thanks!
@@ -869,68 +874,67 @@ metadata_package metadata_queue_items[metadata_queue_size];
static pthread_t metadata_thread;
void metadata_create(void) {
- if (config.metadata_enabled==0)
- return;
+ if (config.metadata_enabled == 0)
+ return;
- size_t pl = strlen(config.metadata_pipename) + 1;
+ size_t pl = strlen(config.metadata_pipename) + 1;
- char* path = malloc(pl+1);
- snprintf(path, pl+1, "%s", config.metadata_pipename);
+ char *path = malloc(pl + 1);
+ snprintf(path, pl + 1, "%s", config.metadata_pipename);
- if (mkfifo(path, 0644) && errno != EEXIST)
- die("Could not create metadata FIFO %s", path);
+ if (mkfifo(path, 0644) && errno != EEXIST)
+ die("Could not create metadata FIFO %s", path);
- free(path);
+ free(path);
}
void metadata_open(void) {
- if (config.metadata_enabled==0)
- return;
+ if (config.metadata_enabled == 0)
+ return;
- size_t pl = strlen(config.metadata_pipename) + 1;
+ size_t pl = strlen(config.metadata_pipename) + 1;
- char* path = malloc(pl+1);
- snprintf(path, pl+1, "%s", config.metadata_pipename);
+ char *path = malloc(pl + 1);
+ snprintf(path, pl + 1, "%s", config.metadata_pipename);
- fd = open(path, O_WRONLY | O_NONBLOCK);
- //if (fd < 0)
- // debug(1, "Could not open metadata FIFO %s. Will try again later.", path);
+ fd = open(path, O_WRONLY | O_NONBLOCK);
+ // if (fd < 0)
+ // debug(1, "Could not open metadata FIFO %s. Will try again later.", path);
- free(path);
+ free(path);
}
static void metadata_close(void) {
- close(fd);
- fd = -1;
+ close(fd);
+ fd = -1;
}
ssize_t non_blocking_write(int fd, const void *buf, size_t count) {
// debug(1,"writing %u to pipe...",count);
// we are assuming that the count is always smaller than the FIFO's buffer
struct pollfd ufds[1];
- ssize_t reply;
+ ssize_t reply;
do {
- ufds[0].fd=fd;
+ ufds[0].fd = fd;
ufds[0].events = POLLOUT;
- int rv = poll(ufds,1,5000);
- if (rv==-1)
- debug(1,"error waiting for pipe to unblock...");
- if (rv==0)
- debug(1,"timeout waiting for pipe to unblock");
- reply=write(fd,buf,count);
- if ((reply==-1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
- debug(1,"writing to pipe will block...");
-// else
-// debug(1,"writing %u to pipe done...",reply);
- } while ((reply==-1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)));
+ int rv = poll(ufds, 1, 5000);
+ if (rv == -1)
+ debug(1, "error waiting for pipe to unblock...");
+ if (rv == 0)
+ debug(1, "timeout waiting for pipe to unblock");
+ reply = write(fd, buf, count);
+ if ((reply == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
+ debug(1, "writing to pipe will block...");
+ // else
+ // debug(1,"writing %u to pipe done...",reply);
+ } while ((reply == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)));
return reply;
-
-// return write(fd,buf,count);
+ // return write(fd,buf,count);
}
-void metadata_process(uint32_t type,uint32_t code,char *data,uint32_t length) {
- debug(2,"Process metadata with type %x, code %x and length %u.",type,code,length);
+void metadata_process(uint32_t type, uint32_t code, char *data, uint32_t length) {
+ debug(2, "Process metadata with type %x, code %x and length %u.", type, code, length);
int ret;
// readers may go away and come back
if (fd < 0)
@@ -938,14 +942,15 @@ void metadata_process(uint32_t type,uint32_t code,char *data,uint32_t length) {
if (fd < 0)
return;
char thestring[1024];
- snprintf(thestring,1024,"<type>%x</type><code>%x</code><length>%u</length>\n",type,code,length);
+ snprintf(thestring, 1024, "<type>%x</type><code>%x</code><length>%u</length>\n", type, code,
+ length);
ret = non_blocking_write(fd, thestring, strlen(thestring));
if (ret < 1)
return;
- if ((data!=NULL) && (length>0)) {
- snprintf(thestring,1024,"<data encoding=\"base64\">\n");
+ if ((data != NULL) && (length > 0)) {
+ snprintf(thestring, 1024, "<data encoding=\"base64\">\n");
ret = non_blocking_write(fd, thestring, strlen(thestring));
- if (ret < 1) // no reader
+ if (ret < 1) // no reader
return;
// here, we write the data in base64 form using our nice base64 encoder
// but, we break it into lines of 76 output characters, except for the last one.
@@ -954,37 +959,37 @@ void metadata_process(uint32_t type,uint32_t code,char *data,uint32_t length) {
char *remaining_data = data;
size_t towrite_count;
char outbuf[76];
- while ((remaining_count) && (ret>=0)) {
- size_t towrite_count = remaining_count;
- if (towrite_count>57)
- towrite_count = 57;
- size_t outbuf_size = 76; // size of output buffer on entry, length of result on exit
- if (base64_encode_so(remaining_data, towrite_count, outbuf, &outbuf_size)==NULL)
- debug(1,"Error encoding base64 data.");
- //debug(1,"Remaining count: %d ret: %d, outbuf_size: %d.",remaining_count,ret,outbuf_size);
- ret = non_blocking_write(fd,outbuf,outbuf_size);
- if (ret<0)
- return;
- remaining_data+=towrite_count;
- remaining_count-=towrite_count;
+ while ((remaining_count) && (ret >= 0)) {
+ size_t towrite_count = remaining_count;
+ if (towrite_count > 57)
+ towrite_count = 57;
+ size_t outbuf_size = 76; // size of output buffer on entry, length of result on exit
+ if (base64_encode_so(remaining_data, towrite_count, outbuf, &outbuf_size) == NULL)
+ debug(1, "Error encoding base64 data.");
+ // debug(1,"Remaining count: %d ret: %d, outbuf_size: %d.",remaining_count,ret,outbuf_size);
+ ret = non_blocking_write(fd, outbuf, outbuf_size);
+ if (ret < 0)
+ return;
+ remaining_data += towrite_count;
+ remaining_count -= towrite_count;
// ret = write(fd,"\r\n",2);
// if (ret<0)
- // return;
+ // return;
}
- snprintf(thestring,1024,"</data>\n");
+ snprintf(thestring, 1024, "</data>\n");
ret = non_blocking_write(fd, thestring, strlen(thestring));
- if (ret < 1) // no reader
+ if (ret < 1) // no reader
return;
}
}
-void* metadata_thread_function(void *ignore) {
+void *metadata_thread_function(void *ignore) {
metadata_create();
metadata_package pack;
while (1) {
pc_queue_get_item(&metadata_queue, &pack);
if (config.metadata_enabled)
- metadata_process(pack.type,pack.code,pack.data,pack.length);
+ metadata_process(pack.type, pack.code, pack.data, pack.length);
if (pack.carrier)
msg_free(pack.carrier); // release the message
else if (pack.data)
@@ -995,13 +1000,15 @@ void* metadata_thread_function(void *ignore) {
void metadata_init(void) {
// create a pc_queue for passing information to a threaded metadata handler
- pc_queue_init(&metadata_queue,(char *)&metadata_queue_items,sizeof(metadata_package),metadata_queue_size);
+ pc_queue_init(&metadata_queue, (char *)&metadata_queue_items, sizeof(metadata_package),
+ metadata_queue_size);
int ret = pthread_create(&metadata_thread, NULL, metadata_thread_function, NULL);
if (ret)
- debug(1,"Failed to create metadata thread!");
+ debug(1, "Failed to create metadata thread!");
}
-int send_metadata(uint32_t type,uint32_t code,char *data,uint32_t length, rtsp_message* carrier, int block) {
+int send_metadata(uint32_t type, uint32_t code, char *data, uint32_t length, rtsp_message *carrier,
+ int block) {
metadata_package pack;
pack.type = type;
pack.code = code;
@@ -1010,102 +1017,104 @@ int send_metadata(uint32_t type,uint32_t code,char *data,uint32_t length, rtsp_m
if (carrier)
msg_retain(carrier);
pack.carrier = carrier;
- int rc = pc_queue_add_item(&metadata_queue,&pack,block);
- if ((rc==EBUSY) && (carrier))
+ int rc = pc_queue_add_item(&metadata_queue, &pack, block);
+ if ((rc == EBUSY) && (carrier))
msg_free(carrier);
- if (rc==EBUSY)
- warn("Metadata queue is busy, dropping message of type 0x%08X, code 0x%08X.",type,code);
+ if (rc == EBUSY)
+ warn("Metadata queue is busy, dropping message of type 0x%08X, code 0x%08X.", type, code);
return rc;
}
-static void handle_set_parameter_metadata(rtsp_conn_info *conn,
- rtsp_message *req,
- rtsp_message *resp) {
- char *cp = req->content;
- int cl = req->contentlength;
-
- unsigned int off = 8;
-
- // inform the listener that a set of metadata is starting
- // this doesn't include the cover art though...
-
- // parameters: type, code, pointer to data or NULL, length of data or NULL, the rtsp_message or NULL
- // the rtsp_message is sent for 'core' messages, because it contains the data and must not be
- // freed until the data has been read. So, it is passed to send_metadata to be retained,
- // sent to the thread where metadata is processed and released (and probably freed).
-
- // The reading of the parameters is a bit complex
- // If the rtsp_message field is non-null, then it represents an rtsp_message which should be freed in the thread handler when the parameter pointed to by the pointer and specified by the length is finished with
- // If the rtsp_message is NULL, then if the pointer is non-null, it points to a malloc'ed block and should be freed when the thread is finished with it. The length of the data in the block is given in length
- // If the rtsp_message is NULL and the pointer is also NULL, nothing further is done.
-
- send_metadata('ssnc','mdst',NULL,0,NULL,1);
-
- while (off < cl) {
- // pick up the metadata tag as an unsigned longint
- uint32_t itag = ntohl(*(uint32_t *)(cp+off));
- off += sizeof(uint32_t);
-
- // pick up the length of the data
- uint32_t vl = ntohl(*(uint32_t *)(cp+off));
- off += sizeof(uint32_t);
-
- // pass the data over
- if (vl==0)
- send_metadata('core',itag,NULL,0,NULL,1);
- else
- send_metadata('core',itag,(char *)(cp+off),vl,req,1);
-
- // move on to the next item
- off += vl;
- }
-
- // inform the listener that a set of metadata is ending
- send_metadata('ssnc','mden',NULL,0,NULL,1);
+static void handle_set_parameter_metadata(rtsp_conn_info *conn, rtsp_message *req,
+ rtsp_message *resp) {
+ char *cp = req->content;
+ int cl = req->contentlength;
+
+ unsigned int off = 8;
+
+ // inform the listener that a set of metadata is starting
+ // this doesn't include the cover art though...
+
+ // parameters: type, code, pointer to data or NULL, length of data or NULL, the rtsp_message or
+ // NULL
+ // the rtsp_message is sent for 'core' messages, because it contains the data and must not be
+ // freed until the data has been read. So, it is passed to send_metadata to be retained,
+ // sent to the thread where metadata is processed and released (and probably freed).
+
+ // The reading of the parameters is a bit complex
+ // If the rtsp_message field is non-null, then it represents an rtsp_message which should be freed
+ // in the thread handler when the parameter pointed to by the pointer and specified by the length
+ // is finished with
+ // If the rtsp_message is NULL, then if the pointer is non-null, it points to a malloc'ed block
+ // and should be freed when the thread is finished with it. The length of the data in the block is
+ // given in length
+ // If the rtsp_message is NULL and the pointer is also NULL, nothing further is done.
+
+ send_metadata('ssnc', 'mdst', NULL, 0, NULL, 1);
+
+ while (off < cl) {
+ // pick up the metadata tag as an unsigned longint
+ uint32_t itag = ntohl(*(uint32_t *)(cp + off));
+ off += sizeof(uint32_t);
+
+ // pick up the length of the data
+ uint32_t vl = ntohl(*(uint32_t *)(cp + off));
+ off += sizeof(uint32_t);
+
+ // pass the data over
+ if (vl == 0)
+ send_metadata('core', itag, NULL, 0, NULL, 1);
+ else
+ send_metadata('core', itag, (char *)(cp + off), vl, req, 1);
+
+ // move on to the next item
+ off += vl;
+ }
+
+ // inform the listener that a set of metadata is ending
+ send_metadata('ssnc', 'mden', NULL, 0, NULL, 1);
// send the user some shairport-originated metadata
// send the name of the player, e.g. "Joe's iPhone" or "iTunes"
- send_metadata('ssnc','sndr',strdup(sender_name),strlen(sender_name),NULL,1);
+ send_metadata('ssnc', 'sndr', strdup(sender_name), strlen(sender_name), NULL, 1);
}
#endif
-static void handle_set_parameter(rtsp_conn_info *conn,
- rtsp_message *req, rtsp_message *resp) {
- //if (!req->contentlength)
- // debug(1, "received empty SET_PARAMETER request.");
+static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
+ // if (!req->contentlength)
+ // debug(1, "received empty SET_PARAMETER request.");
- char *ct = msg_get_header(req, "Content-Type");
+ char *ct = msg_get_header(req, "Content-Type");
- if (ct) {
- debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct);
+ if (ct) {
+ debug(2, "SET_PARAMETER Content-Type:\"%s\".", ct);
#ifdef CONFIG_METADATA
- if (!strncmp(ct, "application/x-dmap-tagged", 25)) {
- debug(2, "received metadata tags in SET_PARAMETER request.");
- handle_set_parameter_metadata(conn, req, resp);
- } else if (!strncmp(ct, "image", 5)) {
- // debug(1, "received image in SET_PARAMETER request.");
- // note: the image/type tag isn't reliable, so it's not being sent
- // -- best look at the first few bytes of the image
- send_metadata('ssnc','PICT',req->content,req->contentlength,req,1);
- } else
-#endif
- if (!strncmp(ct, "text/parameters", 15)) {
- debug(2, "received parameters in SET_PARAMETER request.");
- handle_set_parameter_parameter(conn, req, resp);
- } else {
- debug(1, "received unknown Content-Type \"%s\" in SET_PARAMETER request.", ct);
- }
+ if (!strncmp(ct, "application/x-dmap-tagged", 25)) {
+ debug(2, "received metadata tags in SET_PARAMETER request.");
+ handle_set_parameter_metadata(conn, req, resp);
+ } else if (!strncmp(ct, "image", 5)) {
+ // debug(1, "received image in SET_PARAMETER request.");
+ // note: the image/type tag isn't reliable, so it's not being sent
+ // -- best look at the first few bytes of the image
+ send_metadata('ssnc', 'PICT', req->content, req->contentlength, req, 1);
+ } else
+#endif
+ if (!strncmp(ct, "text/parameters", 15)) {
+ debug(2, "received parameters in SET_PARAMETER request.");
+ handle_set_parameter_parameter(conn, req, resp);
} else {
- debug(1, "missing Content-Type header in SET_PARAMETER request.");
+ debug(1, "received unknown Content-Type \"%s\" in SET_PARAMETER request.", ct);
}
+ } else {
+ debug(1, "missing Content-Type header in SET_PARAMETER request.");
+ }
- resp->respcode = 200;
+ resp->respcode = 200;
}
-static void handle_announce(rtsp_conn_info *conn,
- rtsp_message *req, rtsp_message *resp) {
+static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
// interrupt session if permitted
- if ((config.allow_session_interruption==1) || (pthread_mutex_trylock(&play_lock) == 0)) {
+ if ((config.allow_session_interruption == 1) || (pthread_mutex_trylock(&play_lock) == 0)) {
char *paesiv = NULL;
char *prsaaeskey = NULL;
char *pfmtp = NULL;
@@ -1114,16 +1123,16 @@ static void handle_announce(rtsp_conn_info *conn,
char *next;
while (cp_left && cp) {
next = nextline(cp, cp_left);
- cp_left -= next-cp;
+ cp_left -= next - cp;
if (!strncmp(cp, "a=fmtp:", 7))
- pfmtp = cp+7;
+ pfmtp = cp + 7;
if (!strncmp(cp, "a=aesiv:", 8))
- paesiv = cp+8;
+ paesiv = cp + 8;
if (!strncmp(cp, "a=rsaaeskey:", 12))
- prsaaeskey = cp+12;
+ prsaaeskey = cp + 12;
cp = next;
}
@@ -1155,25 +1164,25 @@ static void handle_announce(rtsp_conn_info *conn,
free(aeskey);
int i;
- for (i=0; i<sizeof(conn->stream.fmtp)/sizeof(conn->stream.fmtp[0]); i++)
+ for (i = 0; i < sizeof(conn->stream.fmtp) / sizeof(conn->stream.fmtp[0]); i++)
conn->stream.fmtp[i] = atoi(strsep(&pfmtp, " \t"));
-
+
char *hdr = msg_get_header(req, "X-Apple-Client-Name");
if (hdr) {
- strncpy(sender_name,hdr,1024);
- debug(1,"Play connection from \"%s\".",hdr);
+ strncpy(sender_name, hdr, 1024);
+ debug(1, "Play connection from \"%s\".", hdr);
} else {
hdr = msg_get_header(req, "User-Agent");
if (hdr) {
- debug(1,"Play connection from \"%s\".",hdr);
- strncpy(sender_name,hdr,1024);
- } else
- sender_name[0]=0;
- }
+ debug(1, "Play connection from \"%s\".", hdr);
+ strncpy(sender_name, hdr, 1024);
+ } else
+ sender_name[0] = 0;
+ }
resp->respcode = 200;
} else {
resp->respcode = 453;
- debug(1,"Already playing.");
+ debug(1, "Already playing.");
}
out:
@@ -1182,422 +1191,416 @@ out:
}
}
-
static struct method_handler {
- char *method;
- void (*handler)(rtsp_conn_info *conn, rtsp_message *req,
- rtsp_message *resp);
-} method_handlers[] = {
- {"OPTIONS", handle_options},
- {"ANNOUNCE", handle_announce},
- {"FLUSH", handle_flush},
- {"TEARDOWN", handle_teardown},
- {"SETUP", handle_setup},
- {"GET_PARAMETER", handle_ignore},
- {"SET_PARAMETER", handle_set_parameter},
- {"RECORD", handle_record},
- {NULL, NULL}
-};
+ char *method;
+ void (*handler)(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp);
+} method_handlers[] = {{"OPTIONS", handle_options},
+ {"ANNOUNCE", handle_announce},
+ {"FLUSH", handle_flush},
+ {"TEARDOWN", handle_teardown},
+ {"SETUP", handle_setup},
+ {"GET_PARAMETER", handle_ignore},
+ {"SET_PARAMETER", handle_set_parameter},
+ {"RECORD", handle_record},
+ {NULL, NULL}};
static void apple_challenge(int fd, rtsp_message *req, rtsp_message *resp) {
- char *hdr = msg_get_header(req, "Apple-Challenge");
- if (!hdr)
- return;
+ char *hdr = msg_get_header(req, "Apple-Challenge");
+ if (!hdr)
+ return;
- SOCKADDR fdsa;
- socklen_t sa_len = sizeof(fdsa);
- getsockname(fd, (struct sockaddr*)&fdsa, &sa_len);
+ SOCKADDR fdsa;
+ socklen_t sa_len = sizeof(fdsa);
+ getsockname(fd, (struct sockaddr *)&fdsa, &sa_len);
- int chall_len;
- uint8_t *chall = base64_dec(hdr, &chall_len);
- uint8_t buf[48], *bp = buf;
- int i;
- memset(buf, 0, sizeof(buf));
+ int chall_len;
+ uint8_t *chall = base64_dec(hdr, &chall_len);
+ uint8_t buf[48], *bp = buf;
+ int i;
+ memset(buf, 0, sizeof(buf));
- if (chall_len > 16) {
- warn("oversized Apple-Challenge!");
- free(chall);
- return;
- }
- memcpy(bp, chall, chall_len);
+ if (chall_len > 16) {
+ warn("oversized Apple-Challenge!");
free(chall);
- bp += chall_len;
+ return;
+ }
+ memcpy(bp, chall, chall_len);
+ free(chall);
+ bp += chall_len;
#ifdef AF_INET6
- if (fdsa.SAFAMILY == AF_INET6) {
- struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)(&fdsa);
- memcpy(bp, sa6->sin6_addr.s6_addr, 16);
- bp += 16;
- } else
+ if (fdsa.SAFAMILY == AF_INET6) {
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)(&fdsa);
+ memcpy(bp, sa6->sin6_addr.s6_addr, 16);
+ bp += 16;
+ } else
#endif
- {
- struct sockaddr_in *sa = (struct sockaddr_in*)(&fdsa);
- memcpy(bp, &sa->sin_addr.s_addr, 4);
- bp += 4;
- }
+ {
+ struct sockaddr_in *sa = (struct sockaddr_in *)(&fdsa);
+ memcpy(bp, &sa->sin_addr.s_addr, 4);
+ bp += 4;
+ }
- for (i=0; i<6; i++)
- *bp++ = config.hw_addr[i];
+ for (i = 0; i < 6; i++)
+ *bp++ = config.hw_addr[i];
- int buflen, resplen;
- buflen = bp-buf;
- if (buflen < 0x20)
- buflen = 0x20;
+ int buflen, resplen;
+ buflen = bp - buf;
+ if (buflen < 0x20)
+ buflen = 0x20;
- uint8_t *challresp = rsa_apply(buf, buflen, &resplen, RSA_MODE_AUTH);
- char *encoded = base64_enc(challresp, resplen);
+ uint8_t *challresp = rsa_apply(buf, buflen, &resplen, RSA_MODE_AUTH);
+ char *encoded = base64_enc(challresp, resplen);
- // strip the padding.
- char *padding = strchr(encoded, '=');
- if (padding)
- *padding = 0;
+ // strip the padding.
+ char *padding = strchr(encoded, '=');
+ if (padding)
+ *padding = 0;
- msg_add_header(resp, "Apple-Response", encoded);
- free(challresp);
- free(encoded);
+ msg_add_header(resp, "Apple-Response", encoded);
+ free(challresp);
+ free(encoded);
}
static char *make_nonce(void) {
- uint8_t random[8];
- int fd = open("/dev/random", O_RDONLY);
- if (fd < 0)
- die("could not open /dev/random!");
- int ignore = read(fd, random, sizeof(random));
- close(fd);
- return base64_enc(random, 8);
+ uint8_t random[8];
+ int fd = open("/dev/random", O_RDONLY);
+ if (fd < 0)
+ die("could not open /dev/random!");
+ int ignore = read(fd, random, sizeof(random));
+ close(fd);
+ return base64_enc(random, 8);
}
static int rtsp_auth(char **nonce, rtsp_message *req, rtsp_message *resp) {
- if (!config.password)
- return 0;
- if (!*nonce) {
- *nonce = make_nonce();
- goto authenticate;
- }
+ if (!config.password)
+ return 0;
+ if (!*nonce) {
+ *nonce = make_nonce();
+ goto authenticate;
+ }
+
+ char *hdr = msg_get_header(req, "Authorization");
+ if (!hdr || strncmp(hdr, "Digest ", 7))
+ goto authenticate;
+
+ char *realm = strstr(hdr, "realm=\"");
+ char *username = strstr(hdr, "username=\"");
+ char *response = strstr(hdr, "response=\"");
+ char *uri = strstr(hdr, "uri=\"");
+
+ if (!realm || !username || !response || !uri)
+ goto authenticate;
+
+ char *quote;
+ realm = strchr(realm, '"') + 1;
+ if (!(quote = strchr(realm, '"')))
+ goto authenticate;
+ *quote = 0;
+ username = strchr(username, '"') + 1;
+ if (!(quote = strchr(username, '"')))
+ goto authenticate;
+ *quote = 0;
+ response = strchr(response, '"') + 1;
+ if (!(quote = strchr(response, '"')))
+ goto authenticate;
+ *quote = 0;
+ uri = strchr(uri, '"') + 1;
+ if (!(quote = strchr(uri, '"')))
+ goto authenticate;
+ *quote = 0;
+
+ uint8_t digest_urp[16], digest_mu[16], digest_total[16];
- char *hdr = msg_get_header(req, "Authorization");
- if (!hdr || strncmp(hdr, "Digest ", 7))
- goto authenticate;
-
- char *realm = strstr(hdr, "realm=\"");
- char *username = strstr(hdr, "username=\"");
- char *response = strstr(hdr, "response=\"");
- char *uri = strstr(hdr, "uri=\"");
-
- if (!realm || !username || !response || !uri)
- goto authenticate;
-
- char *quote;
- realm = strchr(realm, '"') + 1;
- if (!(quote = strchr(realm, '"')))
- goto authenticate;
- *quote = 0;
- username = strchr(username, '"') + 1;
- if (!(quote = strchr(username, '"')))
- goto authenticate;
- *quote = 0;
- response = strchr(response, '"') + 1;
- if (!(quote = strchr(response, '"')))
- goto authenticate;
- *quote = 0;
- uri = strchr(uri, '"') + 1;
- if (!(quote = strchr(uri, '"')))
- goto authenticate;
- *quote = 0;
-
- uint8_t digest_urp[16], digest_mu[16], digest_total[16];
-
#ifdef HAVE_LIBSSL
- MD5_CTX ctx;
-
- MD5_Init(&ctx);
- MD5_Update(&ctx, username, strlen(username));
- MD5_Update(&ctx, ":", 1);
- MD5_Update(&ctx, realm, strlen(realm));
- MD5_Update(&ctx, ":", 1);
- MD5_Update(&ctx, config.password, strlen(config.password));
- MD5_Final(digest_urp, &ctx);
- MD5_Init(&ctx);
- MD5_Update(&ctx, req->method, strlen(req->method));
- MD5_Update(&ctx, ":", 1);
- MD5_Update(&ctx, uri, strlen(uri));
- MD5_Final(digest_mu, &ctx);
+ MD5_CTX ctx;
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, username, strlen(username));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, realm, strlen(realm));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, config.password, strlen(config.password));
+ MD5_Final(digest_urp, &ctx);
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, req->method, strlen(req->method));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, uri, strlen(uri));
+ MD5_Final(digest_mu, &ctx);
#endif
-
#ifdef HAVE_LIBPOLARSSL
- md5_context tctx;
- md5_starts(&tctx);
- md5_update(&tctx, (const unsigned char *)username, strlen(username));
- md5_update(&tctx, (unsigned char *) ":", 1);
- md5_update(&tctx, (const unsigned char *)realm, strlen(realm));
- md5_update(&tctx, (unsigned char *) ":", 1);
- md5_update(&tctx, (const unsigned char *)config.password, strlen(config.password));
- md5_finish(&tctx,digest_urp);
- md5_starts(&tctx);
- md5_update(&tctx, (const unsigned char *)req->method, strlen(req->method));
- md5_update(&tctx, (unsigned char *) ":", 1);
- md5_update(&tctx, (const unsigned char *)uri, strlen(uri));
- md5_finish(&tctx,digest_mu);
+ md5_context tctx;
+ md5_starts(&tctx);
+ md5_update(&tctx, (const unsigned char *)username, strlen(username));
+ md5_update(&tctx, (unsigned char *)":", 1);
+ md5_update(&tctx, (const unsigned char *)realm, strlen(realm));
+ md5_update(&tctx, (unsigned char *)":", 1);
+ md5_update(&tctx, (const unsigned char *)config.password, strlen(config.password));
+ md5_finish(&tctx, digest_urp);
+ md5_starts(&tctx);
+ md5_update(&tctx, (const unsigned char *)req->method, strlen(req->method));
+ md5_update(&tctx, (unsigned char *)":", 1);
+ md5_update(&tctx, (const unsigned char *)uri, strlen(uri));
+ md5_finish(&tctx, digest_mu);
#endif
-
- int i;
- unsigned char buf[33];
- for (i=0; i<16; i++)
- sprintf((char *)buf + 2*i, "%02X", digest_urp[i]);
-
+
+ int i;
+ unsigned char buf[33];
+ for (i = 0; i < 16; i++)
+ sprintf((char *)buf + 2 * i, "%02X", digest_urp[i]);
+
#ifdef HAVE_LIBSSL
- MD5_Init(&ctx);
- MD5_Update(&ctx, buf, 32);
- MD5_Update(&ctx, ":", 1);
- MD5_Update(&ctx, *nonce, strlen(*nonce));
- MD5_Update(&ctx, ":", 1);
- for (i=0; i<16; i++)
- sprintf(buf + 2*i, "%02X", digest_mu[i]);
- MD5_Update(&ctx, buf, 32);
- MD5_Final(digest_total, &ctx);
- #endif
-
-
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, buf, 32);
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *nonce, strlen(*nonce));
+ MD5_Update(&ctx, ":", 1);
+ for (i = 0; i < 16; i++)
+ sprintf(buf + 2 * i, "%02X", digest_mu[i]);
+ MD5_Update(&ctx, buf, 32);
+ MD5_Final(digest_total, &ctx);
+#endif
+
#ifdef HAVE_LIBPOLARSSL
- md5_starts(&tctx);
- md5_update(&tctx, buf, 32);
- md5_update(&tctx, (unsigned char *) ":", 1);
- md5_update(&tctx, (const unsigned char *)*nonce, strlen(*nonce));
- md5_update(&tctx, (unsigned char *) ":", 1);
- for (i=0; i<16; i++)
- sprintf((char *)buf + 2*i,"%02X", digest_mu[i]);
- md5_update(&tctx, buf, 32);
- md5_finish(&tctx,digest_total);
+ md5_starts(&tctx);
+ md5_update(&tctx, buf, 32);
+ md5_update(&tctx, (unsigned char *)":", 1);
+ md5_update(&tctx, (const unsigned char *)*nonce, strlen(*nonce));
+ md5_update(&tctx, (unsigned char *)":", 1);
+ for (i = 0; i < 16; i++)
+ sprintf((char *)buf + 2 * i, "%02X", digest_mu[i]);
+ md5_update(&tctx, buf, 32);
+ md5_finish(&tctx, digest_total);
#endif
- for (i=0; i<16; i++)
- sprintf((char *)buf + 2*i,"%02X", digest_total[i]);
+ for (i = 0; i < 16; i++)
+ sprintf((char *)buf + 2 * i, "%02X", digest_total[i]);
- if (!strcmp(response, (const char *)buf))
- return 0;
- warn("auth failed");
+ if (!strcmp(response, (const char *)buf))
+ return 0;
+ warn("auth failed");
authenticate:
- resp->respcode = 401;
- int hdrlen = strlen(*nonce) + 40;
- char *authhdr = malloc(hdrlen);
- snprintf(authhdr, hdrlen, "Digest realm=\"taco\", nonce=\"%s\"", *nonce);
- msg_add_header(resp, "WWW-Authenticate", authhdr);
- free(authhdr);
- return 1;
+ resp->respcode = 401;
+ int hdrlen = strlen(*nonce) + 40;
+ char *authhdr = malloc(hdrlen);
+ snprintf(authhdr, hdrlen, "Digest realm=\"taco\", nonce=\"%s\"", *nonce);
+ msg_add_header(resp, "WWW-Authenticate", authhdr);
+ free(authhdr);
+ return 1;
}
static void *rtsp_conversation_thread_func(void *pconn) {
- // SIGUSR1 is used to interrupt this thread if blocked for read
- sigset_t set;
- sigemptyset(&set);
- sigaddset(&set, SIGUSR1);
- pthread_sigmask(SIG_UNBLOCK, &set, NULL);
-
- rtsp_conn_info *conn = pconn;
-
- rtsp_message *req, *resp;
- char *hdr, *auth_nonce = NULL;
-
- enum rtsp_read_request_response reply;
-
- do {
- reply=rtsp_read_request(conn->fd,&req);
- if (reply==rtsp_read_request_response_ok) {
- resp = msg_init();
- resp->respcode = 400;
-
- apple_challenge(conn->fd, req, resp);
- hdr = msg_get_header(req, "CSeq");
- if (hdr)
- msg_add_header(resp, "CSeq", hdr);
- msg_add_header(resp, "Audio-Jack-Status", "connected; type=analog");
-
- if (rtsp_auth(&auth_nonce, req, resp))
- goto respond;
-
- struct method_handler *mh;
- for (mh=method_handlers; mh->method; mh++) {
- if (!strcmp(mh->method, req->method)) {
- // debug(1,"RTSP Packet received of type \"%s\":",mh->method),
- // msg_print_debug_headers(req);
- mh->handler(conn, req, resp);
- // debug(1,"RTSP Response:");
- // msg_print_debug_headers(resp);
- break;
- }
- }
+ // SIGUSR1 is used to interrupt this thread if blocked for read
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, SIGUSR1);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+
+ rtsp_conn_info *conn = pconn;
-respond:
- msg_write_response(conn->fd, resp);
- msg_free(req);
- msg_free(resp);
- } else {
- if (reply!=rtsp_read_request_response_shutdown_requested)
- debug(1,"rtsp_read_request error %d, packet ignored.",(int)reply);
+ rtsp_message *req, *resp;
+ char *hdr, *auth_nonce = NULL;
+
+ enum rtsp_read_request_response reply;
+
+ do {
+ reply = rtsp_read_request(conn->fd, &req);
+ if (reply == rtsp_read_request_response_ok) {
+ resp = msg_init();
+ resp->respcode = 400;
+
+ apple_challenge(conn->fd, req, resp);
+ hdr = msg_get_header(req, "CSeq");
+ if (hdr)
+ msg_add_header(resp, "CSeq", hdr);
+ msg_add_header(resp, "Audio-Jack-Status", "connected; type=analog");
+
+ if (rtsp_auth(&auth_nonce, req, resp))
+ goto respond;
+
+ struct method_handler *mh;
+ for (mh = method_handlers; mh->method; mh++) {
+ if (!strcmp(mh->method, req->method)) {
+ // debug(1,"RTSP Packet received of type \"%s\":",mh->method),
+ // msg_print_debug_headers(req);
+ mh->handler(conn, req, resp);
+ // debug(1,"RTSP Response:");
+ // msg_print_debug_headers(resp);
+ break;
+ }
}
- } while (reply!=rtsp_read_request_response_shutdown_requested);
-
- debug(1, "closing RTSP connection.");
- if (conn->fd > 0)
- close(conn->fd);
- if (rtsp_playing()) {
- rtp_shutdown();
- player_stop();
- pthread_mutex_unlock(&play_lock);
- please_shutdown = 0;
- pthread_mutex_unlock(&playing_mutex);
+
+ respond:
+ msg_write_response(conn->fd, resp);
+ msg_free(req);
+ msg_free(resp);
+ } else {
+ if (reply != rtsp_read_request_response_shutdown_requested)
+ debug(1, "rtsp_read_request error %d, packet ignored.", (int)reply);
}
- if (auth_nonce)
- free(auth_nonce);
- conn->running = 0;
- debug(2, "terminating RTSP thread.");
- return NULL;
+ } while (reply != rtsp_read_request_response_shutdown_requested);
+
+ debug(1, "closing RTSP connection.");
+ if (conn->fd > 0)
+ close(conn->fd);
+ if (rtsp_playing()) {
+ rtp_shutdown();
+ player_stop();
+ pthread_mutex_unlock(&play_lock);
+ please_shutdown = 0;
+ pthread_mutex_unlock(&playing_mutex);
+ }
+ if (auth_nonce)
+ free(auth_nonce);
+ conn->running = 0;
+ debug(2, "terminating RTSP thread.");
+ return NULL;
}
// this function is not thread safe.
-static const char* format_address(struct sockaddr *fsa) {
- static char string[INETx_ADDRSTRLEN];
- void *addr;
+static const char *format_address(struct sockaddr *fsa) {
+ static char string[INETx_ADDRSTRLEN];
+ void *addr;
#ifdef AF_INET6
- if (fsa->sa_family == AF_INET6) {
- struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)(fsa);
- addr = &(sa6->sin6_addr);
- } else
+ if (fsa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)(fsa);
+ addr = &(sa6->sin6_addr);
+ } else
#endif
- {
- struct sockaddr_in *sa = (struct sockaddr_in*)(fsa);
- addr = &(sa->sin_addr);
- }
- return inet_ntop(fsa->sa_family, addr, string, sizeof(string));
+ {
+ struct sockaddr_in *sa = (struct sockaddr_in *)(fsa);
+ addr = &(sa->sin_addr);
+ }
+ return inet_ntop(fsa->sa_family, addr, string, sizeof(string));
}
void rtsp_listen_loop(void) {
- struct addrinfo hints, *info, *p;
- char portstr[6];
- int *sockfd = NULL;
- int nsock = 0;
- int i, ret;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_PASSIVE;
-
- snprintf(portstr, 6, "%d", config.port);
-
- // debug(1,"listen socket port request is \"%s\".",portstr);
-
- ret = getaddrinfo(NULL, portstr, &hints, &info);
- if (ret) {
- die("getaddrinfo failed: %s", gai_strerror(ret));
- }
+ struct addrinfo hints, *info, *p;
+ char portstr[6];
+ int *sockfd = NULL;
+ int nsock = 0;
+ int i, ret;
- for (p=info; p; p=p->ai_next) {
- int fd = socket(p->ai_family, p->ai_socktype, IPPROTO_TCP);
- int yes = 1;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
- // Handle socket open failures if protocol unavailable (or IPV6 not handled)
- if (fd == -1) {
- // debug(1, "Failed to get socket: fam=%d, %s\n", p->ai_family, strerror(errno));
- continue;
- }
+ snprintf(portstr, 6, "%d", config.port);
+
+ // debug(1,"listen socket port request is \"%s\".",portstr);
+
+ ret = getaddrinfo(NULL, portstr, &hints, &info);
+ if (ret) {
+ die("getaddrinfo failed: %s", gai_strerror(ret));
+ }
- ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
+ for (p = info; p; p = p->ai_next) {
+ int fd = socket(p->ai_family, p->ai_socktype, IPPROTO_TCP);
+ int yes = 1;
+
+ // Handle socket open failures if protocol unavailable (or IPV6 not handled)
+ if (fd == -1) {
+ // debug(1, "Failed to get socket: fam=%d, %s\n", p->ai_family, strerror(errno));
+ continue;
+ }
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
#ifdef IPV6_V6ONLY
- // some systems don't support v4 access on v6 sockets, but some do.
- // since we need to account for two sockets we might as well
- // always.
- if (p->ai_family == AF_INET6) {
- ret |= setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes));
- }
+ // some systems don't support v4 access on v6 sockets, but some do.
+ // since we need to account for two sockets we might as well
+ // always.
+ if (p->ai_family == AF_INET6) {
+ ret |= setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes));
+ }
#endif
- if (!ret)
- ret = bind(fd, p->ai_addr, p->ai_addrlen);
-
- // one of the address families will fail on some systems that
- // report its availability. do not complain.
- if (ret) {
- debug(1, "Failed to bind to address %s.", format_address(p->ai_addr));
- continue;
- }
+ if (!ret)
+ ret = bind(fd, p->ai_addr, p->ai_addrlen);
- listen(fd, 5);
- nsock++;
- sockfd = realloc(sockfd, nsock*sizeof(int));
- sockfd[nsock-1] = fd;
+ // one of the address families will fail on some systems that
+ // report its availability. do not complain.
+ if (ret) {
+ debug(1, "Failed to bind to address %s.", format_address(p->ai_addr));
+ continue;
}
- freeaddrinfo(info);
+ listen(fd, 5);
+ nsock++;
+ sockfd = realloc(sockfd, nsock * sizeof(int));
+ sockfd[nsock - 1] = fd;
+ }
- if (!nsock)
- die("could not bind any listen sockets!");
+ freeaddrinfo(info);
- int maxfd = -1;
- fd_set fds;
- FD_ZERO(&fds);
- for (i=0; i<nsock; i++) {
- if (sockfd[i] > maxfd)
- maxfd = sockfd[i];
- }
+ if (!nsock)
+ die("could not bind any listen sockets!");
- mdns_register();
+ int maxfd = -1;
+ fd_set fds;
+ FD_ZERO(&fds);
+ for (i = 0; i < nsock; i++) {
+ if (sockfd[i] > maxfd)
+ maxfd = sockfd[i];
+ }
- // printf("Listening for connections.");
- // shairport_startup_complete();
+ mdns_register();
- int acceptfd;
- struct timeval tv;
- while (1) {
- tv.tv_sec = 300;
- tv.tv_usec = 0;
+ // printf("Listening for connections.");
+ // shairport_startup_complete();
- for (i=0; i<nsock; i++)
- FD_SET(sockfd[i], &fds);
+ int acceptfd;
+ struct timeval tv;
+ while (1) {
+ tv.tv_sec = 300;
+ tv.tv_usec = 0;
- ret = select(maxfd+1, &fds, 0, 0, &tv);
- if (ret<0) {
- if (errno==EINTR)
- continue;
- break;
- }
+ for (i = 0; i < nsock; i++)
+ FD_SET(sockfd[i], &fds);
- cleanup_threads();
+ ret = select(maxfd + 1, &fds, 0, 0, &tv);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
- acceptfd = -1;
- for (i=0; i<nsock; i++) {
- if (FD_ISSET(sockfd[i], &fds)) {
- acceptfd = sockfd[i];
- break;
- }
- }
- if (acceptfd < 0) // timeout
- continue;
-
- rtsp_conn_info *conn = malloc(sizeof(rtsp_conn_info));
- memset(conn, 0, sizeof(rtsp_conn_info));
- socklen_t slen = sizeof(conn->remote);
-
- debug(1, "new RTSP connection.");
- conn->fd = accept(acceptfd, (struct sockaddr *)&conn->remote, &slen);
- if (conn->fd < 0) {
- perror("failed to accept connection");
- free(conn);
- } else {
- pthread_t rtsp_conversation_thread;
- ret = pthread_create(&rtsp_conversation_thread, NULL, rtsp_conversation_thread_func, conn);
- if (ret)
- die("Failed to create RTSP receiver thread!");
-
- conn->thread = rtsp_conversation_thread;
- conn->running = 1;
- track_thread(conn);
- }
+ cleanup_threads();
+
+ acceptfd = -1;
+ for (i = 0; i < nsock; i++) {
+ if (FD_ISSET(sockfd[i], &fds)) {
+ acceptfd = sockfd[i];
+ break;
+ }
}
- perror("select");
- die("fell out of the RTSP select loop");
+ if (acceptfd < 0) // timeout
+ continue;
+
+ rtsp_conn_info *conn = malloc(sizeof(rtsp_conn_info));
+ memset(conn, 0, sizeof(rtsp_conn_info));
+ socklen_t slen = sizeof(conn->remote);
+
+ debug(1, "new RTSP connection.");
+ conn->fd = accept(acceptfd, (struct sockaddr *)&conn->remote, &slen);
+ if (conn->fd < 0) {
+ perror("failed to accept connection");
+ free(conn);
+ } else {
+ pthread_t rtsp_conversation_thread;
+ ret = pthread_create(&rtsp_conversation_thread, NULL, rtsp_conversation_thread_func, conn);
+ if (ret)
+ die("Failed to create RTSP receiver thread!");
+
+ conn->thread = rtsp_conversation_thread;
+ conn->running = 1;
+ track_thread(conn);
+ }
+ }
+ perror("select");
+ die("fell out of the RTSP select loop");
}
diff --git a/rtsp.h b/rtsp.h
index 74fdd52..672eefe 100644
--- a/rtsp.h
+++ b/rtsp.h
@@ -11,6 +11,6 @@ void rtsp_request_shutdown_stream(void);
// e.g. it it's malloced, to free it, etc.
// nothing is done automatically
-int send_ssnc_metadata(uint32_t code,char *data,uint32_t length, int block);
+int send_ssnc_metadata(uint32_t code, char *data, uint32_t length, int block);
#endif // _RTSP_H
diff --git a/shairport.c b/shairport.c
index cf8ce06..5ab2638 100644
--- a/shairport.c
+++ b/shairport.c
@@ -60,518 +60,536 @@
#include <libdaemon/dpid.h>
#include <libdaemon/dexec.h>
-
static int shutting_down = 0;
-static char* appName = NULL;
+static char *appName = NULL;
void shairport_shutdown() {
- if (shutting_down)
- return;
- shutting_down = 1;
- mdns_unregister();
- rtsp_shutdown_stream();
- if (config.output)
- config.output->deinit();
+ if (shutting_down)
+ return;
+ shutting_down = 1;
+ mdns_unregister();
+ rtsp_shutdown_stream();
+ if (config.output)
+ config.output->deinit();
}
-static void sig_ignore(int foo, siginfo_t *bar, void *baz) {
-}
+static void sig_ignore(int foo, siginfo_t *bar, void *baz) {}
static void sig_shutdown(int foo, siginfo_t *bar, void *baz) {
- debug(1, "shutdown requested...");
- shairport_shutdown();
- daemon_log(LOG_NOTICE, "exit...");
- daemon_retval_send(255);
- daemon_pid_file_remove();
- exit(0);
+ debug(1, "shutdown requested...");
+ shairport_shutdown();
+ daemon_log(LOG_NOTICE, "exit...");
+ daemon_retval_send(255);
+ daemon_pid_file_remove();
+ exit(0);
}
static void sig_child(int foo, siginfo_t *bar, void *baz) {
- pid_t pid;
- while ((pid = waitpid((pid_t)-1, 0, WNOHANG)) > 0) {
- if (pid == mdns_pid && !shutting_down) {
- die("MDNS child process died unexpectedly!");
- }
- }
+ pid_t pid;
+ while ((pid = waitpid((pid_t)-1, 0, WNOHANG)) > 0) {
+ if (pid == mdns_pid && !shutting_down) {
+ die("MDNS child process died unexpectedly!");
+ }
+ }
}
static void sig_disconnect_audio_output(int foo, siginfo_t *bar, void *baz) {
- debug(1,"disconnect audio output requested.");
- set_requested_connection_state_to_output(0);
+ debug(1, "disconnect audio output requested.");
+ set_requested_connection_state_to_output(0);
}
static void sig_connect_audio_output(int foo, siginfo_t *bar, void *baz) {
- debug(1,"connect audio output requested.");
- set_requested_connection_state_to_output(1);
+ debug(1, "connect audio output requested.");
+ set_requested_connection_state_to_output(1);
}
void print_version(void) {
char version_string[200];
- strcpy(version_string,PACKAGE_VERSION);
+ strcpy(version_string, PACKAGE_VERSION);
#ifdef HAVE_LIBPOLARSSL
- strcat(version_string,"-polarssl");
+ strcat(version_string, "-polarssl");
#endif
#ifdef HAVE_LIBSSL
- strcat(version_string,"-openssl");
+ strcat(version_string, "-openssl");
#endif
#ifdef CONFIG_TINYSVCMDNS
- strcat(version_string,"-tinysvcmdns");
+ strcat(version_string, "-tinysvcmdns");
#endif
#ifdef CONFIG_AVAHI
- strcat(version_string,"-Avahi");
+ strcat(version_string, "-Avahi");
#endif
#ifdef CONFIG_DNS_SD
- strcat(version_string,"-dns_sd");
+ strcat(version_string, "-dns_sd");
#endif
#ifdef CONFIG_ALSA
- strcat(version_string,"-ALSA");
+ strcat(version_string, "-ALSA");
#endif
#ifdef CONFIG_SNDIO
- strcat(version_string,"-sndio");
+ strcat(version_string, "-sndio");
#endif
#ifdef CONFIG_AO
- strcat(version_string,"-ao");
+ strcat(version_string, "-ao");
#endif
#ifdef CONFIG_PULSE
- strcat(version_string,"-pulse");
+ strcat(version_string, "-pulse");
#endif
#ifdef HAVE_LIBSOXR
- strcat(version_string,"-soxr");
+ strcat(version_string, "-soxr");
#endif
#ifdef CONFIG_METADATA
- strcat(version_string,"-metadata");
+ strcat(version_string, "-metadata");
#endif
#ifdef SUPPORT_CONFIG_FILES
- strcat(version_string,"-configfile");
+ strcat(version_string, "-configfile");
#endif
- printf("%s\n",version_string);
+ printf("%s\n", version_string);
}
void usage(char *progname) {
- printf("Usage: %s [options...]\n", progname);
- printf(" or: %s [options...] -- [audio output-specific options]\n", progname);
- printf("\n");
- printf("Options:\n");
- printf(" -h, --help show this help\n");
- printf(" -d, --daemon daemonise.\n");
- printf(" -V, --version show version information\n");
- printf(" -k, --kill kill the existing shairport daemon.\n");
- printf(" -D, --disconnectFromOutput disconnect immediately from the output device.\n");
- printf(" -R, --reconnectToOutput reconnect to the output device.\n");
- printf(" -c, --configfile=FILE read configuration settings from FILE. Default is /etc/shairport-sync.conf.\n");
+ printf("Usage: %s [options...]\n", progname);
+ printf(" or: %s [options...] -- [audio output-specific options]\n", progname);
+ printf("\n");
+ printf("Options:\n");
+ printf(" -h, --help show this help\n");
+ printf(" -d, --daemon daemonise.\n");
+ printf(" -V, --version show version information\n");
+ printf(" -k, --kill kill the existing shairport daemon.\n");
+ printf(" -D, --disconnectFromOutput disconnect immediately from the output device.\n");
+ printf(" -R, --reconnectToOutput reconnect to the output device.\n");
+ printf(" -c, --configfile=FILE read configuration settings from FILE. Default is "
+ "/etc/shairport-sync.conf.\n");
#ifdef COMMAND_LINE_ARGUMENT_SUPPORT
- printf(" -v, --verbose -v print debug information; -vv more; -vvv lots\n");
- printf(" -p, --port=PORT set RTSP listening port\n");
- printf(" -a, --name=NAME set advertised name\n");
- printf(" -A, --AirPlayLatency=FRAMES set the latency for audio sent from an AirPlay device.\n");
- printf(" The default value is %u frames.\n", config.AirPlayLatency);
- printf(" -i, --iTunesLatency=FRAMES set the latency for audio sent from iTunes 10 or later.\n");
- printf(" The default value is %u frames.\n", config.iTunesLatency);
- printf(" -L, --latency=FRAMES set the latency for audio sent from an unknown device\n");
- printf(" or from an old version of iTunes. Default is %d frames.\n",config.latency);
- printf(" --forkedDaapdLatency=FRAMES set the latency for audio sent from forked-daapd.\n");
- printf(" -S, --stuffing=MODE set how to adjust current latency to match desired latency \n");
- printf(" \"basic\" (default) inserts or deletes audio frames from packet frames with low processor overhead.\n");
- printf(" \"soxr\" uses libsoxr to minimally resample packet frames -- moderate processor overhead.\n");
- printf(" \"soxr\" option only available if built with soxr support.\n");
- printf(" -B, --on-start=PROGRAM run PROGRAM when playback is about to begin.\n");
- printf(" -E, --on-stop=PROGRAM run PROGRAM when playback has ended.\n");
- printf(" For -B and -E options, specify the full path to the program, e.g. /usr/bin/logger.\n");
- printf(" Executable scripts work, but must have #!/bin/sh (or whatever) in the headline.\n");
- printf(" -w, --wait-cmd wait until the -B or -E programs finish before continuing\n");
- printf(" -o, --output=BACKEND select audio output method\n");
- printf(" -m, --mdns=BACKEND force the use of BACKEND to advertize the service\n");
- printf(" if no mdns provider is specified,\n");
- printf(" shairport tries them all until one works.\n");
- printf(" -r, --resync=THRESHOLD resync if error exceeds this number of frames. Set to 0 to stop resyncing.\n");
- printf(" -t, --timeout=SECONDS go back to idle mode from play mode after a break in communications of this many seconds (default 120). Set to 0 never to exit play mode.\n");
- printf(" --statistics print some interesting statistics -- output to the logfile if running as a daemon.\n");
- printf(" --tolerance=TOLERANCE allow a synchronization error of TOLERANCE frames (default 88) before trying to correct it.\n");
- printf(" --password=PASSWORD require PASSWORD to connect. Default is not to require a password.\n");
+ printf(" -v, --verbose -v print debug information; -vv more; -vvv lots\n");
+ printf(" -p, --port=PORT set RTSP listening port\n");
+ printf(" -a, --name=NAME set advertised name\n");
+ printf(
+ " -A, --AirPlayLatency=FRAMES set the latency for audio sent from an AirPlay device.\n");
+ printf(" The default value is %u frames.\n", config.AirPlayLatency);
+ printf(
+ " -i, --iTunesLatency=FRAMES set the latency for audio sent from iTunes 10 or later.\n");
+ printf(" The default value is %u frames.\n", config.iTunesLatency);
+ printf(" -L, --latency=FRAMES set the latency for audio sent from an unknown device\n");
+ printf(" or from an old version of iTunes. Default is %d frames.\n",
+ config.latency);
+ printf(" --forkedDaapdLatency=FRAMES set the latency for audio sent from forked-daapd.\n");
+ printf(" -S, --stuffing=MODE set how to adjust current latency to match desired latency \n");
+ printf(" \"basic\" (default) inserts or deletes audio frames from "
+ "packet frames with low processor overhead.\n");
+ printf(" \"soxr\" uses libsoxr to minimally resample packet frames -- "
+ "moderate processor overhead.\n");
+ printf(
+ " \"soxr\" option only available if built with soxr support.\n");
+ printf(" -B, --on-start=PROGRAM run PROGRAM when playback is about to begin.\n");
+ printf(" -E, --on-stop=PROGRAM run PROGRAM when playback has ended.\n");
+ printf(" For -B and -E options, specify the full path to the program, "
+ "e.g. /usr/bin/logger.\n");
+ printf(" Executable scripts work, but must have #!/bin/sh (or "
+ "whatever) in the headline.\n");
+ printf(" -w, --wait-cmd wait until the -B or -E programs finish before continuing\n");
+ printf(" -o, --output=BACKEND select audio output method\n");
+ printf(" -m, --mdns=BACKEND force the use of BACKEND to advertize the service\n");
+ printf(" if no mdns provider is specified,\n");
+ printf(" shairport tries them all until one works.\n");
+ printf(" -r, --resync=THRESHOLD resync if error exceeds this number of frames. Set to 0 to "
+ "stop resyncing.\n");
+ printf(" -t, --timeout=SECONDS go back to idle mode from play mode after a break in "
+ "communications of this many seconds (default 120). Set to 0 never to exit play mode.\n");
+ printf(" --statistics print some interesting statistics -- output to the logfile "
+ "if running as a daemon.\n");
+ printf(" --tolerance=TOLERANCE allow a synchronization error of TOLERANCE frames (default "
+ "88) before trying to correct it.\n");
+ printf(" --password=PASSWORD require PASSWORD to connect. Default is not to require a "
+ "password.\n");
#ifdef CONFIG_METADATA
- printf(" --metadata-pipename=PIPE send metadata to PIPE, e.g. --metadata-pipename=/tmp/shairport-sync-metadata.\n");
- printf(" --get-coverart send cover art through the metadata pipe.\n");
+ printf(" --metadata-pipename=PIPE send metadata to PIPE, e.g. "
+ "--metadata-pipename=/tmp/shairport-sync-metadata.\n");
+ printf(" --get-coverart send cover art through the metadata pipe.\n");
#endif
#endif
#ifdef SUPPORT_CONFIG_FILES
- printf("\nGeneral options can be configured in /etc/%s.conf.\n",appName);
+ printf("\nGeneral options can be configured in /etc/%s.conf.\n", appName);
#endif
- printf("\n");
- mdns_ls_backends();
- printf("\n");
- audio_ls_outputs();
+ printf("\n");
+ mdns_ls_backends();
+ printf("\n");
+ audio_ls_outputs();
}
int parse_options(int argc, char **argv) {
- char *stuffing = NULL; /* used for picking up the stuffing option */
-
-
-
-
- signed char c; /* used for argument parsing */
- int i = 0; /* used for tracking options */
- poptContext optCon; /* context for parsing command-line options */
+ char *stuffing = NULL; /* used for picking up the stuffing option */
+
+ signed char c; /* used for argument parsing */
+ int i = 0; /* used for tracking options */
+ poptContext optCon; /* context for parsing command-line options */
struct poptOption optionsTable[] = {
- { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', NULL },
- { "disconnectFromOutput", 'D', POPT_ARG_NONE, NULL, 0, NULL },
- { "reconnectToOutput", 'R', POPT_ARG_NONE, NULL, 0, NULL },
- { "kill", 'k', POPT_ARG_NONE, NULL, 0, NULL },
- { "daemon", 'd', POPT_ARG_NONE, &config.daemonise, 0, NULL },
+ {"verbose", 'v', POPT_ARG_NONE, NULL, 'v', NULL},
+ {"disconnectFromOutput", 'D', POPT_ARG_NONE, NULL, 0, NULL},
+ {"reconnectToOutput", 'R', POPT_ARG_NONE, NULL, 0, NULL},
+ {"kill", 'k', POPT_ARG_NONE, NULL, 0, NULL},
+ {"daemon", 'd', POPT_ARG_NONE, &config.daemonise, 0, NULL},
#ifdef SUPPORT_CONFIG_FILES
- { "configfile", 'c', POPT_ARG_STRING, &config.configfile, 0, NULL },
+ {"configfile", 'c', POPT_ARG_STRING, &config.configfile, 0, NULL},
#endif
#ifdef COMMAND_LINE_ARGUMENT_SUPPORT
- { "statistics", 0, POPT_ARG_NONE, &config.statistics_requested, 0, NULL},
- { "version", 'V', POPT_ARG_NONE, NULL, 0, NULL},
- { "port", 'p', POPT_ARG_INT, &config.port, 0, NULL } ,
- { "name", 'a', POPT_ARG_STRING, &config.apname, 0, NULL } ,
- { "output", 'o', POPT_ARG_STRING, &config.output_name, 0, NULL } ,
- { "on-start", 'B', POPT_ARG_STRING, &config.cmd_start, 0, NULL } ,
- { "on-stop", 'E', POPT_ARG_STRING, &config.cmd_stop, 0, NULL } ,
- { "wait-cmd", 'w', POPT_ARG_NONE, &config.cmd_blocking, 0, NULL } ,
- { "mdns", 'm', POPT_ARG_STRING, &config.mdns_name, 0, NULL } ,
- { "latency", 'L', POPT_ARG_INT, &config.userSuppliedLatency, 0, NULL } ,
- { "AirPlayLatency", 'A', POPT_ARG_INT, &config.AirPlayLatency, 0, NULL } ,
- { "iTunesLatency", 'i', POPT_ARG_INT, &config.iTunesLatency, 0, NULL } ,
- { "forkedDaapdLatency", 0, POPT_ARG_INT, &config.ForkedDaapdLatency, 0, NULL } ,
- { "stuffing", 'S', POPT_ARG_STRING, &stuffing, 'S', NULL } ,
- { "resync", 'r', POPT_ARG_INT, &config.resyncthreshold, 0, NULL } ,
- { "timeout", 't', POPT_ARG_INT, &config.timeout, 't', NULL } ,
- { "password", 0, POPT_ARG_STRING, &config.password, 0, NULL } ,
- { "tolerance", 0, POPT_ARG_INT, &config.tolerance, 0, NULL } ,
+ {"statistics", 0, POPT_ARG_NONE, &config.statistics_requested, 0, NULL},
+ {"version", 'V', POPT_ARG_NONE, NULL, 0, NULL},
+ {"port", 'p', POPT_ARG_INT, &config.port, 0, NULL},
+ {"name", 'a', POPT_ARG_STRING, &config.apname, 0, NULL},
+ {"output", 'o', POPT_ARG_STRING, &config.output_name, 0, NULL},
+ {"on-start", 'B', POPT_ARG_STRING, &config.cmd_start, 0, NULL},
+ {"on-stop", 'E', POPT_ARG_STRING, &config.cmd_stop, 0, NULL},
+ {"wait-cmd", 'w', POPT_ARG_NONE, &config.cmd_blocking, 0, NULL},
+ {"mdns", 'm', POPT_ARG_STRING, &config.mdns_name, 0, NULL},
+ {"latency", 'L', POPT_ARG_INT, &config.userSuppliedLatency, 0, NULL},
+ {"AirPlayLatency", 'A', POPT_ARG_INT, &config.AirPlayLatency, 0, NULL},
+ {"iTunesLatency", 'i', POPT_ARG_INT, &config.iTunesLatency, 0, NULL},
+ {"forkedDaapdLatency", 0, POPT_ARG_INT, &config.ForkedDaapdLatency, 0, NULL},
+ {"stuffing", 'S', POPT_ARG_STRING, &stuffing, 'S', NULL},
+ {"resync", 'r', POPT_ARG_INT, &config.resyncthreshold, 0, NULL},
+ {"timeout", 't', POPT_ARG_INT, &config.timeout, 't', NULL},
+ {"password", 0, POPT_ARG_STRING, &config.password, 0, NULL},
+ {"tolerance", 0, POPT_ARG_INT, &config.tolerance, 0, NULL},
#ifdef CONFIG_METADATA
- { "metadata-pipename", 'M', POPT_ARG_STRING, &config.metadata_pipename, 'M', NULL } ,
- { "get-coverart", 'g', POPT_ARG_NONE, &config.get_coverart, 'g', NULL } ,
+ {"metadata-pipename", 'M', POPT_ARG_STRING, &config.metadata_pipename, 'M', NULL},
+ {"get-coverart", 'g', POPT_ARG_NONE, &config.get_coverart, 'g', NULL},
#endif
- POPT_AUTOHELP
+ POPT_AUTOHELP
#endif
- { NULL, 0, 0, NULL, 0 }
- };
+ {NULL, 0, 0, NULL, 0}};
-
- int optind=argc;
+ int optind = argc;
int j;
- for (j=0;j<argc;j++)
- if (strcmp(argv[j],"--")==0)
- optind=j;
+ for (j = 0; j < argc; j++)
+ if (strcmp(argv[j], "--") == 0)
+ optind = j;
- optCon = poptGetContext(NULL, optind,(const char **)argv, optionsTable, 0);
+ optCon = poptGetContext(NULL, optind, (const char **)argv, optionsTable, 0);
poptSetOtherOptionHelp(optCon, "[OPTIONS]* ");
/* Now do options processing, get portname */
-
+
while ((c = poptGetNextOpt(optCon)) >= 0) {
switch (c) {
- case 'v':
- debuglev++;
- break;
- case 't':
- if (config.timeout==0) {
- config.dont_check_timeout=1;
- config.allow_session_interruption=1;
- } else {
- config.dont_check_timeout=0;
- config.allow_session_interruption=0;
- }
- break;
+ case 'v':
+ debuglev++;
+ break;
+ case 't':
+ if (config.timeout == 0) {
+ config.dont_check_timeout = 1;
+ config.allow_session_interruption = 1;
+ } else {
+ config.dont_check_timeout = 0;
+ config.allow_session_interruption = 0;
+ }
+ break;
#ifdef CONFIG_METADATA
- case 'M':
- config.metadata_enabled=1;
- break;
- case 'g':
- if (config.metadata_enabled==0)
- die("If you want to get cover art, you must also select the --metadata-pipename option.");
- break;
-#endif
- case 'S':
- if (strcmp(stuffing,"basic")==0)
- config.packet_stuffing = ST_basic;
- else if (strcmp(stuffing,"soxr")==0)
+ case 'M':
+ config.metadata_enabled = 1;
+ break;
+ case 'g':
+ if (config.metadata_enabled == 0)
+ die("If you want to get cover art, you must also select the --metadata-pipename option.");
+ break;
+#endif
+ case 'S':
+ if (strcmp(stuffing, "basic") == 0)
+ config.packet_stuffing = ST_basic;
+ else if (strcmp(stuffing, "soxr") == 0)
#ifdef HAVE_LIBSOXR
- config.packet_stuffing = ST_soxr;
+ config.packet_stuffing = ST_soxr;
#else
- die("soxr option not available -- this version of shairport-sync was built without libsoxr support");
+ die("soxr option not available -- this version of shairport-sync was built without libsoxr "
+ "support");
#endif
- else
- die("Illegal stuffing option \"%s\" -- must be \"basic\" or \"soxr\"",stuffing);
- break;
+ else
+ die("Illegal stuffing option \"%s\" -- must be \"basic\" or \"soxr\"", stuffing);
+ break;
}
}
if (c < -1) {
- die("%s: %s",poptBadOption(optCon, POPT_BADOPTION_NOALIAS),poptStrerror(c));
+ die("%s: %s", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
}
#ifdef SUPPORT_CONFIG_FILES
- char configuration_file_path[4096];
- strcpy(configuration_file_path,"/etc/");
- strcat(configuration_file_path,appName);
- strcat(configuration_file_path,".conf");
- debug(2,"Looking for file \"%s\"",configuration_file_path);
- config_setting_t *setting;
- const char *str;
- int value;
-
- config_init(&config_file_stuff);
- char *cfp = configuration_file_path;
- // use the configuration file path given in the -c option, if any
- if (config.configfile)
- cfp = config.configfile;
- /* Read the file. If there is an error, report it and exit. */
- if(config_read_file(&config_file_stuff,cfp)) {
- // make config.cfg point to it
- config.cfg = &config_file_stuff;
- /* Get the Service Name. */
- if(config_lookup_string(config.cfg, "general.name", &str))
- config.apname=(char *)str;
-
- /* Get the Daemonize setting. */
- if(config_lookup_string(config.cfg, "general.daemonize", &str)) {
- if (strcasecmp(str,"no")==0)
- config.daemonise=0;
- else if (strcasecmp(str,"yes")==0)
- config.daemonise=1;
- else
- die("Invalid daemonize option choice \"%s\". It should be \"yes\" or \"no\"");
- }
+ char configuration_file_path[4096];
+ strcpy(configuration_file_path, "/etc/");
+ strcat(configuration_file_path, appName);
+ strcat(configuration_file_path, ".conf");
+ debug(2, "Looking for file \"%s\"", configuration_file_path);
+ config_setting_t *setting;
+ const char *str;
+ int value;
+
+ config_init(&config_file_stuff);
+ char *cfp = configuration_file_path;
+ // use the configuration file path given in the -c option, if any
+ if (config.configfile)
+ cfp = config.configfile;
+ /* Read the file. If there is an error, report it and exit. */
+ if (config_read_file(&config_file_stuff, cfp)) {
+ // make config.cfg point to it
+ config.cfg = &config_file_stuff;
+ /* Get the Service Name. */
+ if (config_lookup_string(config.cfg, "general.name", &str))
+ config.apname = (char *)str;
+
+ /* Get the Daemonize setting. */
+ if (config_lookup_string(config.cfg, "general.daemonize", &str)) {
+ if (strcasecmp(str, "no") == 0)
+ config.daemonise = 0;
+ else if (strcasecmp(str, "yes") == 0)
+ config.daemonise = 1;
+ else
+ die("Invalid daemonize option choice \"%s\". It should be \"yes\" or \"no\"");
+ }
- /* Get the mdns_backend setting. */
- if(config_lookup_string(config.cfg, "general.mdns_backend", &str))
- config.mdns_name=(char *)str;
+ /* Get the mdns_backend setting. */
+ if (config_lookup_string(config.cfg, "general.mdns_backend", &str))
+ config.mdns_name = (char *)str;
- /* Get the output_backend setting. */
- if(config_lookup_string(config.cfg, "general.output_backend", &str))
- config.output_name=(char *)str;
+ /* Get the output_backend setting. */
+ if (config_lookup_string(config.cfg, "general.output_backend", &str))
+ config.output_name = (char *)str;
- /* Get the port setting. */
- if(config_lookup_int(config.cfg, "general.port", &value)) {
- if ((value<0) || (value>65535))
- die("Invalid port number \"%sd\". It should be between 0 and 65535, default is 5000",value);
- else
- config.port=value;
- }
+ /* Get the port setting. */
+ if (config_lookup_int(config.cfg, "general.port", &value)) {
+ if ((value < 0) || (value > 65535))
+ die("Invalid port number \"%sd\". It should be between 0 and 65535, default is 5000",
+ value);
+ else
+ config.port = value;
+ }
- /* Get the password setting. */
- if(config_lookup_string(config.cfg, "general.password", &str))
- config.password=(char *)str;
-
- if(config_lookup_string(config.cfg, "general.interpolation", &str)) {
- if (strcasecmp(str,"basic")==0)
- config.packet_stuffing=ST_basic;
- else if (strcasecmp(str,"soxr")==0)
- config.packet_stuffing=ST_soxr;
- else
- die("Invalid interpolation option choice \"%s\". It should be \"basic\" or \"soxr\"");
- }
+ /* Get the password setting. */
+ if (config_lookup_string(config.cfg, "general.password", &str))
+ config.password = (char *)str;
- /* Get the statistics setting. */
- if(config_lookup_string(config.cfg, "general.statistics", &str)) {
- if (strcasecmp(str,"no")==0)
- config.statistics_requested=0;
- else if (strcasecmp(str,"yes")==0)
- config.statistics_requested=1;
- else
- die("Invalid statistics option choice \"%s\". It should be \"yes\" or \"no\"");
- }
+ if (config_lookup_string(config.cfg, "general.interpolation", &str)) {
+ if (strcasecmp(str, "basic") == 0)
+ config.packet_stuffing = ST_basic;
+ else if (strcasecmp(str, "soxr") == 0)
+ config.packet_stuffing = ST_soxr;
+ else
+ die("Invalid interpolation option choice \"%s\". It should be \"basic\" or \"soxr\"");
+ }
- /* Get the drift tolerance setting. */
- if(config_lookup_int(config.cfg, "general.drift", &value))
- config.tolerance=value;
-
- /* Get the resync setting. */
- if(config_lookup_int(config.cfg, "general.resync_threshold", &value))
- config.resyncthreshold=value;
-
- /* Get the verbosity setting. */
- if(config_lookup_int(config.cfg, "general.log_verbosity", &value))
- if ((value>=0) && (value<=3))
- debuglev=value;
- else
- die("Invalid log verbosity setting option choice \"%d\". It should be between 0 and 3, inclusive.",value);
-
- /* Get the ignore_volume_control setting. */
- if(config_lookup_string(config.cfg, "general.ignore_volume_control", &str)) {
- if (strcasecmp(str,"no")==0)
- config.ignore_volume_control=0;
- else if (strcasecmp(str,"yes")==0)
- config.ignore_volume_control=1;
- else
- die("Invalid ignore_volume_control option choice \"%s\". It should be \"yes\" or \"no\"");
- }
-
- /* Get the default latency. */
- if(config_lookup_int(config.cfg, "latencies.default", &value))
- config.latency=value;
+ /* Get the statistics setting. */
+ if (config_lookup_string(config.cfg, "general.statistics", &str)) {
+ if (strcasecmp(str, "no") == 0)
+ config.statistics_requested = 0;
+ else if (strcasecmp(str, "yes") == 0)
+ config.statistics_requested = 1;
+ else
+ die("Invalid statistics option choice \"%s\". It should be \"yes\" or \"no\"");
+ }
+
+ /* Get the drift tolerance setting. */
+ if (config_lookup_int(config.cfg, "general.drift", &value))
+ config.tolerance = value;
+
+ /* Get the resync setting. */
+ if (config_lookup_int(config.cfg, "general.resync_threshold", &value))
+ config.resyncthreshold = value;
+
+ /* Get the verbosity setting. */
+ if (config_lookup_int(config.cfg, "general.log_verbosity", &value))
+ if ((value >= 0) && (value <= 3))
+ debuglev = value;
+ else
+ die("Invalid log verbosity setting option choice \"%d\". It should be between 0 and 3, "
+ "inclusive.",
+ value);
+
+ /* Get the ignore_volume_control setting. */
+ if (config_lookup_string(config.cfg, "general.ignore_volume_control", &str)) {
+ if (strcasecmp(str, "no") == 0)
+ config.ignore_volume_control = 0;
+ else if (strcasecmp(str, "yes") == 0)
+ config.ignore_volume_control = 1;
+ else
+ die("Invalid ignore_volume_control option choice \"%s\". It should be \"yes\" or \"no\"");
+ }
+
+ /* Get the default latency. */
+ if (config_lookup_int(config.cfg, "latencies.default", &value))
+ config.latency = value;
- /* Get the itunes latency. */
- if(config_lookup_int(config.cfg, "latencies.itunes", &value))
- config.iTunesLatency=value;
+ /* Get the itunes latency. */
+ if (config_lookup_int(config.cfg, "latencies.itunes", &value))
+ config.iTunesLatency = value;
- /* Get the AirPlay latency. */
- if(config_lookup_int(config.cfg, "latencies.airplay", &value))
- config.AirPlayLatency=value;
+ /* Get the AirPlay latency. */
+ if (config_lookup_int(config.cfg, "latencies.airplay", &value))
+ config.AirPlayLatency = value;
- /* Get the forkedDaapd latency. */
- if(config_lookup_int(config.cfg, "latencies.forkedDaapd", &value))
- config.ForkedDaapdLatency=value;
+ /* Get the forkedDaapd latency. */
+ if (config_lookup_int(config.cfg, "latencies.forkedDaapd", &value))
+ config.ForkedDaapdLatency = value;
#ifdef CONFIG_METADATA
- /* Get the metadata setting. */
- if(config_lookup_string(config.cfg, "metadata.enabled", &str)) {
- if (strcasecmp(str,"no")==0)
- config.metadata_enabled=0;
- else if (strcasecmp(str,"yes")==0)
- config.metadata_enabled=1;
- else
- die("Invalid metadata enabled option choice \"%s\". It should be \"yes\" or \"no\"");
- }
-
- if(config_lookup_string(config.cfg, "metadata.include_cover_art", &str)) {
- if (strcasecmp(str,"no")==0)
- config.get_coverart=0;
- else if (strcasecmp(str,"yes")==0)
- config.get_coverart=1;
- else
- die("Invalid metadata include_cover_art option choice \"%s\". It should be \"yes\" or \"no\"");
- }
-
- if(config_lookup_string(config.cfg, "metadata.pipe_name", &str)) {
- config.metadata_pipename=(char *)str;
- }
+ /* Get the metadata setting. */
+ if (config_lookup_string(config.cfg, "metadata.enabled", &str)) {
+ if (strcasecmp(str, "no") == 0)
+ config.metadata_enabled = 0;
+ else if (strcasecmp(str, "yes") == 0)
+ config.metadata_enabled = 1;
+ else
+ die("Invalid metadata enabled option choice \"%s\". It should be \"yes\" or \"no\"");
+ }
+
+ if (config_lookup_string(config.cfg, "metadata.include_cover_art", &str)) {
+ if (strcasecmp(str, "no") == 0)
+ config.get_coverart = 0;
+ else if (strcasecmp(str, "yes") == 0)
+ config.get_coverart = 1;
+ else
+ die("Invalid metadata include_cover_art option choice \"%s\". It should be \"yes\" or "
+ "\"no\"");
+ }
+
+ if (config_lookup_string(config.cfg, "metadata.pipe_name", &str)) {
+ config.metadata_pipename = (char *)str;
+ }
#endif
- if(config_lookup_string(config.cfg, "sessioncontrol.run_this_before_play_begins", &str)) {
- config.cmd_start=(char *)str;
- }
-
- if(config_lookup_string(config.cfg, "sessioncontrol.run_this_after_play_ends", &str)) {
- config.cmd_stop=(char *)str;
- }
-
- if(config_lookup_string(config.cfg, "sessioncontrol.wait_for_completion", &str)) {
- if (strcasecmp(str,"no")==0)
- config.cmd_blocking=0;
- else if (strcasecmp(str,"yes")==0)
- config.cmd_blocking=1;
- else
- die("Invalid session control wait_for_completion option choice \"%s\". It should be \"yes\" or \"no\"");
- }
-
- if(config_lookup_string(config.cfg, "sessioncontrol.allow_session_interruption", &str)) {
- config.dont_check_timeout=0; // this is for legacy -- only set by -t 0
- if (strcasecmp(str,"no")==0)
- config.allow_session_interruption=0;
- else if (strcasecmp(str,"yes")==0)
- config.allow_session_interruption=1;
- else
- die("Invalid session control allow_interruption option choice \"%s\". It should be \"yes\" or \"no\"");
- }
+ if (config_lookup_string(config.cfg, "sessioncontrol.run_this_before_play_begins", &str)) {
+ config.cmd_start = (char *)str;
+ }
- if(config_lookup_int(config.cfg, "sessioncontrol.session_timeout", &value)) {
- config.timeout=value;
- config.dont_check_timeout=0; // this is for legacy -- only set by -t 0
- }
+ if (config_lookup_string(config.cfg, "sessioncontrol.run_this_after_play_ends", &str)) {
+ config.cmd_stop = (char *)str;
+ }
- } else {
- if (config_error_type (&config_file_stuff)==CONFIG_ERR_FILE_IO)
- debug(1,"Error reading configuration file \"%s\": \"%s\".",config_error_file(&config_file_stuff),config_error_text(&config_file_stuff));
- else {
- die("Line %d of the configuration file \"%s\":\n%s",
- config_error_line(&config_file_stuff),config_error_file(&config_file_stuff),config_error_text(&config_file_stuff));
- }
+ if (config_lookup_string(config.cfg, "sessioncontrol.wait_for_completion", &str)) {
+ if (strcasecmp(str, "no") == 0)
+ config.cmd_blocking = 0;
+ else if (strcasecmp(str, "yes") == 0)
+ config.cmd_blocking = 1;
+ else
+ die("Invalid session control wait_for_completion option choice \"%s\". It should be "
+ "\"yes\" or \"no\"");
+ }
+
+ if (config_lookup_string(config.cfg, "sessioncontrol.allow_session_interruption", &str)) {
+ config.dont_check_timeout = 0; // this is for legacy -- only set by -t 0
+ if (strcasecmp(str, "no") == 0)
+ config.allow_session_interruption = 0;
+ else if (strcasecmp(str, "yes") == 0)
+ config.allow_session_interruption = 1;
+ else
+ die("Invalid session control allow_interruption option choice \"%s\". It should be \"yes\" "
+ "or \"no\"");
+ }
+
+ if (config_lookup_int(config.cfg, "sessioncontrol.session_timeout", &value)) {
+ config.timeout = value;
+ config.dont_check_timeout = 0; // this is for legacy -- only set by -t 0
+ }
+
+ } else {
+ if (config_error_type(&config_file_stuff) == CONFIG_ERR_FILE_IO)
+ debug(1, "Error reading configuration file \"%s\": \"%s\".",
+ config_error_file(&config_file_stuff), config_error_text(&config_file_stuff));
+ else {
+ die("Line %d of the configuration file \"%s\":\n%s", config_error_line(&config_file_stuff),
+ config_error_file(&config_file_stuff), config_error_text(&config_file_stuff));
}
-
+ }
+
#endif
/* Print out options */
-
- 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,"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);
- debug(2,"on-stop action is \"%s\".",config.cmd_stop);
- debug(2,"wait-cmd status is %d.",config.cmd_blocking);
- debug(2,"mdns backend \"%s\".",config.mdns_name);
- debug(2,"userSuppliedLatency is %d.",config.userSuppliedLatency);
- debug(2,"AirPlayLatency is %d.",config.AirPlayLatency);
- debug(2,"iTunesLatency is %d.",config.iTunesLatency);
- debug(2,"forkedDaapdLatency is %d.",config.ForkedDaapdLatency);
- debug(2,"stuffing option is \"%d\".",config.packet_stuffing);
- debug(2,"resync time is %d.",config.resyncthreshold);
- debug(2,"allow a session to be interrupted: %d.",config.allow_session_interruption);
- debug(2,"busy timeout time is %d.",config.timeout);
- debug(2,"tolerance is %d frames.",config.tolerance);
- debug(2,"password is \"%s\".",config.password);
- debug(2,"ignore_volume_control is %d.",config.ignore_volume_control);
- debug(2,"audio backend desired buffer length is %d.",config.audio_backend_buffer_desired_length);
- debug(2,"audio backend latency offset is %d.",config.audio_backend_latency_offset);
+
+ 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, "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);
+ debug(2, "on-stop action is \"%s\".", config.cmd_stop);
+ debug(2, "wait-cmd status is %d.", config.cmd_blocking);
+ debug(2, "mdns backend \"%s\".", config.mdns_name);
+ debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency);
+ debug(2, "AirPlayLatency is %d.", config.AirPlayLatency);
+ debug(2, "iTunesLatency is %d.", config.iTunesLatency);
+ debug(2, "forkedDaapdLatency is %d.", config.ForkedDaapdLatency);
+ debug(2, "stuffing option is \"%d\".", config.packet_stuffing);
+ debug(2, "resync time is %d.", config.resyncthreshold);
+ debug(2, "allow a session to be interrupted: %d.", config.allow_session_interruption);
+ debug(2, "busy timeout time is %d.", config.timeout);
+ debug(2, "tolerance is %d frames.", config.tolerance);
+ debug(2, "password is \"%s\".", config.password);
+ debug(2, "ignore_volume_control is %d.", config.ignore_volume_control);
+ debug(2, "audio backend desired buffer length is %d.",
+ config.audio_backend_buffer_desired_length);
+ debug(2, "audio backend latency offset is %d.", config.audio_backend_latency_offset);
#ifdef CONFIG_METADATA
- debug(2,"metdata enabled is %d.",config.metadata_enabled);
- debug(2,"metadata pipename is \"%s\".",config.metadata_pipename);
- debug(2,"get-coverart is %d.",config.get_coverart);
+ debug(2, "metdata enabled is %d.", config.metadata_enabled);
+ debug(2, "metadata pipename is \"%s\".", config.metadata_pipename);
+ debug(2, "get-coverart is %d.", config.get_coverart);
#endif
- return optind+1;
+ return optind + 1;
}
void signal_setup(void) {
- // mask off all signals before creating threads.
- // this way we control which thread gets which signals.
- // for now, we don't care which thread gets the following.
- sigset_t set;
- sigfillset(&set);
- sigdelset(&set, SIGINT);
- sigdelset(&set, SIGTERM);
- sigdelset(&set, SIGHUP);
- sigdelset(&set, SIGSTOP);
- sigdelset(&set, SIGCHLD);
- sigdelset(&set, SIGUSR2);
- pthread_sigmask(SIG_BLOCK, &set, NULL);
-
- // setting this to SIG_IGN would prevent signalling any threads.
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_flags = SA_SIGINFO;
- sa.sa_sigaction = &sig_ignore;
- sigaction(SIGUSR1, &sa, NULL);
-
- sa.sa_flags = SA_SIGINFO | SA_RESTART;
- sa.sa_sigaction = &sig_shutdown;
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGTERM, &sa, NULL);
-
- sa.sa_sigaction = &sig_disconnect_audio_output;
- sigaction(SIGUSR2, &sa, NULL);
-
- sa.sa_sigaction = &sig_connect_audio_output;
- sigaction(SIGHUP, &sa, NULL);
-
- sa.sa_sigaction = &sig_child;
- sigaction(SIGCHLD, &sa, NULL);
+ // mask off all signals before creating threads.
+ // this way we control which thread gets which signals.
+ // for now, we don't care which thread gets the following.
+ sigset_t set;
+ sigfillset(&set);
+ sigdelset(&set, SIGINT);
+ sigdelset(&set, SIGTERM);
+ sigdelset(&set, SIGHUP);
+ sigdelset(&set, SIGSTOP);
+ sigdelset(&set, SIGCHLD);
+ sigdelset(&set, SIGUSR2);
+ pthread_sigmask(SIG_BLOCK, &set, NULL);
+
+ // setting this to SIG_IGN would prevent signalling any threads.
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_SIGINFO;
+ sa.sa_sigaction = &sig_ignore;
+ sigaction(SIGUSR1, &sa, NULL);
+
+ sa.sa_flags = SA_SIGINFO | SA_RESTART;
+ sa.sa_sigaction = &sig_shutdown;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ sa.sa_sigaction = &sig_disconnect_audio_output;
+ sigaction(SIGUSR2, &sa, NULL);
+
+ sa.sa_sigaction = &sig_connect_audio_output;
+ sigaction(SIGHUP, &sa, NULL);
+
+ sa.sa_sigaction = &sig_child;
+ sigaction(SIGCHLD, &sa, NULL);
}
// forked daemon lets the spawner know it's up and running OK
// should be called only once!
void shairport_startup_complete(void) {
- if (config.daemonise) {
-// daemon_ready();
- }
+ if (config.daemonise) {
+ // daemon_ready();
+ }
}
#ifdef USE_CUSTOM_PID_DIR
const char *pid_file_proc(void) {
#ifdef HAVE_ASPRINTF
- static char *fn = NULL;
- free(fn);
- asprintf(&fn, "%s/%s.pid", PIDDIR, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown");
+ static char *fn = NULL;
+ free(fn);
+ asprintf(&fn, "%s/%s.pid", PIDDIR, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown");
#else
- static char fn[8192];
- snprintf(fn, sizeof(fn), "%s/%s.pid", PIDDIR, daemon_pid_file_ident ? daemon_pid_file_ident : "unknown");
+ static char fn[8192];
+ snprintf(fn, sizeof(fn), "%s/%s.pid", PIDDIR,
+ daemon_pid_file_ident ? daemon_pid_file_ident : "unknown");
#endif
- return fn;
+ return fn;
}
#endif
@@ -579,222 +597,234 @@ void exit_function() {
#ifdef SUPPORT_CONFIG_FILES
if (config.cfg)
config_destroy(config.cfg);
-#endif
+#endif
}
int main(int argc, char **argv) {
- daemon_set_verbosity(LOG_DEBUG);
- memset(&config, 0, sizeof(config)); // also clears all strings, BTW
- atexit(exit_function);
-
- // set defaults
- config.statistics_requested - 0; // don't print stats in the log
- config.latency = 88200; // AirPlay. Is also reset in rtsp.c when play is about to start
- config.userSuppliedLatency = 0; // zero means none supplied
- config.iTunesLatency = 99400; // this seems to work pretty well for iTunes from Version 10 (?) upwards-- two left-ear headphones, one from the iMac jack, one from an NSLU2 running a cheap "3D Sound" USB Soundcard
- config.AirPlayLatency = 88200; // this seems to work pretty well for AirPlay -- Syncs sound and vision on AppleTV, but also used for iPhone/iPod/iPad sources
- config.ForkedDaapdLatency = 99400; // Seems to be right
- config.resyncthreshold = 441*5; // this number of frames is 50 ms
- config.timeout = 120; // this number of seconds to wait for [more] audio before switching to idle.
- config.tolerance = 88; // this number of frames of error before attempting to correct it.
- config.buffer_start_fill = 220;
- config.port = 5000;
- config.packet_stuffing = ST_basic; // simple interpolation or deletion
- char hostname[100];
- gethostname(hostname, 100);
- config.apname = malloc(20 + 100);
- snprintf(config.apname, 20 + 100, "Shairport Sync on %s", hostname);
- 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.
-
- // this is a bit weird, but apparently necessary
- char* basec = strdup(argv[0]);
- char* bname = basename(basec);
- appName = strdup(bname);
- free(basec);
-
- /* Check if we are called with -V or --version parameter */
- if (argc >= 2 && ((strcmp(argv[1], "-V")==0) || (strcmp(argv[1], "--version")==0))) {
- print_version();
- exit(1);
- }
+ daemon_set_verbosity(LOG_DEBUG);
+ memset(&config, 0, sizeof(config)); // also clears all strings, BTW
+ atexit(exit_function);
+
+ // set defaults
+ config.statistics_requested - 0; // don't print stats in the log
+ config.latency = 88200; // AirPlay. Is also reset in rtsp.c when play is about to start
+ config.userSuppliedLatency = 0; // zero means none supplied
+ config.iTunesLatency = 99400; // this seems to work pretty well for iTunes from Version 10 (?)
+ // upwards-- two left-ear headphones, one from the iMac jack, one
+ // from an NSLU2 running a cheap "3D Sound" USB Soundcard
+ config.AirPlayLatency = 88200; // this seems to work pretty well for AirPlay -- Syncs sound and
+ // vision on AppleTV, but also used for iPhone/iPod/iPad sources
+ config.ForkedDaapdLatency = 99400; // Seems to be right
+ config.resyncthreshold = 441 * 5; // this number of frames is 50 ms
+ config.timeout = 120; // this number of seconds to wait for [more] audio before switching to idle.
+ config.tolerance = 88; // this number of frames of error before attempting to correct it.
+ config.buffer_start_fill = 220;
+ config.port = 5000;
+ config.packet_stuffing = ST_basic; // simple interpolation or deletion
+ char hostname[100];
+ gethostname(hostname, 100);
+ config.apname = malloc(20 + 100);
+ snprintf(config.apname, 20 + 100, "Shairport Sync on %s", hostname);
+ 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.
+
+ // this is a bit weird, but apparently necessary
+ char *basec = strdup(argv[0]);
+ char *bname = basename(basec);
+ appName = strdup(bname);
+ free(basec);
+
+ /* Check if we are called with -V or --version parameter */
+ if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) {
+ print_version();
+ exit(1);
+ }
- /* Check if we are called with -h or --help parameter */
- if (argc >= 2 && ((strcmp(argv[1], "-h")==0) || (strcmp(argv[1], "--help")==0))) {
- usage(argv[0]);
- exit(1);
- }
+ /* Check if we are called with -h or --help parameter */
+ if (argc >= 2 && ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0))) {
+ usage(argv[0]);
+ exit(1);
+ }
- pid_t pid;
+ pid_t pid;
- /* Reset signal handlers */
- if (daemon_reset_sigs(-1) < 0) {
- daemon_log(LOG_ERR, "Failed to reset all signal handlers: %s", strerror(errno));
- return 1;
- }
+ /* Reset signal handlers */
+ if (daemon_reset_sigs(-1) < 0) {
+ daemon_log(LOG_ERR, "Failed to reset all signal handlers: %s", strerror(errno));
+ return 1;
+ }
+
+ /* Unblock signals */
+ if (daemon_unblock_sigs(-1) < 0) {
+ daemon_log(LOG_ERR, "Failed to unblock all signals: %s", strerror(errno));
+ return 1;
+ }
- /* Unblock signals */
- if (daemon_unblock_sigs(-1) < 0) {
- daemon_log(LOG_ERR, "Failed to unblock all signals: %s", strerror(errno));
- return 1;
- }
-
-
#if USE_CUSTOM_LOCAL_STATE_DIR
- debug(1,"Locating localstatedir at \"%s\"",LOCALSTATEDIR);
- /* Point to a function to help locate where the PID file will go */
- daemon_pid_file_proc = pid_file_proc;
-#endif
-
- /* Set indentification string for the daemon for both syslog and PID file */
- daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]);
-
- /* Check if we are called with -D or --disconnectFromOutput parameter */
- if (argc >= 2 && ((strcmp(argv[1], "-D")==0) || (strcmp(argv[1], "--disconnectFromOutput")==0))) {
- if ((pid = daemon_pid_file_is_running()) >= 0) {
- if (kill(pid,SIGUSR2)!=0) { // try to send the signal
- daemon_log(LOG_WARNING, "Failed trying to send disconnectFromOutput command to daemon pid: %d: %s",pid, strerror(errno));
- }
- } else {
- daemon_log(LOG_WARNING, "Can't send a disconnectFromOutput request -- Failed to find daemon: %s", strerror(errno));
+ debug(1, "Locating localstatedir at \"%s\"", LOCALSTATEDIR);
+ /* Point to a function to help locate where the PID file will go */
+ daemon_pid_file_proc = pid_file_proc;
+#endif
+
+ /* Set indentification string for the daemon for both syslog and PID file */
+ daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]);
+
+ /* Check if we are called with -D or --disconnectFromOutput parameter */
+ if (argc >= 2 &&
+ ((strcmp(argv[1], "-D") == 0) || (strcmp(argv[1], "--disconnectFromOutput") == 0))) {
+ if ((pid = daemon_pid_file_is_running()) >= 0) {
+ if (kill(pid, SIGUSR2) != 0) { // try to send the signal
+ daemon_log(LOG_WARNING,
+ "Failed trying to send disconnectFromOutput command to daemon pid: %d: %s", pid,
+ strerror(errno));
}
- exit(1);
+ } else {
+ daemon_log(LOG_WARNING,
+ "Can't send a disconnectFromOutput request -- Failed to find daemon: %s",
+ strerror(errno));
}
-
- /* Check if we are called with -R or --reconnectToOutput parameter */
- if (argc >= 2 && ((strcmp(argv[1], "-R")==0) || (strcmp(argv[1], "--reconnectToOutput")==0))) {
- if ((pid = daemon_pid_file_is_running()) >= 0) {
- if (kill(pid,SIGHUP)!=0) { // try to send the signal
- daemon_log(LOG_WARNING, "Failed trying to send reconnectToOutput command to daemon pid: %d: %s",pid, strerror(errno));
- }
- } else {
- daemon_log(LOG_WARNING, "Can't send a reconnectToOutput request -- Failed to find daemon: %s", strerror(errno));
+ exit(1);
+ }
+
+ /* Check if we are called with -R or --reconnectToOutput parameter */
+ if (argc >= 2 &&
+ ((strcmp(argv[1], "-R") == 0) || (strcmp(argv[1], "--reconnectToOutput") == 0))) {
+ if ((pid = daemon_pid_file_is_running()) >= 0) {
+ if (kill(pid, SIGHUP) != 0) { // try to send the signal
+ daemon_log(LOG_WARNING,
+ "Failed trying to send reconnectToOutput command to daemon pid: %d: %s", pid,
+ strerror(errno));
}
- exit(1);
+ } else {
+ daemon_log(LOG_WARNING, "Can't send a reconnectToOutput request -- Failed to find daemon: %s",
+ strerror(errno));
}
-
- /* Check if we are called with -k or --kill parameter */
- if (argc >= 2 && ((strcmp(argv[1], "-k")==0) || (strcmp(argv[1], "--kill")==0))) {
- int ret;
+ exit(1);
+ }
- /* Kill daemon with SIGTERM */
- /* Check if the new function daemon_pid_file_kill_wait() is available, if it is, use it. */
- if ((ret = daemon_pid_file_kill_wait(SIGTERM, 5)) < 0)
- daemon_log(LOG_WARNING, "Failed to kill daemon: %s", strerror(errno));
- else
- daemon_pid_file_remove();
- return ret < 0 ? 1 : 0;
- }
+ /* Check if we are called with -k or --kill parameter */
+ if (argc >= 2 && ((strcmp(argv[1], "-k") == 0) || (strcmp(argv[1], "--kill") == 0))) {
+ int ret;
+
+ /* Kill daemon with SIGTERM */
+ /* Check if the new function daemon_pid_file_kill_wait() is available, if it is, use it. */
+ if ((ret = daemon_pid_file_kill_wait(SIGTERM, 5)) < 0)
+ daemon_log(LOG_WARNING, "Failed to kill daemon: %s", strerror(errno));
+ else
+ daemon_pid_file_remove();
+ return ret < 0 ? 1 : 0;
+ }
- /* Check that the daemon is not running twice at the same time */
- if ((pid = daemon_pid_file_is_running()) >= 0) {
- daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid);
- return 1;
+ /* Check that the daemon is not running twice at the same time */
+ if ((pid = daemon_pid_file_is_running()) >= 0) {
+ daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid);
+ return 1;
+ }
+
+ // parse arguments into config
+ int audio_arg = parse_options(argc, argv);
+
+ // mDNS supports maximum of 63-character names (we append 13).
+ if (strlen(config.apname) > 50)
+ die("Supplied name too long (max 50 characters)");
+
+ /* here, daemonise with libdaemon */
+
+ if (config.daemonise) {
+ /* Prepare for return value passing from the initialization procedure of the daemon process */
+ if (daemon_retval_init() < 0) {
+ daemon_log(LOG_ERR, "Failed to create pipe.");
+ return 1;
}
- // parse arguments into config
- int audio_arg = parse_options(argc, argv);
-
- // mDNS supports maximum of 63-character names (we append 13).
- if (strlen(config.apname) > 50)
- die("Supplied name too long (max 50 characters)");
-
- /* here, daemonise with libdaemon */
-
- if (config.daemonise) {
- /* Prepare for return value passing from the initialization procedure of the daemon process */
- if (daemon_retval_init() < 0) {
- daemon_log(LOG_ERR, "Failed to create pipe.");
- return 1;
+ /* Do the fork */
+ if ((pid = daemon_fork()) < 0) {
+
+ /* Exit on error */
+ daemon_retval_done();
+ return 1;
+
+ } else if (pid) { /* The parent */
+ int ret;
+
+ /* Wait for 20 seconds for the return value passed from the daemon process */
+ if ((ret = daemon_retval_wait(20)) < 0) {
+ daemon_log(LOG_ERR, "Could not receive return value from daemon process: %s",
+ strerror(errno));
+ return 255;
}
- /* Do the fork */
- if ((pid = daemon_fork()) < 0) {
-
- /* Exit on error */
- daemon_retval_done();
- return 1;
-
- } else if (pid) { /* The parent */
- int ret;
-
- /* Wait for 20 seconds for the return value passed from the daemon process */
- if ((ret = daemon_retval_wait(20)) < 0) {
- daemon_log(LOG_ERR, "Could not receive return value from daemon process: %s", strerror(errno));
- return 255;
- }
-
- if (ret != 0)
- daemon_log(ret != 0 ? LOG_ERR : LOG_INFO, "Daemon returned %i as return value.", ret);
- return ret;
-
- } else { /* The daemon */
-
- /* Close FDs */
- if (daemon_close_all(-1) < 0) {
- daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno));
-
- /* Send the error condition to the parent process */
- daemon_retval_send(1);
- goto finish;
- }
-
- /* Create the PID file */
- if (daemon_pid_file_create() < 0) {
- daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno));
- daemon_retval_send(2);
- goto finish;
- }
-
- /* Send OK to parent process */
- daemon_retval_send(0);
+ if (ret != 0)
+ daemon_log(ret != 0 ? LOG_ERR : LOG_INFO, "Daemon returned %i as return value.", ret);
+ return ret;
+
+ } else { /* The daemon */
+
+ /* Close FDs */
+ if (daemon_close_all(-1) < 0) {
+ daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno));
+
+ /* Send the error condition to the parent process */
+ daemon_retval_send(1);
+ goto finish;
}
- /* end libdaemon stuff */
- }
- signal_setup();
-
- // make sure the program can create files that group and world can read
- umask(S_IWGRP | S_IWOTH);
+ /* Create the PID file */
+ if (daemon_pid_file_create() < 0) {
+ daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno));
+ daemon_retval_send(2);
+ goto finish;
+ }
- config.output = audio_get_output(config.output_name);
- if (!config.output) {
- audio_ls_outputs();
- die("Invalid audio output specified!");
+ /* Send OK to parent process */
+ daemon_retval_send(0);
}
- config.output->init(argc-audio_arg, argv+audio_arg);
+ /* end libdaemon stuff */
+ }
- daemon_log(LOG_NOTICE, "startup");
+ signal_setup();
- uint8_t ap_md5[16];
+ // make sure the program can create files that group and world can read
+ umask(S_IWGRP | S_IWOTH);
+
+ config.output = audio_get_output(config.output_name);
+ if (!config.output) {
+ audio_ls_outputs();
+ die("Invalid audio output specified!");
+ }
+ config.output->init(argc - audio_arg, argv + audio_arg);
+
+ daemon_log(LOG_NOTICE, "startup");
+
+ uint8_t ap_md5[16];
#ifdef HAVE_LIBSSL
- MD5_CTX ctx;
- MD5_Init(&ctx);
- MD5_Update(&ctx, config.apname, strlen(config.apname));
- MD5_Final(ap_md5, &ctx);
+ MD5_CTX ctx;
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, config.apname, strlen(config.apname));
+ MD5_Final(ap_md5, &ctx);
#endif
#ifdef HAVE_LIBPOLARSSL
- md5_context tctx;
- md5_starts(&tctx);
- md5_update(&tctx, (unsigned char *)config.apname, strlen(config.apname));
- md5_finish(&tctx, ap_md5);
+ md5_context tctx;
+ md5_starts(&tctx);
+ md5_update(&tctx, (unsigned char *)config.apname, strlen(config.apname));
+ md5_finish(&tctx, ap_md5);
#endif
- memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr));
+ memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr));
#ifdef CONFIG_METADATA
- metadata_init() ; // create the metadata pipe if necessary
+ metadata_init(); // create the metadata pipe if necessary
#endif
- rtsp_listen_loop();
+ rtsp_listen_loop();
- // should not reach this...
- shairport_shutdown();
+ // should not reach this...
+ shairport_shutdown();
finish:
- daemon_log(LOG_NOTICE, "Unexpected exit...");
- daemon_retval_send(255);
- daemon_pid_file_remove();
- return 1;
-
+ daemon_log(LOG_NOTICE, "Unexpected exit...");
+ daemon_retval_send(255);
+ daemon_pid_file_remove();
+ return 1;
}
diff --git a/tinysvcmdns.c b/tinysvcmdns.c
index 8c03b38..bdb6c09 100644
--- a/tinysvcmdns.c
+++ b/tinysvcmdns.c
@@ -34,18 +34,16 @@
#include "common.h"
#define DEBUG_PRINTF(...) debug(1, __VA_ARGS__)
-#define log_message(level, ...) \
- do { \
- switch(level) \
- { \
- case LOG_ERR: \
- warn(__VA_ARGS__); \
- break; \
- default: \
- debug(1, __VA_ARGS__); \
- } \
- } while (0)
-
+#define log_message(level, ...) \
+ do { \
+ switch (level) { \
+ case LOG_ERR: \
+ warn(__VA_ARGS__); \
+ break; \
+ default: \
+ debug(1, __VA_ARGS__); \
+ } \
+ } while (0)
//******************************************************//
// mdns.c //
@@ -63,311 +61,320 @@
#include <netinet/in.h>
#endif
-// See RFC 6762 Section 10 for an account of two TTLs -- 120 seconds for rrs with a host name as the record's name
+// See RFC 6762 Section 10 for an account of two TTLs -- 120 seconds for rrs with a host name as the
+// record's name
// or a host name in the record's rdata
// 75 minutes for everything else.
// https://tools.ietf.org/html/rfc6762
-#define DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME 120
-#define DEFAULT_TTL 4500
+#define DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME 120
+#define DEFAULT_TTL 4500
struct name_comp {
- uint8_t *label; // label
- size_t pos; // position in msg
+ uint8_t *label; // label
+ size_t pos; // position in msg
- struct name_comp *next;
+ struct name_comp *next;
};
// ----- label functions -----
// duplicates a name
inline uint8_t *dup_nlabel(const uint8_t *n) {
- assert(n[0] <= 63); // prevent mis-use
- return (uint8_t *) strdup((char *) n);
+ assert(n[0] <= 63); // prevent mis-use
+ return (uint8_t *)strdup((char *)n);
}
// duplicates a label
uint8_t *dup_label(const uint8_t *label) {
- int len = *label + 1;
- if (len > 63)
- return NULL;
- uint8_t *newlabel = malloc(len + 1);
- strncpy((char *) newlabel, (char *) label, len);
- newlabel[len] = '\0';
- return newlabel;
+ int len = *label + 1;
+ if (len > 63)
+ return NULL;
+ uint8_t *newlabel = malloc(len + 1);
+ strncpy((char *)newlabel, (char *)label, len);
+ newlabel[len] = '\0';
+ return newlabel;
}
uint8_t *join_nlabel(const uint8_t *n1, const uint8_t *n2) {
- int len1, len2;
- uint8_t *s;
+ int len1, len2;
+ uint8_t *s;
- assert(n1[0] <= 63 && n2[0] <= 63); // detect misuse
+ assert(n1[0] <= 63 && n2[0] <= 63); // detect misuse
- len1 = strlen((char *) n1);
- len2 = strlen((char *) n2);
+ len1 = strlen((char *)n1);
+ len2 = strlen((char *)n2);
- s = malloc(len1 + len2 + 1);
- strncpy((char *) s, (char *) n1, len1);
- strncpy((char *) s+len1, (char *) n2, len2);
- s[len1 + len2] = '\0';
- return s;
+ s = malloc(len1 + len2 + 1);
+ strncpy((char *)s, (char *)n1, len1);
+ strncpy((char *)s + len1, (char *)n2, len2);
+ s[len1 + len2] = '\0';
+ return s;
}
// returns a human-readable name label in dotted form
char *nlabel_to_str(const uint8_t *name) {
- char *label, *labelp;
- const uint8_t *p;
+ char *label, *labelp;
+ const uint8_t *p;
- assert(name != NULL);
+ assert(name != NULL);
- label = labelp = malloc(256);
+ label = labelp = malloc(256);
- for (p = name; *p; p++) {
- strncpy(labelp, (char *) p + 1, *p);
- labelp += *p;
- *labelp = '.';
- labelp++;
+ for (p = name; *p; p++) {
+ strncpy(labelp, (char *)p + 1, *p);
+ labelp += *p;
+ *labelp = '.';
+ labelp++;
- p += *p;
- }
+ p += *p;
+ }
- *labelp = '\0';
+ *labelp = '\0';
- return label;
+ return label;
}
// returns the length of a label field
// does NOT uncompress the field, so it could be as small as 2 bytes
// or 1 for the root
static size_t label_len(uint8_t *pkt_buf, size_t pkt_len, size_t off) {
- uint8_t *p;
- uint8_t *e = pkt_buf + pkt_len;
- size_t len = 0;
-
- for (p = pkt_buf + off; p < e; p++) {
- if (*p == 0) {
- return len + 1;
- } else if ((*p & 0xC0) == 0xC0) {
- return len + 2;
- } else {
- len += *p + 1;
- p += *p;
- }
- }
-
- return len;
+ uint8_t *p;
+ uint8_t *e = pkt_buf + pkt_len;
+ size_t len = 0;
+
+ for (p = pkt_buf + off; p < e; p++) {
+ if (*p == 0) {
+ return len + 1;
+ } else if ((*p & 0xC0) == 0xC0) {
+ return len + 2;
+ } else {
+ len += *p + 1;
+ p += *p;
+ }
+ }
+
+ return len;
}
// creates a label
// free() after use
uint8_t *create_label(const char *txt) {
- int len;
- uint8_t *s;
+ int len;
+ uint8_t *s;
- assert(txt != NULL);
- len = strlen(txt);
- if (len > 63)
- return NULL;
+ assert(txt != NULL);
+ len = strlen(txt);
+ if (len > 63)
+ return NULL;
- s = malloc(len + 2);
- s[0] = len;
- strncpy((char *) s + 1, txt, len);
- s[len + 1] = '\0';
+ s = malloc(len + 2);
+ s[0] = len;
+ strncpy((char *)s + 1, txt, len);
+ s[len + 1] = '\0';
- return s;
+ return s;
}
// creates a uncompressed name label given a DNS name like "apple.b.com"
// free() after use
uint8_t *create_nlabel(const char *name) {
- char *label;
- char *p, *e, *lenpos;
- int len = 0;
+ char *label;
+ char *p, *e, *lenpos;
+ int len = 0;
- assert(name != NULL);
+ assert(name != NULL);
- len = strlen(name);
- label = malloc(len + 1 + 1);
- if (label == NULL)
- return NULL;
+ len = strlen(name);
+ label = malloc(len + 1 + 1);
+ if (label == NULL)
+ return NULL;
- strncpy((char *) label + 1, name, len);
- label[len + 1] = '\0';
+ strncpy((char *)label + 1, name, len);
+ label[len + 1] = '\0';
- p = label;
- e = p + len;
- lenpos = p;
+ p = label;
+ e = p + len;
+ lenpos = p;
- while (p < e) {
- *lenpos = 0;
- char *dot = memchr(p + 1, '.', e - p - 1);
- if (dot == NULL)
- dot = e + 1;
- *lenpos = dot - p - 1;
+ while (p < e) {
+ *lenpos = 0;
+ char *dot = memchr(p + 1, '.', e - p - 1);
+ if (dot == NULL)
+ dot = e + 1;
+ *lenpos = dot - p - 1;
- p = dot;
- lenpos = dot;
- }
+ p = dot;
+ lenpos = dot;
+ }
- return (uint8_t *) label;
+ return (uint8_t *)label;
}
// copies a label from the buffer into a newly-allocated string
// free() after use
static uint8_t *copy_label(uint8_t *pkt_buf, size_t pkt_len, size_t off) {
- int len;
+ int len;
- if (off > pkt_len)
- return NULL;
+ if (off > pkt_len)
+ return NULL;
- len = pkt_buf[off] + 1;
- if (off + len > pkt_len) {
- DEBUG_PRINTF("label length exceeds packet buffer\n");
- return NULL;
- }
+ len = pkt_buf[off] + 1;
+ if (off + len > pkt_len) {
+ DEBUG_PRINTF("label length exceeds packet buffer\n");
+ return NULL;
+ }
- return dup_label(pkt_buf + off);
+ return dup_label(pkt_buf + off);
}
// uncompresses a name
// free() after use
static uint8_t *uncompress_nlabel(uint8_t *pkt_buf, size_t pkt_len, size_t off) {
- uint8_t *p;
- uint8_t *e = pkt_buf + pkt_len;
- size_t len = 0;
- char *str, *sp;
- if (off >= pkt_len)
- return NULL;
-
- // calculate length of uncompressed label
- for (p = pkt_buf + off; *p && p < e; p++) {
- size_t llen = 0;
- if ((*p & 0xC0) == 0xC0) {
- uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]);
- llen = *p2 + 1;
- p = p2 + llen - 1;
- } else {
- llen = *p + 1;
- p += llen - 1;
- }
- len += llen;
- }
-
- str = sp = malloc(len + 1);
- if (str == NULL)
- return NULL;
-
- // FIXME: must merge this with above code
- for (p = pkt_buf + off; *p && p < e; p++) {
- size_t llen = 0;
- if ((*p & 0xC0) == 0xC0) {
- uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]);
- llen = *p2 + 1;
- strncpy(sp, (char *) p2, llen);
- p = p2 + llen - 1;
- } else {
- llen = *p + 1;
- strncpy(sp, (char *) p, llen);
- p += llen - 1;
- }
- sp += llen;
- }
- *sp = '\0';
-
- return (uint8_t *) str;
+ uint8_t *p;
+ uint8_t *e = pkt_buf + pkt_len;
+ size_t len = 0;
+ char *str, *sp;
+ if (off >= pkt_len)
+ return NULL;
+
+ // calculate length of uncompressed label
+ for (p = pkt_buf + off; *p && p < e; p++) {
+ size_t llen = 0;
+ if ((*p & 0xC0) == 0xC0) {
+ uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]);
+ llen = *p2 + 1;
+ p = p2 + llen - 1;
+ } else {
+ llen = *p + 1;
+ p += llen - 1;
+ }
+ len += llen;
+ }
+
+ str = sp = malloc(len + 1);
+ if (str == NULL)
+ return NULL;
+
+ // FIXME: must merge this with above code
+ for (p = pkt_buf + off; *p && p < e; p++) {
+ size_t llen = 0;
+ if ((*p & 0xC0) == 0xC0) {
+ uint8_t *p2 = pkt_buf + (((p[0] & ~0xC0) << 8) | p[1]);
+ llen = *p2 + 1;
+ strncpy(sp, (char *)p2, llen);
+ p = p2 + llen - 1;
+ } else {
+ llen = *p + 1;
+ strncpy(sp, (char *)p, llen);
+ p += llen - 1;
+ }
+ sp += llen;
+ }
+ *sp = '\0';
+
+ return (uint8_t *)str;
}
// ----- RR list & group functions -----
const char *rr_get_type_name(enum rr_type type) {
- switch (type) {
- case RR_A: return "A";
- case RR_PTR: return "PTR";
- case RR_TXT: return "TXT";
- case RR_AAAA: return "AAAA";
- case RR_SRV: return "SRV";
- case RR_NSEC: return "NSEC";
- case RR_ANY: return "ANY";
- }
- return NULL;
+ switch (type) {
+ case RR_A:
+ return "A";
+ case RR_PTR:
+ return "PTR";
+ case RR_TXT:
+ return "TXT";
+ case RR_AAAA:
+ return "AAAA";
+ case RR_SRV:
+ return "SRV";
+ case RR_NSEC:
+ return "NSEC";
+ case RR_ANY:
+ return "ANY";
+ }
+ return NULL;
}
void rr_entry_destroy(struct rr_entry *rr) {
- struct rr_data_txt *txt_rec;
- assert(rr);
-
- // check rr_type and free data elements
- switch (rr->type) {
- case RR_PTR:
- if (rr->data.PTR.name)
- free(rr->data.PTR.name);
- // don't free entry
- break;
-
- case RR_TXT:
- txt_rec = &rr->data.TXT;
- while (txt_rec) {
- struct rr_data_txt *next = txt_rec->next;
- if (txt_rec->txt)
- free(txt_rec->txt);
-
- // only free() if it wasn't part of the struct
- if (txt_rec != &rr->data.TXT)
- free(txt_rec);
-
- txt_rec = next;
- }
- break;
-
- case RR_SRV:
- if (rr->data.SRV.target)
- free(rr->data.SRV.target);
- break;
-
- default:
- // nothing to free
- break;
- }
-
- free(rr->name);
- free(rr);
+ struct rr_data_txt *txt_rec;
+ assert(rr);
+
+ // check rr_type and free data elements
+ switch (rr->type) {
+ case RR_PTR:
+ if (rr->data.PTR.name)
+ free(rr->data.PTR.name);
+ // don't free entry
+ break;
+
+ case RR_TXT:
+ txt_rec = &rr->data.TXT;
+ while (txt_rec) {
+ struct rr_data_txt *next = txt_rec->next;
+ if (txt_rec->txt)
+ free(txt_rec->txt);
+
+ // only free() if it wasn't part of the struct
+ if (txt_rec != &rr->data.TXT)
+ free(txt_rec);
+
+ txt_rec = next;
+ }
+ break;
+
+ case RR_SRV:
+ if (rr->data.SRV.target)
+ free(rr->data.SRV.target);
+ break;
+
+ default:
+ // nothing to free
+ break;
+ }
+
+ free(rr->name);
+ free(rr);
}
// destroys an RR list (and optionally, items)
void rr_list_destroy(struct rr_list *rr, char destroy_items) {
- struct rr_list *rr_next;
-
- for (; rr; rr = rr_next) {
- rr_next = rr->next;
- if (destroy_items)
- rr_entry_destroy(rr->e);
- free(rr);
- }
+ struct rr_list *rr_next;
+
+ for (; rr; rr = rr_next) {
+ rr_next = rr->next;
+ if (destroy_items)
+ rr_entry_destroy(rr->e);
+ free(rr);
+ }
}
int rr_list_count(struct rr_list *rr) {
- int i = 0;
- for (; rr; i++, rr = rr->next);
- return i;
+ int i = 0;
+ for (; rr; i++, rr = rr->next)
+ ;
+ return i;
}
struct rr_entry *rr_list_remove(struct rr_list **rr_head, struct rr_entry *rr) {
- struct rr_list *le = *rr_head, *pe = NULL;
- for (; le; le = le->next) {
- if (le->e == rr) {
- if (pe == NULL) {
- *rr_head = le->next;
- free(le);
- return rr;
- } else {
- pe->next = le->next;
- free(le);
- return rr;
- }
- }
- pe = le;
- }
- return NULL;
+ struct rr_list *le = *rr_head, *pe = NULL;
+ for (; le; le = le->next) {
+ if (le->e == rr) {
+ if (pe == NULL) {
+ *rr_head = le->next;
+ free(le);
+ return rr;
+ } else {
+ pe->next = le->next;
+ free(le);
+ return rr;
+ }
+ }
+ pe = le;
+ }
+ return NULL;
}
// appends an rr_entry to an RR list
@@ -375,650 +382,642 @@ struct rr_entry *rr_list_remove(struct rr_list **rr_head, struct rr_entry *rr) {
// RRs are compared by memory location - not its contents
// return value of 0 means item not added
int rr_list_append(struct rr_list **rr_head, struct rr_entry *rr) {
- struct rr_list *node = malloc(sizeof(struct rr_list));
- node->e = rr;
- node->next = NULL;
-
- if (*rr_head == NULL) {
- *rr_head = node;
- } else {
- struct rr_list *e = *rr_head, *taile;
- for (; e; e = e->next) {
- // already in list - don't add
- if (e->e == rr) {
- free(node);
- return 0;
- }
- if (e->next == NULL)
- taile = e;
- }
- taile->next = node;
- }
- return 1;
+ struct rr_list *node = malloc(sizeof(struct rr_list));
+ node->e = rr;
+ node->next = NULL;
+
+ if (*rr_head == NULL) {
+ *rr_head = node;
+ } else {
+ struct rr_list *e = *rr_head, *taile;
+ for (; e; e = e->next) {
+ // already in list - don't add
+ if (e->e == rr) {
+ free(node);
+ return 0;
+ }
+ if (e->next == NULL)
+ taile = e;
+ }
+ taile->next = node;
+ }
+ return 1;
}
-#define FILL_RR_ENTRY(rr, _name, _type) \
- rr->name = _name; \
- rr->type = _type; \
- rr->ttl = DEFAULT_TTL; \
- rr->cache_flush = 1; \
- rr->rr_class = 1;
+#define FILL_RR_ENTRY(rr, _name, _type) \
+ rr->name = _name; \
+ rr->type = _type; \
+ rr->ttl = DEFAULT_TTL; \
+ rr->cache_flush = 1; \
+ rr->rr_class = 1;
struct rr_entry *rr_create_a(uint8_t *name, uint32_t addr) {
- DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
- FILL_RR_ENTRY(rr, name, RR_A);
- rr->data.A.addr = addr;
- rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10
- return rr;
+ DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
+ FILL_RR_ENTRY(rr, name, RR_A);
+ rr->data.A.addr = addr;
+ rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10
+ return rr;
}
struct rr_entry *rr_create_aaaa(uint8_t *name, struct in6_addr *addr) {
- DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
- FILL_RR_ENTRY(rr, name, RR_AAAA);
- rr->data.AAAA.addr = addr;
- rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10
- return rr;
+ DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
+ FILL_RR_ENTRY(rr, name, RR_AAAA);
+ rr->data.AAAA.addr = addr;
+ rr->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // 120 seconds -- see RFC 6762 Section 10
+ return rr;
}
struct rr_entry *rr_create_srv(uint8_t *name, uint16_t port, uint8_t *target) {
- DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
- FILL_RR_ENTRY(rr, name, RR_SRV);
- rr->data.SRV.port = port;
- rr->data.SRV.target = target;
- return rr;
+ DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
+ FILL_RR_ENTRY(rr, name, RR_SRV);
+ rr->data.SRV.port = port;
+ rr->data.SRV.target = target;
+ return rr;
}
struct rr_entry *rr_create_ptr(uint8_t *name, struct rr_entry *d_rr) {
- DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
- FILL_RR_ENTRY(rr, name, RR_PTR);
- rr->cache_flush = 0; // PTRs shouldn't have their cache flush bit set
- rr->data.PTR.entry = d_rr;
- return rr;
+ DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
+ FILL_RR_ENTRY(rr, name, RR_PTR);
+ rr->cache_flush = 0; // PTRs shouldn't have their cache flush bit set
+ rr->data.PTR.entry = d_rr;
+ return rr;
}
struct rr_entry *rr_create(uint8_t *name, enum rr_type type) {
- DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
- FILL_RR_ENTRY(rr, name, type);
- return rr;
+ DECL_MALLOC_ZERO_STRUCT(rr, rr_entry);
+ FILL_RR_ENTRY(rr, name, type);
+ return rr;
}
void rr_set_nsec(struct rr_entry *rr_nsec, enum rr_type type) {
- assert(rr_nsec->type = RR_NSEC);
- assert((type / 8) < sizeof(rr_nsec->data.NSEC.bitmap));
+ assert(rr_nsec->type = RR_NSEC);
+ assert((type / 8) < sizeof(rr_nsec->data.NSEC.bitmap));
- rr_nsec->data.NSEC.bitmap[ type / 8 ] = 1 << (7 - (type % 8));
+ rr_nsec->data.NSEC.bitmap[type / 8] = 1 << (7 - (type % 8));
}
void rr_add_txt(struct rr_entry *rr_txt, const char *txt) {
- struct rr_data_txt *txt_rec;
- assert(rr_txt->type == RR_TXT);
+ struct rr_data_txt *txt_rec;
+ assert(rr_txt->type == RR_TXT);
- txt_rec = &rr_txt->data.TXT;
+ txt_rec = &rr_txt->data.TXT;
- // is current data filled?
- if (txt_rec->txt == NULL) {
- txt_rec->txt = create_label(txt);
- return;
- }
+ // is current data filled?
+ if (txt_rec->txt == NULL) {
+ txt_rec->txt = create_label(txt);
+ return;
+ }
- // find the last node
- for (; txt_rec->next; txt_rec = txt_rec->next);
+ // find the last node
+ for (; txt_rec->next; txt_rec = txt_rec->next)
+ ;
- // create a new empty node
- txt_rec->next = malloc(sizeof(struct rr_data_txt));
+ // create a new empty node
+ txt_rec->next = malloc(sizeof(struct rr_data_txt));
- txt_rec = txt_rec->next;
- txt_rec->txt = create_label(txt);
- txt_rec->next = NULL;
+ txt_rec = txt_rec->next;
+ txt_rec->txt = create_label(txt);
+ txt_rec->next = NULL;
}
// adds a record to an rr_group
void rr_group_add(struct rr_group **group, struct rr_entry *rr) {
- struct rr_group *g;
+ struct rr_group *g;
- assert(rr != NULL);
+ assert(rr != NULL);
- if (*group) {
- g = rr_group_find(*group, rr->name);
- if (g) {
- rr_list_append(&g->rr, rr);
- return;
- }
- }
+ if (*group) {
+ g = rr_group_find(*group, rr->name);
+ if (g) {
+ rr_list_append(&g->rr, rr);
+ return;
+ }
+ }
- MALLOC_ZERO_STRUCT(g, rr_group);
- g->name = dup_nlabel(rr->name);
- rr_list_append(&g->rr, rr);
+ MALLOC_ZERO_STRUCT(g, rr_group);
+ g->name = dup_nlabel(rr->name);
+ rr_list_append(&g->rr, rr);
- // prepend to list
- g->next = *group;
- *group = g;
+ // prepend to list
+ g->next = *group;
+ *group = g;
}
// finds a rr_group matching the given name
-struct rr_group *rr_group_find(struct rr_group* g, uint8_t *name) {
- for (; g; g = g->next) {
- if (cmp_nlabel(g->name, name) == 0)
- return g;
- }
- return NULL;
+struct rr_group *rr_group_find(struct rr_group *g, uint8_t *name) {
+ for (; g; g = g->next) {
+ if (cmp_nlabel(g->name, name) == 0)
+ return g;
+ }
+ return NULL;
}
struct rr_entry *rr_entry_find(struct rr_list *rr_list, uint8_t *name, uint16_t type) {
- struct rr_list *rr = rr_list;
- for (; rr; rr = rr->next) {
- if (rr->e->type == type && cmp_nlabel(rr->e->name, name) == 0)
- return rr->e;
- }
- return NULL;
+ struct rr_list *rr = rr_list;
+ for (; rr; rr = rr->next) {
+ if (rr->e->type == type && cmp_nlabel(rr->e->name, name) == 0)
+ return rr->e;
+ }
+ return NULL;
}
// looks for a matching entry in rr_list
// if entry is a PTR, we need to check if the PTR target also matches
struct rr_entry *rr_entry_match(struct rr_list *rr_list, struct rr_entry *entry) {
- struct rr_list *rr = rr_list;
- for (; rr; rr = rr->next) {
- if (rr->e->type == entry->type && cmp_nlabel(rr->e->name, entry->name) == 0) {
- if (entry->type != RR_PTR) {
- return rr->e;
- } else if (cmp_nlabel(MDNS_RR_GET_PTR_NAME(entry), MDNS_RR_GET_PTR_NAME(rr->e)) == 0) {
- // if it's a PTR, we need to make sure PTR target also matches
- return rr->e;
- }
- }
- }
- return NULL;
+ struct rr_list *rr = rr_list;
+ for (; rr; rr = rr->next) {
+ if (rr->e->type == entry->type && cmp_nlabel(rr->e->name, entry->name) == 0) {
+ if (entry->type != RR_PTR) {
+ return rr->e;
+ } else if (cmp_nlabel(MDNS_RR_GET_PTR_NAME(entry), MDNS_RR_GET_PTR_NAME(rr->e)) == 0) {
+ // if it's a PTR, we need to make sure PTR target also matches
+ return rr->e;
+ }
+ }
+ }
+ return NULL;
}
void rr_group_destroy(struct rr_group *group) {
- struct rr_group *g = group;
-
- while (g) {
- struct rr_group *nextg = g->next;
- free(g->name);
- rr_list_destroy(g->rr, 1);
- free(g);
- g = nextg;
- }
+ struct rr_group *g = group;
+
+ while (g) {
+ struct rr_group *nextg = g->next;
+ free(g->name);
+ rr_list_destroy(g->rr, 1);
+ free(g);
+ g = nextg;
+ }
}
uint8_t *mdns_write_u16(uint8_t *ptr, const uint16_t v) {
- *ptr++ = (uint8_t) (v >> 8) & 0xFF;
- *ptr++ = (uint8_t) (v >> 0) & 0xFF;
- return ptr;
+ *ptr++ = (uint8_t)(v >> 8) & 0xFF;
+ *ptr++ = (uint8_t)(v >> 0) & 0xFF;
+ return ptr;
}
uint8_t *mdns_write_u32(uint8_t *ptr, const uint32_t v) {
- *ptr++ = (uint8_t) (v >> 24) & 0xFF;
- *ptr++ = (uint8_t) (v >> 16) & 0xFF;
- *ptr++ = (uint8_t) (v >> 8) & 0xFF;
- *ptr++ = (uint8_t) (v >> 0) & 0xFF;
- return ptr;
+ *ptr++ = (uint8_t)(v >> 24) & 0xFF;
+ *ptr++ = (uint8_t)(v >> 16) & 0xFF;
+ *ptr++ = (uint8_t)(v >> 8) & 0xFF;
+ *ptr++ = (uint8_t)(v >> 0) & 0xFF;
+ return ptr;
}
uint16_t mdns_read_u16(const uint8_t *ptr) {
- return ((ptr[0] & 0xFF) << 8) |
- ((ptr[1] & 0xFF) << 0);
+ return ((ptr[0] & 0xFF) << 8) | ((ptr[1] & 0xFF) << 0);
}
uint32_t mdns_read_u32(const uint8_t *ptr) {
- return ((ptr[0] & 0xFF) << 24) |
- ((ptr[1] & 0xFF) << 16) |
- ((ptr[2] & 0xFF) << 8) |
- ((ptr[3] & 0xFF) << 0);
+ return ((ptr[0] & 0xFF) << 24) | ((ptr[1] & 0xFF) << 16) | ((ptr[2] & 0xFF) << 8) |
+ ((ptr[3] & 0xFF) << 0);
}
// initialize the packet for reply
// clears the packet of list structures but not its list items
void mdns_init_reply(struct mdns_pkt *pkt, uint16_t id) {
- // copy transaction ID
- pkt->id = id;
-
- // response flags
- pkt->flags = MDNS_FLAG_RESP | MDNS_FLAG_AA;
-
- rr_list_destroy(pkt->rr_qn, 0);
- rr_list_destroy(pkt->rr_ans, 0);
- rr_list_destroy(pkt->rr_auth, 0);
- rr_list_destroy(pkt->rr_add, 0);
-
- pkt->rr_qn = NULL;
- pkt->rr_ans = NULL;
- pkt->rr_auth = NULL;
- pkt->rr_add = NULL;
-
- pkt->num_qn = 0;
- pkt->num_ans_rr = 0;
- pkt->num_auth_rr = 0;
- pkt->num_add_rr = 0;
+ // copy transaction ID
+ pkt->id = id;
+
+ // response flags
+ pkt->flags = MDNS_FLAG_RESP | MDNS_FLAG_AA;
+
+ rr_list_destroy(pkt->rr_qn, 0);
+ rr_list_destroy(pkt->rr_ans, 0);
+ rr_list_destroy(pkt->rr_auth, 0);
+ rr_list_destroy(pkt->rr_add, 0);
+
+ pkt->rr_qn = NULL;
+ pkt->rr_ans = NULL;
+ pkt->rr_auth = NULL;
+ pkt->rr_add = NULL;
+
+ pkt->num_qn = 0;
+ pkt->num_ans_rr = 0;
+ pkt->num_auth_rr = 0;
+ pkt->num_add_rr = 0;
}
// destroys an mdns_pkt struct, including its contents
void mdns_pkt_destroy(struct mdns_pkt *p) {
- rr_list_destroy(p->rr_qn, 1);
- rr_list_destroy(p->rr_ans, 1);
- rr_list_destroy(p->rr_auth, 1);
- rr_list_destroy(p->rr_add, 1);
+ rr_list_destroy(p->rr_qn, 1);
+ rr_list_destroy(p->rr_ans, 1);
+ rr_list_destroy(p->rr_auth, 1);
+ rr_list_destroy(p->rr_add, 1);
- free(p);
+ free(p);
}
-
// parse the MDNS questions section
// stores the parsed data in the given mdns_pkt struct
-static size_t mdns_parse_qn(uint8_t *pkt_buf, size_t pkt_len, size_t off,
- struct mdns_pkt *pkt) {
- const uint8_t *p = pkt_buf + off;
- struct rr_entry *rr;
- uint8_t *name;
+static size_t mdns_parse_qn(uint8_t *pkt_buf, size_t pkt_len, size_t off, struct mdns_pkt *pkt) {
+ const uint8_t *p = pkt_buf + off;
+ struct rr_entry *rr;
+ uint8_t *name;
- assert(pkt != NULL);
+ assert(pkt != NULL);
- rr = malloc(sizeof(struct rr_entry));
- memset(rr, 0, sizeof(struct rr_entry));
+ rr = malloc(sizeof(struct rr_entry));
+ memset(rr, 0, sizeof(struct rr_entry));
- name = uncompress_nlabel(pkt_buf, pkt_len, off);
- p += label_len(pkt_buf, pkt_len, off);
- rr->name = name;
+ name = uncompress_nlabel(pkt_buf, pkt_len, off);
+ p += label_len(pkt_buf, pkt_len, off);
+ rr->name = name;
- rr->type = mdns_read_u16(p);
- p += sizeof(uint16_t);
+ rr->type = mdns_read_u16(p);
+ p += sizeof(uint16_t);
- rr->unicast_query = (*p & 0x80) == 0x80;
- rr->rr_class = mdns_read_u16(p) & ~0x80;
- p += sizeof(uint16_t);
+ rr->unicast_query = (*p & 0x80) == 0x80;
+ rr->rr_class = mdns_read_u16(p) & ~0x80;
+ p += sizeof(uint16_t);
- rr_list_append(&pkt->rr_qn, rr);
+ rr_list_append(&pkt->rr_qn, rr);
- return p - (pkt_buf + off);
+ return p - (pkt_buf + off);
}
// parse the MDNS RR section
// stores the parsed data in the given mdns_pkt struct
-static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
- struct mdns_pkt *pkt) {
- const uint8_t *p = pkt_buf + off;
- const uint8_t *e = pkt_buf + pkt_len;
- struct rr_entry *rr;
- uint8_t *name;
- size_t rr_data_len = 0;
- struct rr_data_txt *txt_rec;
- int parse_error = 0;
-
- assert(pkt != NULL);
-
- if (off > pkt_len)
- return 0;
-
- rr = malloc(sizeof(struct rr_entry));
- memset(rr, 0, sizeof(struct rr_entry));
-
- name = uncompress_nlabel(pkt_buf, pkt_len, off);
- p += label_len(pkt_buf, pkt_len, off);
- rr->name = name;
-
- rr->type = mdns_read_u16(p);
- p += sizeof(uint16_t);
-
- rr->cache_flush = (*p & 0x80) == 0x80;
- rr->rr_class = mdns_read_u16(p) & ~0x80;
- p += sizeof(uint16_t);
-
- rr->ttl = mdns_read_u32(p);
- p += sizeof(uint32_t);
-
- // RR data
- rr_data_len = mdns_read_u16(p);
- p += sizeof(uint16_t);
-
- if (p + rr_data_len > e) {
- DEBUG_PRINTF("rr_data_len goes beyond packet buffer: %lu > %lu\n", rr_data_len, e - p);
- rr_entry_destroy(rr);
- return 0;
- }
-
- e = p + rr_data_len;
-
- // see if we can parse the RR data
- switch (rr->type) {
- case RR_A:
- if (rr_data_len < sizeof(uint32_t)) {
- DEBUG_PRINTF("invalid rr_data_len=%lu for A record\n", rr_data_len);
- parse_error = 1;
- break;
- }
- rr->data.A.addr = ntohl(mdns_read_u32(p)); /* addr already in net order */
- p += sizeof(uint32_t);
- break;
-
- case RR_AAAA:
- if (rr_data_len < sizeof(struct in6_addr)) {
- DEBUG_PRINTF("invalid rr_data_len=%lu for AAAA record\n", rr_data_len);
- parse_error = 1;
- break;
- }
- rr->data.AAAA.addr = malloc(sizeof(struct in6_addr));
- int i;
- for (i = 0; i < sizeof(struct in6_addr); i++)
- rr->data.AAAA.addr->s6_addr[i] = p[i];
- p += sizeof(struct in6_addr);
- break;
-
- case RR_PTR:
- rr->data.PTR.name = uncompress_nlabel(pkt_buf, pkt_len, p - pkt_buf);
- if (rr->data.PTR.name == NULL) {
- DEBUG_PRINTF("unable to parse/uncompress label for PTR name\n");
- parse_error = 1;
- break;
- }
- p += rr_data_len;
- break;
-
- case RR_TXT:
- txt_rec = &rr->data.TXT;
-
- // not supposed to happen, but we should handle it
- if (rr_data_len == 0) {
- DEBUG_PRINTF("WARN: rr_data_len for TXT is 0\n");
- txt_rec->txt = create_label("");
- break;
- }
-
- while (1) {
- txt_rec->txt = copy_label(pkt_buf, pkt_len, p - pkt_buf);
- if (txt_rec->txt == NULL) {
- DEBUG_PRINTF("unable to copy label for TXT record\n");
- parse_error = 1;
- break;
- }
- p += txt_rec->txt[0] + 1;
-
- if (p >= e)
- break;
-
- // allocate another record
- txt_rec->next = malloc(sizeof(struct rr_data_txt));
- txt_rec = txt_rec->next;
- txt_rec->next = NULL;
- }
- break;
-
- default:
- // skip to end of RR data
- p = e;
- }
-
- // if there was a parse error, destroy partial rr_entry
- if (parse_error) {
- rr_entry_destroy(rr);
- return 0;
- }
-
- rr_list_append(&pkt->rr_ans, rr);
-
- return p - (pkt_buf + off);
+static size_t mdns_parse_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off, struct mdns_pkt *pkt) {
+ const uint8_t *p = pkt_buf + off;
+ const uint8_t *e = pkt_buf + pkt_len;
+ struct rr_entry *rr;
+ uint8_t *name;
+ size_t rr_data_len = 0;
+ struct rr_data_txt *txt_rec;
+ int parse_error = 0;
+
+ assert(pkt != NULL);
+
+ if (off > pkt_len)
+ return 0;
+
+ rr = malloc(sizeof(struct rr_entry));
+ memset(rr, 0, sizeof(struct rr_entry));
+
+ name = uncompress_nlabel(pkt_buf, pkt_len, off);
+ p += label_len(pkt_buf, pkt_len, off);
+ rr->name = name;
+
+ rr->type = mdns_read_u16(p);
+ p += sizeof(uint16_t);
+
+ rr->cache_flush = (*p & 0x80) == 0x80;
+ rr->rr_class = mdns_read_u16(p) & ~0x80;
+ p += sizeof(uint16_t);
+
+ rr->ttl = mdns_read_u32(p);
+ p += sizeof(uint32_t);
+
+ // RR data
+ rr_data_len = mdns_read_u16(p);
+ p += sizeof(uint16_t);
+
+ if (p + rr_data_len > e) {
+ DEBUG_PRINTF("rr_data_len goes beyond packet buffer: %lu > %lu\n", rr_data_len, e - p);
+ rr_entry_destroy(rr);
+ return 0;
+ }
+
+ e = p + rr_data_len;
+
+ // see if we can parse the RR data
+ switch (rr->type) {
+ case RR_A:
+ if (rr_data_len < sizeof(uint32_t)) {
+ DEBUG_PRINTF("invalid rr_data_len=%lu for A record\n", rr_data_len);
+ parse_error = 1;
+ break;
+ }
+ rr->data.A.addr = ntohl(mdns_read_u32(p)); /* addr already in net order */
+ p += sizeof(uint32_t);
+ break;
+
+ case RR_AAAA:
+ if (rr_data_len < sizeof(struct in6_addr)) {
+ DEBUG_PRINTF("invalid rr_data_len=%lu for AAAA record\n", rr_data_len);
+ parse_error = 1;
+ break;
+ }
+ rr->data.AAAA.addr = malloc(sizeof(struct in6_addr));
+ int i;
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ rr->data.AAAA.addr->s6_addr[i] = p[i];
+ p += sizeof(struct in6_addr);
+ break;
+
+ case RR_PTR:
+ rr->data.PTR.name = uncompress_nlabel(pkt_buf, pkt_len, p - pkt_buf);
+ if (rr->data.PTR.name == NULL) {
+ DEBUG_PRINTF("unable to parse/uncompress label for PTR name\n");
+ parse_error = 1;
+ break;
+ }
+ p += rr_data_len;
+ break;
+
+ case RR_TXT:
+ txt_rec = &rr->data.TXT;
+
+ // not supposed to happen, but we should handle it
+ if (rr_data_len == 0) {
+ DEBUG_PRINTF("WARN: rr_data_len for TXT is 0\n");
+ txt_rec->txt = create_label("");
+ break;
+ }
+
+ while (1) {
+ txt_rec->txt = copy_label(pkt_buf, pkt_len, p - pkt_buf);
+ if (txt_rec->txt == NULL) {
+ DEBUG_PRINTF("unable to copy label for TXT record\n");
+ parse_error = 1;
+ break;
+ }
+ p += txt_rec->txt[0] + 1;
+
+ if (p >= e)
+ break;
+
+ // allocate another record
+ txt_rec->next = malloc(sizeof(struct rr_data_txt));
+ txt_rec = txt_rec->next;
+ txt_rec->next = NULL;
+ }
+ break;
+
+ default:
+ // skip to end of RR data
+ p = e;
+ }
+
+ // if there was a parse error, destroy partial rr_entry
+ if (parse_error) {
+ rr_entry_destroy(rr);
+ return 0;
+ }
+
+ rr_list_append(&pkt->rr_ans, rr);
+
+ return p - (pkt_buf + off);
}
// parse a MDNS packet into an mdns_pkt struct
struct mdns_pkt *mdns_parse_pkt(uint8_t *pkt_buf, size_t pkt_len) {
- uint8_t *p = pkt_buf;
- size_t off;
- struct mdns_pkt *pkt;
- int i;
-
- if (pkt_len < 12)
- return NULL;
-
- MALLOC_ZERO_STRUCT(pkt, mdns_pkt);
-
- // parse header
- pkt->id = mdns_read_u16(p); p += sizeof(uint16_t);
- pkt->flags = mdns_read_u16(p); p += sizeof(uint16_t);
- pkt->num_qn = mdns_read_u16(p); p += sizeof(uint16_t);
- pkt->num_ans_rr = mdns_read_u16(p); p += sizeof(uint16_t);
- pkt->num_auth_rr = mdns_read_u16(p); p += sizeof(uint16_t);
- pkt->num_add_rr = mdns_read_u16(p); p += sizeof(uint16_t);
-
- off = p - pkt_buf;
-
- // parse questions
- for (i = 0; i < pkt->num_qn; i++) {
- size_t l = mdns_parse_qn(pkt_buf, pkt_len, off, pkt);
- if (! l) {
- DEBUG_PRINTF("error parsing question #%d\n", i);
- mdns_pkt_destroy(pkt);
- return NULL;
- }
-
- off += l;
+ uint8_t *p = pkt_buf;
+ size_t off;
+ struct mdns_pkt *pkt;
+ int i;
+
+ if (pkt_len < 12)
+ return NULL;
+
+ MALLOC_ZERO_STRUCT(pkt, mdns_pkt);
+
+ // parse header
+ pkt->id = mdns_read_u16(p);
+ p += sizeof(uint16_t);
+ pkt->flags = mdns_read_u16(p);
+ p += sizeof(uint16_t);
+ pkt->num_qn = mdns_read_u16(p);
+ p += sizeof(uint16_t);
+ pkt->num_ans_rr = mdns_read_u16(p);
+ p += sizeof(uint16_t);
+ pkt->num_auth_rr = mdns_read_u16(p);
+ p += sizeof(uint16_t);
+ pkt->num_add_rr = mdns_read_u16(p);
+ p += sizeof(uint16_t);
+
+ off = p - pkt_buf;
+
+ // parse questions
+ for (i = 0; i < pkt->num_qn; i++) {
+ size_t l = mdns_parse_qn(pkt_buf, pkt_len, off, pkt);
+ if (!l) {
+ DEBUG_PRINTF("error parsing question #%d\n", i);
+ mdns_pkt_destroy(pkt);
+ return NULL;
+ }
+
+ off += l;
+ }
+
+ // parse answer RRs
+ for (i = 0; i < pkt->num_ans_rr; i++) {
+ size_t l = mdns_parse_rr(pkt_buf, pkt_len, off, pkt);
+ if (!l) {
+ DEBUG_PRINTF("error parsing answer #%d\n", i);
+ mdns_pkt_destroy(pkt);
+ return NULL;
+ }
+
+ off += l;
+ }
+
+ // TODO: parse the authority and additional RR sections
+
+ return pkt;
+}
+
+// encodes a name (label) into a packet using the name compression scheme
+// encoded names will be added to the compression list for subsequent use
+static size_t mdns_encode_name(uint8_t *pkt_buf, size_t pkt_len, size_t off, const uint8_t *name,
+ struct name_comp *comp) {
+ struct name_comp *c, *c_tail = NULL;
+ uint8_t *p = pkt_buf + off;
+ size_t len = 0;
+
+ if (name) {
+ while (*name) {
+ // find match for compression
+ for (c = comp; c; c = c->next) {
+ if (cmp_nlabel(name, c->label) == 0) {
+ mdns_write_u16(p, 0xC000 | (c->pos & ~0xC000));
+ return len + sizeof(uint16_t);
}
- // parse answer RRs
- for (i = 0; i < pkt->num_ans_rr; i++) {
- size_t l = mdns_parse_rr(pkt_buf, pkt_len, off, pkt);
- if (! l) {
- DEBUG_PRINTF("error parsing answer #%d\n", i);
- mdns_pkt_destroy(pkt);
- return NULL;
- }
+ if (c->next == NULL)
+ c_tail = c;
+ }
- off += l;
- }
+ // copy this segment
+ int segment_len = *name + 1;
+ strncpy((char *)p, (char *)name, segment_len);
- // TODO: parse the authority and additional RR sections
+ // cache the name for subsequent compression
+ DECL_MALLOC_ZERO_STRUCT(new_c, name_comp);
- return pkt;
-}
+ new_c->label = (uint8_t *)name;
+ new_c->pos = p - pkt_buf;
+ c_tail->next = new_c;
-// encodes a name (label) into a packet using the name compression scheme
-// encoded names will be added to the compression list for subsequent use
-static size_t mdns_encode_name(uint8_t *pkt_buf, size_t pkt_len, size_t off,
- const uint8_t *name, struct name_comp *comp) {
- struct name_comp *c, *c_tail = NULL;
- uint8_t *p = pkt_buf + off;
- size_t len = 0;
-
- if (name) {
- while (*name) {
- // find match for compression
- for (c = comp; c; c = c->next) {
- if (cmp_nlabel(name, c->label) == 0) {
- mdns_write_u16(p, 0xC000 | (c->pos & ~0xC000));
- return len + sizeof(uint16_t);
- }
-
- if (c->next == NULL)
- c_tail = c;
- }
-
- // copy this segment
- int segment_len = *name + 1;
- strncpy((char *) p, (char *) name, segment_len);
-
- // cache the name for subsequent compression
- DECL_MALLOC_ZERO_STRUCT(new_c, name_comp);
-
- new_c->label = (uint8_t *) name;
- new_c->pos = p - pkt_buf;
- c_tail->next = new_c;
-
- // advance to next name segment
- p += segment_len;
- len += segment_len;
- name += segment_len;
- }
- }
+ // advance to next name segment
+ p += segment_len;
+ len += segment_len;
+ name += segment_len;
+ }
+ }
- *p = '\0'; // root "label"
- len += 1;
+ *p = '\0'; // root "label"
+ len += 1;
- return len;
+ return len;
}
// encodes an RR entry at the given offset
// returns the size of the entire RR entry
-static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off,
- struct rr_entry *rr, struct name_comp *comp) {
- uint8_t *p = pkt_buf + off, *p_data;
- size_t l;
- struct rr_data_txt *txt_rec;
- uint8_t *label;
- int i;
+static size_t mdns_encode_rr(uint8_t *pkt_buf, size_t pkt_len, size_t off, struct rr_entry *rr,
+ struct name_comp *comp) {
+ uint8_t *p = pkt_buf + off, *p_data;
+ size_t l;
+ struct rr_data_txt *txt_rec;
+ uint8_t *label;
+ int i;
- assert(off < pkt_len);
+ assert(off < pkt_len);
- // name
- l = mdns_encode_name(pkt_buf, pkt_len, off, rr->name, comp);
- assert(l != 0);
- p += l;
+ // name
+ l = mdns_encode_name(pkt_buf, pkt_len, off, rr->name, comp);
+ assert(l != 0);
+ p += l;
- // type
- p = mdns_write_u16(p, rr->type);
+ // type
+ p = mdns_write_u16(p, rr->type);
- // class & cache flush
- p = mdns_write_u16(p, (rr->rr_class & ~0x8000) | (rr->cache_flush << 15));
+ // class & cache flush
+ p = mdns_write_u16(p, (rr->rr_class & ~0x8000) | (rr->cache_flush << 15));
- // TTL
- p = mdns_write_u32(p, rr->ttl);
+ // TTL
+ p = mdns_write_u32(p, rr->ttl);
- // data length (filled in later)
- p += sizeof(uint16_t);
+ // data length (filled in later)
+ p += sizeof(uint16_t);
- // start of data marker
- p_data = p;
+ // start of data marker
+ p_data = p;
- switch (rr->type) {
- case RR_A:
- /* htonl() needed coz addr already in net order */
- p = mdns_write_u32(p, htonl(rr->data.A.addr));
- break;
+ switch (rr->type) {
+ case RR_A:
+ /* htonl() needed coz addr already in net order */
+ p = mdns_write_u32(p, htonl(rr->data.A.addr));
+ break;
- case RR_AAAA:
- for (i = 0; i < sizeof(struct in6_addr); i++)
- *p++ = rr->data.AAAA.addr->s6_addr[i];
- break;
+ case RR_AAAA:
+ for (i = 0; i < sizeof(struct in6_addr); i++)
+ *p++ = rr->data.AAAA.addr->s6_addr[i];
+ break;
- case RR_PTR:
- label = rr->data.PTR.name ?
- rr->data.PTR.name :
- rr->data.PTR.entry->name;
- p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, label, comp);
- break;
+ case RR_PTR:
+ label = rr->data.PTR.name ? rr->data.PTR.name : rr->data.PTR.entry->name;
+ p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, label, comp);
+ break;
- case RR_TXT:
- txt_rec = &rr->data.TXT;
- for (; txt_rec; txt_rec = txt_rec->next) {
- int len = txt_rec->txt[0] + 1;
- strncpy((char *) p, (char *) txt_rec->txt, len);
- p += len;
- }
- break;
+ case RR_TXT:
+ txt_rec = &rr->data.TXT;
+ for (; txt_rec; txt_rec = txt_rec->next) {
+ int len = txt_rec->txt[0] + 1;
+ strncpy((char *)p, (char *)txt_rec->txt, len);
+ p += len;
+ }
+ break;
- case RR_SRV:
- p = mdns_write_u16(p, rr->data.SRV.priority);
+ case RR_SRV:
+ p = mdns_write_u16(p, rr->data.SRV.priority);
- p = mdns_write_u16(p, rr->data.SRV.weight);
+ p = mdns_write_u16(p, rr->data.SRV.weight);
- p = mdns_write_u16(p, rr->data.SRV.port);
+ p = mdns_write_u16(p, rr->data.SRV.port);
- p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf,
- rr->data.SRV.target, comp);
- break;
+ p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, rr->data.SRV.target, comp);
+ break;
- case RR_NSEC:
- p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf,
- rr->name, comp);
+ case RR_NSEC:
+ p += mdns_encode_name(pkt_buf, pkt_len, p - pkt_buf, rr->name, comp);
- *p++ = 0; // bitmap window/block number
+ *p++ = 0; // bitmap window/block number
- *p++ = sizeof(rr->data.NSEC.bitmap); // bitmap length
+ *p++ = sizeof(rr->data.NSEC.bitmap); // bitmap length
- for (i = 0; i < sizeof(rr->data.NSEC.bitmap); i++)
- *p++ = rr->data.NSEC.bitmap[i];
+ for (i = 0; i < sizeof(rr->data.NSEC.bitmap); i++)
+ *p++ = rr->data.NSEC.bitmap[i];
- break;
+ break;
- default:
- DEBUG_PRINTF("unhandled rr type 0x%02x\n", rr->type);
- }
+ default:
+ DEBUG_PRINTF("unhandled rr type 0x%02x\n", rr->type);
+ }
- // calculate data length based on p
- l = p - p_data;
+ // calculate data length based on p
+ l = p - p_data;
- // fill in the length
- mdns_write_u16(p - l - sizeof(uint16_t), l);
+ // fill in the length
+ mdns_write_u16(p - l - sizeof(uint16_t), l);
- return p - pkt_buf - off;
+ return p - pkt_buf - off;
}
// encodes a MDNS packet from the given mdns_pkt struct into a buffer
// returns the size of the entire MDNS packet
size_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len) {
- struct name_comp *comp;
- uint8_t *p = pkt_buf;
- //uint8_t *e = pkt_buf + pkt_len;
- size_t off;
- int i;
-
- assert(answer != NULL);
- assert(pkt_len >= 12);
-
- if (p == NULL)
- return -1;
-
- // this is an Answer - number of qns should be zero
- assert(answer->num_qn == 0);
-
- p = mdns_write_u16(p, answer->id);
- p = mdns_write_u16(p, answer->flags);
- p = mdns_write_u16(p, answer->num_qn);
- p = mdns_write_u16(p, answer->num_ans_rr);
- p = mdns_write_u16(p, answer->num_auth_rr);
- p = mdns_write_u16(p, answer->num_add_rr);
-
- off = p - pkt_buf;
-
- // allocate list for name compression
- comp = malloc(sizeof(struct name_comp));
- if (comp == NULL)
- return -1;
- memset(comp, 0, sizeof(struct name_comp));
-
- // dummy entry
- comp->label = (uint8_t *) "";
- comp->pos = 0;
-
- // skip encoding of qn
-
- struct rr_list *rr_set[] = {
- answer->rr_ans,
- answer->rr_auth,
- answer->rr_add
- };
-
- // encode answer, authority and additional RRs
- for (i = 0; i < sizeof(rr_set) / sizeof(rr_set[0]); i++) {
- struct rr_list *rr = rr_set[i];
- for (; rr; rr = rr->next) {
- size_t l = mdns_encode_rr(pkt_buf, pkt_len, off, rr->e, comp);
- off += l;
-
- if (off >= pkt_len) {
- DEBUG_PRINTF("packet buffer too small\n");
- return -1;
- }
- }
-
- }
-
- // free name compression list
- while (comp) {
- struct name_comp *c = comp->next;
- free(comp);
- comp = c;
- }
-
- return off;
+ struct name_comp *comp;
+ uint8_t *p = pkt_buf;
+ // uint8_t *e = pkt_buf + pkt_len;
+ size_t off;
+ int i;
+
+ assert(answer != NULL);
+ assert(pkt_len >= 12);
+
+ if (p == NULL)
+ return -1;
+
+ // this is an Answer - number of qns should be zero
+ assert(answer->num_qn == 0);
+
+ p = mdns_write_u16(p, answer->id);
+ p = mdns_write_u16(p, answer->flags);
+ p = mdns_write_u16(p, answer->num_qn);
+ p = mdns_write_u16(p, answer->num_ans_rr);
+ p = mdns_write_u16(p, answer->num_auth_rr);
+ p = mdns_write_u16(p, answer->num_add_rr);
+
+ off = p - pkt_buf;
+
+ // allocate list for name compression
+ comp = malloc(sizeof(struct name_comp));
+ if (comp == NULL)
+ return -1;
+ memset(comp, 0, sizeof(struct name_comp));
+
+ // dummy entry
+ comp->label = (uint8_t *)"";
+ comp->pos = 0;
+
+ // skip encoding of qn
+
+ struct rr_list *rr_set[] = {answer->rr_ans, answer->rr_auth, answer->rr_add};
+
+ // encode answer, authority and additional RRs
+ for (i = 0; i < sizeof(rr_set) / sizeof(rr_set[0]); i++) {
+ struct rr_list *rr = rr_set[i];
+ for (; rr; rr = rr->next) {
+ size_t l = mdns_encode_rr(pkt_buf, pkt_len, off, rr->e, comp);
+ off += l;
+
+ if (off >= pkt_len) {
+ DEBUG_PRINTF("packet buffer too small\n");
+ return -1;
+ }
+ }
+ }
+
+ // free name compression list
+ while (comp) {
+ struct name_comp *c = comp->next;
+ free(comp);
+ comp = c;
+ }
+
+ return off;
}
//******************************************************//
@@ -1064,621 +1063,599 @@ size_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len
#define PACKET_SIZE 65536
-#define SERVICES_DNS_SD_NLABEL \
- ((uint8_t *) "\x09_services\x07_dns-sd\x04_udp\x05local")
+#define SERVICES_DNS_SD_NLABEL ((uint8_t *)"\x09_services\x07_dns-sd\x04_udp\x05local")
struct mdnsd {
- pthread_mutex_t data_lock;
- int sockfd;
- int notify_pipe[2];
- int stop_flag;
-
- struct rr_group *group;
- struct rr_list *announce;
- struct rr_list *services;
- uint8_t *hostname;
+ pthread_mutex_t data_lock;
+ int sockfd;
+ int notify_pipe[2];
+ int stop_flag;
+
+ struct rr_group *group;
+ struct rr_list *announce;
+ struct rr_list *services;
+ uint8_t *hostname;
};
struct mdns_service {
- struct rr_list *entries;
+ struct rr_list *entries;
};
/////////////////////////////////
-
-
static int create_recv_sock() {
- int sd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sd < 0) {
- log_message(LOG_ERR, "recv socket(): %m");
- return sd;
- }
-
- int r = -1;
-
- int on = 1;
- if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on))) < 0) {
- log_message(LOG_ERR, "recv setsockopt(SO_REUSEADDR): %m");
- return r;
- }
-
- /* bind to an address */
- struct sockaddr_in serveraddr;
- memset(&serveraddr, 0, sizeof(serveraddr));
- serveraddr.sin_family = AF_INET;
- serveraddr.sin_port = htons(MDNS_PORT);
- serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* receive multicast */
- if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) {
- log_message(LOG_ERR, "recv bind(): %m");
- }
-
- // add membership to receiving socket
- struct ip_mreq mreq;
- memset(&mreq, 0, sizeof(struct ip_mreq));
- mreq.imr_interface.s_addr = htonl(INADDR_ANY);
- mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR);
- if ((r = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq))) < 0) {
- log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %m");
- return r;
- }
-
- // enable loopback in case someone else needs the data
- if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &on, sizeof(on))) < 0) {
- log_message(LOG_ERR, "recv setsockopt(IP_MULTICAST_LOOP): %m");
- return r;
- }
-
+ int sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0) {
+ log_message(LOG_ERR, "recv socket(): %m");
+ return sd;
+ }
+
+ int r = -1;
+
+ int on = 1;
+ if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) < 0) {
+ log_message(LOG_ERR, "recv setsockopt(SO_REUSEADDR): %m");
+ return r;
+ }
+
+ /* bind to an address */
+ struct sockaddr_in serveraddr;
+ memset(&serveraddr, 0, sizeof(serveraddr));
+ serveraddr.sin_family = AF_INET;
+ serveraddr.sin_port = htons(MDNS_PORT);
+ serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* receive multicast */
+ if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) {
+ log_message(LOG_ERR, "recv bind(): %m");
+ }
+
+ // add membership to receiving socket
+ struct ip_mreq mreq;
+ memset(&mreq, 0, sizeof(struct ip_mreq));
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR);
+ if ((r = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq))) < 0) {
+ log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %m");
+ return r;
+ }
+
+ // enable loopback in case someone else needs the data
+ if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&on, sizeof(on))) < 0) {
+ log_message(LOG_ERR, "recv setsockopt(IP_MULTICAST_LOOP): %m");
+ return r;
+ }
#ifdef IP_PKTINFO
- if ((r = setsockopt(sd, SOL_IP, IP_PKTINFO, (char *) &on, sizeof(on))) < 0) {
- log_message(LOG_ERR, "recv setsockopt(IP_PKTINFO): %m");
- return r;
- }
+ if ((r = setsockopt(sd, SOL_IP, IP_PKTINFO, (char *)&on, sizeof(on))) < 0) {
+ log_message(LOG_ERR, "recv setsockopt(IP_PKTINFO): %m");
+ return r;
+ }
#endif
- return sd;
+ return sd;
}
static ssize_t send_packet(int fd, const void *data, size_t len) {
- static struct sockaddr_in toaddr;
- if (toaddr.sin_family != AF_INET) {
- memset(&toaddr, 0, sizeof(struct sockaddr_in));
- toaddr.sin_family = AF_INET;
- toaddr.sin_port = htons(MDNS_PORT);
- toaddr.sin_addr.s_addr = inet_addr(MDNS_ADDR);
- }
-
- return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in));
+ static struct sockaddr_in toaddr;
+ if (toaddr.sin_family != AF_INET) {
+ memset(&toaddr, 0, sizeof(struct sockaddr_in));
+ toaddr.sin_family = AF_INET;
+ toaddr.sin_port = htons(MDNS_PORT);
+ toaddr.sin_addr.s_addr = inet_addr(MDNS_ADDR);
+ }
+
+ return sendto(fd, data, len, 0, (struct sockaddr *)&toaddr, sizeof(struct sockaddr_in));
}
-
// populate the specified list which matches the RR name and type
// type can be RR_ANY, which populates all entries EXCEPT RR_NSEC
-static int populate_answers(struct mdnsd *svr, struct rr_list **rr_head, uint8_t *name, enum rr_type type) {
- int num_ans = 0;
-
- // check if we have the records
- pthread_mutex_lock(&svr->data_lock);
- struct rr_group *ans_grp = rr_group_find(svr->group, name);
- if (ans_grp == NULL) {
- pthread_mutex_unlock(&svr->data_lock);
- return num_ans;
- }
-
- // decide which records should go into answers
- struct rr_list *n = ans_grp->rr;
- for (; n; n = n->next) {
- // exclude NSEC for RR_ANY
- if (type == RR_ANY && n->e->type == RR_NSEC)
- continue;
-
- if ((type == n->e->type || type == RR_ANY) && cmp_nlabel(name, n->e->name) == 0) {
- num_ans += rr_list_append(rr_head, n->e);
- }
- }
-
- pthread_mutex_unlock(&svr->data_lock);
-
- return num_ans;
+static int populate_answers(struct mdnsd *svr, struct rr_list **rr_head, uint8_t *name,
+ enum rr_type type) {
+ int num_ans = 0;
+
+ // check if we have the records
+ pthread_mutex_lock(&svr->data_lock);
+ struct rr_group *ans_grp = rr_group_find(svr->group, name);
+ if (ans_grp == NULL) {
+ pthread_mutex_unlock(&svr->data_lock);
+ return num_ans;
+ }
+
+ // decide which records should go into answers
+ struct rr_list *n = ans_grp->rr;
+ for (; n; n = n->next) {
+ // exclude NSEC for RR_ANY
+ if (type == RR_ANY && n->e->type == RR_NSEC)
+ continue;
+
+ if ((type == n->e->type || type == RR_ANY) && cmp_nlabel(name, n->e->name) == 0) {
+ num_ans += rr_list_append(rr_head, n->e);
+ }
+ }
+
+ pthread_mutex_unlock(&svr->data_lock);
+
+ return num_ans;
}
// given a list of RRs, look up related records and add them
static void add_related_rr(struct mdnsd *svr, struct rr_list *list, struct mdns_pkt *reply) {
- for (; list; list = list->next) {
- struct rr_entry *ans = list->e;
-
- switch (ans->type) {
- case RR_PTR:
- // target host A, AAAA records
- reply->num_add_rr += populate_answers(svr, &reply->rr_add,
- MDNS_RR_GET_PTR_NAME(ans), RR_ANY);
- break;
-
- case RR_SRV:
- // target host A, AAAA records
- reply->num_add_rr += populate_answers(svr, &reply->rr_add,
- ans->data.SRV.target, RR_ANY);
-
- // perhaps TXT records of the same name?
- // if we use RR_ANY, we risk pulling in the same RR_SRV
- reply->num_add_rr += populate_answers(svr, &reply->rr_add,
- ans->name, RR_TXT);
- break;
-
- case RR_A:
- case RR_AAAA:
- reply->num_add_rr += populate_answers(svr, &reply->rr_add,
- ans->name, RR_NSEC);
- break;
-
- default:
- // nothing to add
- break;
- }
- }
+ for (; list; list = list->next) {
+ struct rr_entry *ans = list->e;
+
+ switch (ans->type) {
+ case RR_PTR:
+ // target host A, AAAA records
+ reply->num_add_rr += populate_answers(svr, &reply->rr_add, MDNS_RR_GET_PTR_NAME(ans), RR_ANY);
+ break;
+
+ case RR_SRV:
+ // target host A, AAAA records
+ reply->num_add_rr += populate_answers(svr, &reply->rr_add, ans->data.SRV.target, RR_ANY);
+
+ // perhaps TXT records of the same name?
+ // if we use RR_ANY, we risk pulling in the same RR_SRV
+ reply->num_add_rr += populate_answers(svr, &reply->rr_add, ans->name, RR_TXT);
+ break;
+
+ case RR_A:
+ case RR_AAAA:
+ reply->num_add_rr += populate_answers(svr, &reply->rr_add, ans->name, RR_NSEC);
+ break;
+
+ default:
+ // nothing to add
+ break;
+ }
+ }
}
// creates an announce packet given the type name PTR
static void announce_srv(struct mdnsd *svr, struct mdns_pkt *reply, uint8_t *name) {
- mdns_init_reply(reply, 0);
+ mdns_init_reply(reply, 0);
- reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, name, RR_PTR);
+ reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, name, RR_PTR);
- // remember to add the services dns-sd PTR too
- reply->num_ans_rr += populate_answers(svr, &reply->rr_ans,
- SERVICES_DNS_SD_NLABEL, RR_PTR);
+ // remember to add the services dns-sd PTR too
+ reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, SERVICES_DNS_SD_NLABEL, RR_PTR);
- // see if we can match additional records for answers
- add_related_rr(svr, reply->rr_ans, reply);
+ // see if we can match additional records for answers
+ add_related_rr(svr, reply->rr_ans, reply);
- // additional records for additional records
- add_related_rr(svr, reply->rr_add, reply);
+ // additional records for additional records
+ add_related_rr(svr, reply->rr_add, reply);
}
// processes the incoming MDNS packet
// returns >0 if processed, 0 otherwise
static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns_pkt *reply) {
- int i;
-
- assert(pkt != NULL);
+ int i;
- // is it standard query?
- if ((pkt->flags & MDNS_FLAG_RESP) == 0 &&
- MDNS_FLAG_GET_OPCODE(pkt->flags) == 0) {
- mdns_init_reply(reply, pkt->id);
+ assert(pkt != NULL);
- DEBUG_PRINTF("flags = %04x, qn = %d, ans = %d, add = %d\n",
- pkt->flags,
- pkt->num_qn,
- pkt->num_ans_rr,
- pkt->num_add_rr);
+ // is it standard query?
+ if ((pkt->flags & MDNS_FLAG_RESP) == 0 && MDNS_FLAG_GET_OPCODE(pkt->flags) == 0) {
+ mdns_init_reply(reply, pkt->id);
- // loop through questions
- struct rr_list *qnl = pkt->rr_qn;
- for (i = 0; i < pkt->num_qn; i++, qnl = qnl->next) {
- struct rr_entry *qn = qnl->e;
- int num_ans_added = 0;
+ DEBUG_PRINTF("flags = %04x, qn = %d, ans = %d, add = %d\n", pkt->flags, pkt->num_qn,
+ pkt->num_ans_rr, pkt->num_add_rr);
- char *namestr = nlabel_to_str(qn->name);
- DEBUG_PRINTF("qn #%d: type %s (%02x) %s - ", i, rr_get_type_name(qn->type), qn->type, namestr);
- free(namestr);
+ // loop through questions
+ struct rr_list *qnl = pkt->rr_qn;
+ for (i = 0; i < pkt->num_qn; i++, qnl = qnl->next) {
+ struct rr_entry *qn = qnl->e;
+ int num_ans_added = 0;
- // check if it's a unicast query - we ignore those
- if (qn->unicast_query) {
- DEBUG_PRINTF("skipping unicast query\n");
- continue;
- }
+ char *namestr = nlabel_to_str(qn->name);
+ DEBUG_PRINTF("qn #%d: type %s (%02x) %s - ", i, rr_get_type_name(qn->type), qn->type,
+ namestr);
+ free(namestr);
- num_ans_added = populate_answers(svr, &reply->rr_ans, qn->name, qn->type);
- reply->num_ans_rr += num_ans_added;
+ // check if it's a unicast query - we ignore those
+ if (qn->unicast_query) {
+ DEBUG_PRINTF("skipping unicast query\n");
+ continue;
+ }
- DEBUG_PRINTF("added %d answers\n", num_ans_added);
- }
+ num_ans_added = populate_answers(svr, &reply->rr_ans, qn->name, qn->type);
+ reply->num_ans_rr += num_ans_added;
- // remove our replies if they were already in their answers
- struct rr_list *ans = NULL, *prev_ans = NULL;
- for (ans = reply->rr_ans; ans; ) {
- struct rr_list *next_ans = ans->next;
- struct rr_entry *known_ans = rr_entry_match(pkt->rr_ans, ans->e);
+ DEBUG_PRINTF("added %d answers\n", num_ans_added);
+ }
- // discard answers that have at least half of the actual TTL
- if (known_ans != NULL && known_ans->ttl >= ans->e->ttl / 2) {
- char *namestr = nlabel_to_str(ans->e->name);
- DEBUG_PRINTF("removing answer for %s\n", namestr);
- free(namestr);
+ // remove our replies if they were already in their answers
+ struct rr_list *ans = NULL, *prev_ans = NULL;
+ for (ans = reply->rr_ans; ans;) {
+ struct rr_list *next_ans = ans->next;
+ struct rr_entry *known_ans = rr_entry_match(pkt->rr_ans, ans->e);
- // check if list item is head
- if (prev_ans == NULL)
- reply->rr_ans = ans->next;
- else
- prev_ans->next = ans->next;
- free(ans);
+ // discard answers that have at least half of the actual TTL
+ if (known_ans != NULL && known_ans->ttl >= ans->e->ttl / 2) {
+ char *namestr = nlabel_to_str(ans->e->name);
+ DEBUG_PRINTF("removing answer for %s\n", namestr);
+ free(namestr);
- ans = prev_ans;
+ // check if list item is head
+ if (prev_ans == NULL)
+ reply->rr_ans = ans->next;
+ else
+ prev_ans->next = ans->next;
+ free(ans);
- // adjust answer count
- reply->num_ans_rr--;
- }
+ ans = prev_ans;
- prev_ans = ans;
- ans = next_ans;
- }
+ // adjust answer count
+ reply->num_ans_rr--;
+ }
+ prev_ans = ans;
+ ans = next_ans;
+ }
- // see if we can match additional records for answers
- add_related_rr(svr, reply->rr_ans, reply);
+ // see if we can match additional records for answers
+ add_related_rr(svr, reply->rr_ans, reply);
- // additional records for additional records
- add_related_rr(svr, reply->rr_add, reply);
+ // additional records for additional records
+ add_related_rr(svr, reply->rr_add, reply);
- DEBUG_PRINTF("\n");
+ DEBUG_PRINTF("\n");
- return reply->num_ans_rr;
- }
+ return reply->num_ans_rr;
+ }
- return 0;
+ return 0;
}
int create_pipe(int handles[2]) {
#ifdef _WIN32
- SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
- if (sock == INVALID_SOCKET) {
- return -1;
- }
- struct sockaddr_in serv_addr;
- memset(&serv_addr, 0, sizeof(serv_addr));
- serv_addr.sin_family = AF_INET;
- serv_addr.sin_port = htons(0);
- serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- if (bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) {
- closesocket(sock);
- return -1;
- }
- if (listen(sock, 1) == SOCKET_ERROR) {
- closesocket(sock);
- return -1;
- }
- int len = sizeof(serv_addr);
- if (getsockname(sock, (SOCKADDR*)&serv_addr, &len) == SOCKET_ERROR) {
- closesocket(sock);
- return -1;
- }
- if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
- closesocket(sock);
- return -1;
- }
- if (connect(handles[1], (struct sockaddr*)&serv_addr, len) == SOCKET_ERROR) {
- closesocket(sock);
- return -1;
- }
- if ((handles[0] = accept(sock, (struct sockaddr*)&serv_addr, &len)) == INVALID_SOCKET) {
- closesocket((SOCKET)handles[1]);
- handles[1] = INVALID_SOCKET;
- closesocket(sock);
- return -1;
- }
- closesocket(sock);
- return 0;
+ SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET) {
+ return -1;
+ }
+ struct sockaddr_in serv_addr;
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = htons(0);
+ serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) {
+ closesocket(sock);
+ return -1;
+ }
+ if (listen(sock, 1) == SOCKET_ERROR) {
+ closesocket(sock);
+ return -1;
+ }
+ int len = sizeof(serv_addr);
+ if (getsockname(sock, (SOCKADDR *)&serv_addr, &len) == SOCKET_ERROR) {
+ closesocket(sock);
+ return -1;
+ }
+ if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
+ closesocket(sock);
+ return -1;
+ }
+ if (connect(handles[1], (struct sockaddr *)&serv_addr, len) == SOCKET_ERROR) {
+ closesocket(sock);
+ return -1;
+ }
+ if ((handles[0] = accept(sock, (struct sockaddr *)&serv_addr, &len)) == INVALID_SOCKET) {
+ closesocket((SOCKET)handles[1]);
+ handles[1] = INVALID_SOCKET;
+ closesocket(sock);
+ return -1;
+ }
+ closesocket(sock);
+ return 0;
#else
- return pipe(handles);
+ return pipe(handles);
#endif
}
-int read_pipe(int s, char* buf, int len) {
+int read_pipe(int s, char *buf, int len) {
#ifdef _WIN32
- int ret = recv(s, buf, len, 0);
- if (ret < 0 && WSAGetLastError() == WSAECONNRESET) {
- ret = 0;
- }
- return ret;
+ int ret = recv(s, buf, len, 0);
+ if (ret < 0 && WSAGetLastError() == WSAECONNRESET) {
+ ret = 0;
+ }
+ return ret;
#else
- return read(s, buf, len);
+ return read(s, buf, len);
#endif
}
-int write_pipe(int s, char* buf, int len) {
+int write_pipe(int s, char *buf, int len) {
#ifdef _WIN32
- return send(s, buf, len, 0);
+ return send(s, buf, len, 0);
#else
- return write(s, buf, len);
+ return write(s, buf, len);
#endif
}
int close_pipe(int s) {
#ifdef _WIN32
- return closesocket(s);
+ return closesocket(s);
#else
- return close(s);
+ return close(s);
#endif
}
// main loop to receive, process and send out MDNS replies
// also handles MDNS service announces
static void main_loop(struct mdnsd *svr) {
- fd_set sockfd_set;
- int max_fd = svr->sockfd;
- char notify_buf[2]; // buffer for reading of notify_pipe
-
- void *pkt_buffer = malloc(PACKET_SIZE);
-
- if (svr->notify_pipe[0] > max_fd)
- max_fd = svr->notify_pipe[0];
-
- struct mdns_pkt *mdns_reply = malloc(sizeof(struct mdns_pkt));
- memset(mdns_reply, 0, sizeof(struct mdns_pkt));
-
- while (! svr->stop_flag) {
- FD_ZERO(&sockfd_set);
- FD_SET(svr->sockfd, &sockfd_set);
- FD_SET(svr->notify_pipe[0], &sockfd_set);
- select(max_fd + 1, &sockfd_set, NULL, NULL, NULL);
-
- if (FD_ISSET(svr->notify_pipe[0], &sockfd_set)) {
- // flush the notify_pipe
- read_pipe(svr->notify_pipe[0], (char*)&notify_buf, 1);
- } else if (FD_ISSET(svr->sockfd, &sockfd_set)) {
- struct sockaddr_in fromaddr;
- socklen_t sockaddr_size = sizeof(struct sockaddr_in);
-
- ssize_t recvsize = recvfrom(svr->sockfd, pkt_buffer, PACKET_SIZE, 0,
- (struct sockaddr *) &fromaddr, &sockaddr_size);
- if (recvsize < 0) {
- log_message(LOG_ERR, "recv(): %m");
- }
-
- DEBUG_PRINTF("data from=%s size=%ld\n", inet_ntoa(fromaddr.sin_addr), (long) recvsize);
- struct mdns_pkt *mdns = mdns_parse_pkt(pkt_buffer, recvsize);
- if (mdns != NULL) {
- if (process_mdns_pkt(svr, mdns, mdns_reply)) {
- size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
- send_packet(svr->sockfd, pkt_buffer, replylen);
- } else if (mdns->num_qn == 0) {
- DEBUG_PRINTF("(no questions in packet)\n\n");
- }
-
- mdns_pkt_destroy(mdns);
- }
- }
-
- // send out announces
- while (1) {
- struct rr_entry *ann_e = NULL;
-
- // extract from head of list
- pthread_mutex_lock(&svr->data_lock);
- if (svr->announce)
- ann_e = rr_list_remove(&svr->announce, svr->announce->e);
- pthread_mutex_unlock(&svr->data_lock);
-
- if (! ann_e)
- break;
-
- char *namestr = nlabel_to_str(ann_e->name);
- DEBUG_PRINTF("sending announce for %s\n", namestr);
- free(namestr);
-
- announce_srv(svr, mdns_reply, ann_e->name);
-
- if (mdns_reply->num_ans_rr > 0) {
- size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
- send_packet(svr->sockfd, pkt_buffer, replylen);
- }
- }
+ fd_set sockfd_set;
+ int max_fd = svr->sockfd;
+ char notify_buf[2]; // buffer for reading of notify_pipe
+
+ void *pkt_buffer = malloc(PACKET_SIZE);
+
+ if (svr->notify_pipe[0] > max_fd)
+ max_fd = svr->notify_pipe[0];
+
+ struct mdns_pkt *mdns_reply = malloc(sizeof(struct mdns_pkt));
+ memset(mdns_reply, 0, sizeof(struct mdns_pkt));
+
+ while (!svr->stop_flag) {
+ FD_ZERO(&sockfd_set);
+ FD_SET(svr->sockfd, &sockfd_set);
+ FD_SET(svr->notify_pipe[0], &sockfd_set);
+ select(max_fd + 1, &sockfd_set, NULL, NULL, NULL);
+
+ if (FD_ISSET(svr->notify_pipe[0], &sockfd_set)) {
+ // flush the notify_pipe
+ read_pipe(svr->notify_pipe[0], (char *)&notify_buf, 1);
+ } else if (FD_ISSET(svr->sockfd, &sockfd_set)) {
+ struct sockaddr_in fromaddr;
+ socklen_t sockaddr_size = sizeof(struct sockaddr_in);
+
+ ssize_t recvsize = recvfrom(svr->sockfd, pkt_buffer, PACKET_SIZE, 0,
+ (struct sockaddr *)&fromaddr, &sockaddr_size);
+ if (recvsize < 0) {
+ log_message(LOG_ERR, "recv(): %m");
+ }
+
+ DEBUG_PRINTF("data from=%s size=%ld\n", inet_ntoa(fromaddr.sin_addr), (long)recvsize);
+ struct mdns_pkt *mdns = mdns_parse_pkt(pkt_buffer, recvsize);
+ if (mdns != NULL) {
+ if (process_mdns_pkt(svr, mdns, mdns_reply)) {
+ size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
+ send_packet(svr->sockfd, pkt_buffer, replylen);
+ } else if (mdns->num_qn == 0) {
+ DEBUG_PRINTF("(no questions in packet)\n\n");
}
- // main thread terminating. send out "goodbye packets" for services
- mdns_init_reply(mdns_reply, 0);
+ mdns_pkt_destroy(mdns);
+ }
+ }
- pthread_mutex_lock(&svr->data_lock);
- struct rr_list *svc_le = svr->services;
- for (; svc_le; svc_le = svc_le->next) {
- // set TTL to zero
- svc_le->e->ttl = 0;
- mdns_reply->num_ans_rr += rr_list_append(&mdns_reply->rr_ans, svc_le->e);
- }
- pthread_mutex_unlock(&svr->data_lock);
+ // send out announces
+ while (1) {
+ struct rr_entry *ann_e = NULL;
- // send out packet
- if (mdns_reply->num_ans_rr > 0) {
- size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
- send_packet(svr->sockfd, pkt_buffer, replylen);
- }
+ // extract from head of list
+ pthread_mutex_lock(&svr->data_lock);
+ if (svr->announce)
+ ann_e = rr_list_remove(&svr->announce, svr->announce->e);
+ pthread_mutex_unlock(&svr->data_lock);
- // destroy packet
- mdns_init_reply(mdns_reply, 0);
- free(mdns_reply);
+ if (!ann_e)
+ break;
- free(pkt_buffer);
+ char *namestr = nlabel_to_str(ann_e->name);
+ DEBUG_PRINTF("sending announce for %s\n", namestr);
+ free(namestr);
- close_pipe(svr->sockfd);
+ announce_srv(svr, mdns_reply, ann_e->name);
- svr->stop_flag = 2;
-}
+ if (mdns_reply->num_ans_rr > 0) {
+ size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
+ send_packet(svr->sockfd, pkt_buffer, replylen);
+ }
+ }
+ }
-/////////////////////////////////////////////////////
+ // main thread terminating. send out "goodbye packets" for services
+ mdns_init_reply(mdns_reply, 0);
+ pthread_mutex_lock(&svr->data_lock);
+ struct rr_list *svc_le = svr->services;
+ for (; svc_le; svc_le = svc_le->next) {
+ // set TTL to zero
+ svc_le->e->ttl = 0;
+ mdns_reply->num_ans_rr += rr_list_append(&mdns_reply->rr_ans, svc_le->e);
+ }
+ pthread_mutex_unlock(&svr->data_lock);
-void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip) {
- struct rr_entry *a_e = NULL,
- *nsec_e = NULL;
+ // send out packet
+ if (mdns_reply->num_ans_rr > 0) {
+ size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
+ send_packet(svr->sockfd, pkt_buffer, replylen);
+ }
- // currently can't be called twice
- // dont ask me what happens if the IP changes
- assert(svr->hostname == NULL);
+ // destroy packet
+ mdns_init_reply(mdns_reply, 0);
+ free(mdns_reply);
- a_e = rr_create_a(create_nlabel(hostname), ip); // 120 seconds automatically
+ free(pkt_buffer);
- nsec_e = rr_create(create_nlabel(hostname), RR_NSEC);
- nsec_e->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // set to 120 seconds (default is 4500)
- rr_set_nsec(nsec_e, RR_A);
+ close_pipe(svr->sockfd);
- pthread_mutex_lock(&svr->data_lock);
- svr->hostname = create_nlabel(hostname);
- rr_group_add(&svr->group, a_e);
- rr_group_add(&svr->group, nsec_e);
- pthread_mutex_unlock(&svr->data_lock);
+ svr->stop_flag = 2;
}
-void mdnsd_set_hostname_v6(struct mdnsd *svr, const char *hostname, struct in6_addr *addr)
-{
- struct rr_entry *aaaa_e = NULL,
- *nsec_e = NULL;
+/////////////////////////////////////////////////////
- // currently can't be called twice
- // dont ask me what happens if the IP changes
- assert(svr->hostname == NULL);
+void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip) {
+ struct rr_entry *a_e = NULL, *nsec_e = NULL;
- aaaa_e = rr_create_aaaa(create_nlabel(hostname), addr); // 120 seconds automatically
+ // currently can't be called twice
+ // dont ask me what happens if the IP changes
+ assert(svr->hostname == NULL);
- nsec_e = rr_create(create_nlabel(hostname), RR_NSEC);
- nsec_e->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // set to 120 seconds (default is 4500)
- rr_set_nsec(nsec_e, RR_AAAA);
+ a_e = rr_create_a(create_nlabel(hostname), ip); // 120 seconds automatically
- pthread_mutex_lock(&svr->data_lock);
- svr->hostname = create_nlabel(hostname);
- rr_group_add(&svr->group, aaaa_e);
- rr_group_add(&svr->group, nsec_e);
- pthread_mutex_unlock(&svr->data_lock);
-}
+ nsec_e = rr_create(create_nlabel(hostname), RR_NSEC);
+ nsec_e->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // set to 120 seconds (default is 4500)
+ rr_set_nsec(nsec_e, RR_A);
-void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr) {
- pthread_mutex_lock(&svr->data_lock);
- rr_group_add(&svr->group, rr);
- pthread_mutex_unlock(&svr->data_lock);
+ pthread_mutex_lock(&svr->data_lock);
+ svr->hostname = create_nlabel(hostname);
+ rr_group_add(&svr->group, a_e);
+ rr_group_add(&svr->group, nsec_e);
+ pthread_mutex_unlock(&svr->data_lock);
}
-struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name,
- const char *type, uint16_t port, const char *hostname, const char *txt[]) {
- struct rr_entry *txt_e = NULL,
- *srv_e = NULL,
- *ptr_e = NULL,
- *bptr_e = NULL;
- uint8_t *target;
- uint8_t *inst_nlabel, *type_nlabel, *nlabel;
- struct mdns_service *service = malloc(sizeof(struct mdns_service));
- memset(service, 0, sizeof(struct mdns_service));
-
- // combine service name
- type_nlabel = create_nlabel(type);
- inst_nlabel = create_label(instance_name);
- nlabel = join_nlabel(inst_nlabel, type_nlabel);
-
- // create TXT record
- if (txt && *txt) {
- txt_e = rr_create(dup_nlabel(nlabel), RR_TXT); // automatically 4500 seconds
- rr_list_append(&service->entries, txt_e);
-
- // add TXTs
- for (; *txt; txt++)
- rr_add_txt(txt_e, *txt);
- }
-
- // create SRV record
- assert(hostname || svr->hostname); // either one as target
- target = hostname ?
- create_nlabel(hostname) :
- dup_nlabel(svr->hostname);
-
- srv_e = rr_create_srv(dup_nlabel(nlabel), port, target); // automatically 4500 seconds
- rr_list_append(&service->entries, srv_e);
-
- // create PTR record for type
- ptr_e = rr_create_ptr(type_nlabel, srv_e); // automatically 4500 seconds
-
- // create services PTR record for type
- // this enables the type to show up as a "service"
- bptr_e = rr_create_ptr(dup_nlabel(SERVICES_DNS_SD_NLABEL), ptr_e); // automatically 4500 seconds
+void mdnsd_set_hostname_v6(struct mdnsd *svr, const char *hostname, struct in6_addr *addr) {
+ struct rr_entry *aaaa_e = NULL, *nsec_e = NULL;
- // modify lists here
- pthread_mutex_lock(&svr->data_lock);
+ // currently can't be called twice
+ // dont ask me what happens if the IP changes
+ assert(svr->hostname == NULL);
- if (txt_e)
- rr_group_add(&svr->group, txt_e);
- rr_group_add(&svr->group, srv_e);
- rr_group_add(&svr->group, ptr_e);
- rr_group_add(&svr->group, bptr_e);
+ aaaa_e = rr_create_aaaa(create_nlabel(hostname), addr); // 120 seconds automatically
- // append PTR entry to announce list
- rr_list_append(&svr->announce, ptr_e);
- rr_list_append(&svr->services, ptr_e);
+ nsec_e = rr_create(create_nlabel(hostname), RR_NSEC);
+ nsec_e->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // set to 120 seconds (default is 4500)
+ rr_set_nsec(nsec_e, RR_AAAA);
- pthread_mutex_unlock(&svr->data_lock);
-
- // don't free type_nlabel - it's with the PTR record
- free(nlabel);
- free(inst_nlabel);
+ pthread_mutex_lock(&svr->data_lock);
+ svr->hostname = create_nlabel(hostname);
+ rr_group_add(&svr->group, aaaa_e);
+ rr_group_add(&svr->group, nsec_e);
+ pthread_mutex_unlock(&svr->data_lock);
+}
- // notify server
- write_pipe(svr->notify_pipe[1], ".", 1);
+void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr) {
+ pthread_mutex_lock(&svr->data_lock);
+ rr_group_add(&svr->group, rr);
+ pthread_mutex_unlock(&svr->data_lock);
+}
- return service;
+struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name,
+ const char *type, uint16_t port, const char *hostname,
+ const char *txt[]) {
+ struct rr_entry *txt_e = NULL, *srv_e = NULL, *ptr_e = NULL, *bptr_e = NULL;
+ uint8_t *target;
+ uint8_t *inst_nlabel, *type_nlabel, *nlabel;
+ struct mdns_service *service = malloc(sizeof(struct mdns_service));
+ memset(service, 0, sizeof(struct mdns_service));
+
+ // combine service name
+ type_nlabel = create_nlabel(type);
+ inst_nlabel = create_label(instance_name);
+ nlabel = join_nlabel(inst_nlabel, type_nlabel);
+
+ // create TXT record
+ if (txt && *txt) {
+ txt_e = rr_create(dup_nlabel(nlabel), RR_TXT); // automatically 4500 seconds
+ rr_list_append(&service->entries, txt_e);
+
+ // add TXTs
+ for (; *txt; txt++)
+ rr_add_txt(txt_e, *txt);
+ }
+
+ // create SRV record
+ assert(hostname || svr->hostname); // either one as target
+ target = hostname ? create_nlabel(hostname) : dup_nlabel(svr->hostname);
+
+ srv_e = rr_create_srv(dup_nlabel(nlabel), port, target); // automatically 4500 seconds
+ rr_list_append(&service->entries, srv_e);
+
+ // create PTR record for type
+ ptr_e = rr_create_ptr(type_nlabel, srv_e); // automatically 4500 seconds
+
+ // create services PTR record for type
+ // this enables the type to show up as a "service"
+ bptr_e = rr_create_ptr(dup_nlabel(SERVICES_DNS_SD_NLABEL), ptr_e); // automatically 4500 seconds
+
+ // modify lists here
+ pthread_mutex_lock(&svr->data_lock);
+
+ if (txt_e)
+ rr_group_add(&svr->group, txt_e);
+ rr_group_add(&svr->group, srv_e);
+ rr_group_add(&svr->group, ptr_e);
+ rr_group_add(&svr->group, bptr_e);
+
+ // append PTR entry to announce list
+ rr_list_append(&svr->announce, ptr_e);
+ rr_list_append(&svr->services, ptr_e);
+
+ pthread_mutex_unlock(&svr->data_lock);
+
+ // don't free type_nlabel - it's with the PTR record
+ free(nlabel);
+ free(inst_nlabel);
+
+ // notify server
+ write_pipe(svr->notify_pipe[1], ".", 1);
+
+ return service;
}
void mdns_service_destroy(struct mdns_service *srv) {
- assert(srv != NULL);
- rr_list_destroy(srv->entries, 0);
- free(srv);
+ assert(srv != NULL);
+ rr_list_destroy(srv->entries, 0);
+ free(srv);
}
struct mdnsd *mdnsd_start() {
- pthread_t tid;
- pthread_attr_t attr;
-
- struct mdnsd *server = malloc(sizeof(struct mdnsd));
- memset(server, 0, sizeof(struct mdnsd));
-
- if (create_pipe(server->notify_pipe) != 0) {
- log_message(LOG_ERR, "pipe(): %m\n");
- free(server);
- return NULL;
- }
-
- server->sockfd = create_recv_sock();
- if (server->sockfd < 0) {
- log_message(LOG_ERR, "unable to create recv socket");
- free(server);
- return NULL;
- }
-
- pthread_mutex_init(&server->data_lock, NULL);
-
- // init thread
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
- if (pthread_create(&tid, &attr, (void *(*)(void *)) main_loop, (void *) server) != 0) {
- pthread_mutex_destroy(&server->data_lock);
- free(server);
- return NULL;
- }
-
- return server;
+ pthread_t tid;
+ pthread_attr_t attr;
+
+ struct mdnsd *server = malloc(sizeof(struct mdnsd));
+ memset(server, 0, sizeof(struct mdnsd));
+
+ if (create_pipe(server->notify_pipe) != 0) {
+ log_message(LOG_ERR, "pipe(): %m\n");
+ free(server);
+ return NULL;
+ }
+
+ server->sockfd = create_recv_sock();
+ if (server->sockfd < 0) {
+ log_message(LOG_ERR, "unable to create recv socket");
+ free(server);
+ return NULL;
+ }
+
+ pthread_mutex_init(&server->data_lock, NULL);
+
+ // init thread
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+ if (pthread_create(&tid, &attr, (void *(*)(void *))main_loop, (void *)server) != 0) {
+ pthread_mutex_destroy(&server->data_lock);
+ free(server);
+ return NULL;
+ }
+
+ return server;
}
void mdnsd_stop(struct mdnsd *s) {
- assert(s != NULL);
+ assert(s != NULL);
- struct timeval tv = {
- .tv_sec = 0,
- .tv_usec = 500 * 1000,
- };
+ struct timeval tv = {
+ .tv_sec = 0, .tv_usec = 500 * 1000,
+ };
- s->stop_flag = 1;
- write_pipe(s->notify_pipe[1], ".", 1);
+ s->stop_flag = 1;
+ write_pipe(s->notify_pipe[1], ".", 1);
- while (s->stop_flag != 2)
- select(0, NULL, NULL, NULL, &tv);
+ while (s->stop_flag != 2)
+ select(0, NULL, NULL, NULL, &tv);
- close_pipe(s->notify_pipe[0]);
- close_pipe(s->notify_pipe[1]);
+ close_pipe(s->notify_pipe[0]);
+ close_pipe(s->notify_pipe[1]);
- pthread_mutex_destroy(&s->data_lock);
- rr_group_destroy(s->group);
- rr_list_destroy(s->announce, 0);
- rr_list_destroy(s->services, 0);
+ pthread_mutex_destroy(&s->data_lock);
+ rr_group_destroy(s->group);
+ rr_list_destroy(s->announce, 0);
+ rr_list_destroy(s->services, 0);
- if (s->hostname)
- free(s->hostname);
+ if (s->hostname)
+ free(s->hostname);
- free(s);
+ free(s);
}
diff --git a/tinysvcmdns.h b/tinysvcmdns.h
index 98f26fa..c32c443 100644
--- a/tinysvcmdns.h
+++ b/tinysvcmdns.h
@@ -45,120 +45,118 @@
#include <arpa/inet.h>
#endif
-#define MALLOC_ZERO_STRUCT(x, type) \
- x = malloc(sizeof(struct type)); \
- memset(x, 0, sizeof(struct type));
-
-#define DECL_MALLOC_ZERO_STRUCT(x, type) \
- struct type * MALLOC_ZERO_STRUCT(x, type)
-
+#define MALLOC_ZERO_STRUCT(x, type) \
+ x = malloc(sizeof(struct type)); \
+ memset(x, 0, sizeof(struct type));
+#define DECL_MALLOC_ZERO_STRUCT(x, type) struct type *MALLOC_ZERO_STRUCT(x, type)
struct rr_data_srv {
- uint16_t priority;
- uint16_t weight;
- uint16_t port;
- uint8_t *target; // host
+ uint16_t priority;
+ uint16_t weight;
+ uint16_t port;
+ uint8_t *target; // host
};
struct rr_data_txt {
- struct rr_data_txt *next;
- uint8_t *txt;
+ struct rr_data_txt *next;
+ uint8_t *txt;
};
struct rr_data_nsec {
- //uint8_t *name; // same as record
+ // uint8_t *name; // same as record
- // NSEC occupies the 47th bit, 5 bytes
- //uint8_t bitmap_len; // = 5
- uint8_t bitmap[5]; // network order: first byte contains LSB
+ // NSEC occupies the 47th bit, 5 bytes
+ // uint8_t bitmap_len; // = 5
+ uint8_t bitmap[5]; // network order: first byte contains LSB
};
struct rr_data_ptr {
- uint8_t *name; // NULL if entry is to be used
- struct rr_entry *entry;
+ uint8_t *name; // NULL if entry is to be used
+ struct rr_entry *entry;
};
struct rr_data_a {
- uint32_t addr;
+ uint32_t addr;
};
struct rr_data_aaaa {
- struct in6_addr *addr;
+ struct in6_addr *addr;
};
struct rr_entry {
- uint8_t *name;
-
- enum rr_type {
- RR_A = 0x01,
- RR_PTR = 0x0C,
- RR_TXT = 0x10,
- RR_AAAA = 0x1C,
- RR_SRV = 0x21,
- RR_NSEC = 0x2F,
- RR_ANY = 0xFF,
- } type;
-
- uint32_t ttl;
-
- // for use in Questions only
- char unicast_query;
-
- // for use in Answers only
- char cache_flush;
-
- uint16_t rr_class;
-
- // RR data
- union {
- struct rr_data_nsec NSEC;
- struct rr_data_srv SRV;
- struct rr_data_txt TXT;
- struct rr_data_ptr PTR;
- struct rr_data_a A;
- struct rr_data_aaaa AAAA;
- } data;
+ uint8_t *name;
+
+ enum rr_type {
+ RR_A = 0x01,
+ RR_PTR = 0x0C,
+ RR_TXT = 0x10,
+ RR_AAAA = 0x1C,
+ RR_SRV = 0x21,
+ RR_NSEC = 0x2F,
+ RR_ANY = 0xFF,
+ } type;
+
+ uint32_t ttl;
+
+ // for use in Questions only
+ char unicast_query;
+
+ // for use in Answers only
+ char cache_flush;
+
+ uint16_t rr_class;
+
+ // RR data
+ union {
+ struct rr_data_nsec NSEC;
+ struct rr_data_srv SRV;
+ struct rr_data_txt TXT;
+ struct rr_data_ptr PTR;
+ struct rr_data_a A;
+ struct rr_data_aaaa AAAA;
+ } data;
};
struct rr_list {
- struct rr_entry *e;
- struct rr_list *next;
+ struct rr_entry *e;
+ struct rr_list *next;
};
struct rr_group {
- uint8_t *name;
+ uint8_t *name;
- struct rr_list *rr;
+ struct rr_list *rr;
- struct rr_group *next;
+ struct rr_group *next;
};
-#define MDNS_FLAG_RESP (1 << 15) // Query=0 / Response=1
-#define MDNS_FLAG_AA (1 << 10) // Authoritative
-#define MDNS_FLAG_TC (1 << 9) // TrunCation
-#define MDNS_FLAG_RD (1 << 8) // Recursion Desired
-#define MDNS_FLAG_RA (1 << 7) // Recursion Available
-#define MDNS_FLAG_Z (1 << 6) // Reserved (zero)
+#define MDNS_FLAG_RESP (1 << 15) // Query=0 / Response=1
+#define MDNS_FLAG_AA (1 << 10) // Authoritative
+#define MDNS_FLAG_TC (1 << 9) // TrunCation
+#define MDNS_FLAG_RD (1 << 8) // Recursion Desired
+#define MDNS_FLAG_RA (1 << 7) // Recursion Available
+#define MDNS_FLAG_Z (1 << 6) // Reserved (zero)
-#define MDNS_FLAG_GET_RCODE(x) (x & 0x0F)
+#define MDNS_FLAG_GET_RCODE(x) (x & 0x0F)
#define MDNS_FLAG_GET_OPCODE(x) ((x >> 11) & 0x0F)
// gets the PTR target name, either from "name" member or "entry" member
-#define MDNS_RR_GET_PTR_NAME(rr) (rr->data.PTR.name != NULL ? rr->data.PTR.name : rr->data.PTR.entry->name)
+#define MDNS_RR_GET_PTR_NAME(rr) \
+ (rr->data.PTR.name != NULL ? rr->data.PTR.name : rr->data.PTR.entry->name)
struct mdns_pkt {
- uint16_t id; // transaction ID
- uint16_t flags;
- uint16_t num_qn;
- uint16_t num_ans_rr;
- uint16_t num_auth_rr;
- uint16_t num_add_rr;
-
- struct rr_list *rr_qn; // questions
- struct rr_list *rr_ans; // answer RRs
- struct rr_list *rr_auth; // authority RRs
- struct rr_list *rr_add; // additional RRs
+ uint16_t id; // transaction ID
+ uint16_t flags;
+ uint16_t num_qn;
+ uint16_t num_ans_rr;
+ uint16_t num_auth_rr;
+ uint16_t num_add_rr;
+
+ struct rr_list *rr_qn; // questions
+ struct rr_list *rr_ans; // answer RRs
+ struct rr_list *rr_auth; // authority RRs
+ struct rr_list *rr_add; // additional RRs
};
struct mdns_pkt *mdns_parse_pkt(uint8_t *pkt_buf, size_t pkt_len);
@@ -197,7 +195,7 @@ uint8_t *join_nlabel(const uint8_t *n1, const uint8_t *n2);
// compares 2 names
static inline int cmp_nlabel(const uint8_t *L1, const uint8_t *L2) {
- return strcmp((char *) L1, (char *) L2);
+ return strcmp((char *)L1, (char *)L2);
}
//******************************************************//
@@ -225,10 +223,10 @@ void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr);
// registers a service with the MDNS responder instance
struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name,
- const char *type, uint16_t port, const char *hostname, const char *txt[]);
+ const char *type, uint16_t port, const char *hostname,
+ const char *txt[]);
// destroys the mdns_service struct returned by mdnsd_register_svc()
void mdns_service_destroy(struct mdns_service *srv);
-
#endif // _TINYSVCMDNS_H