Merge pull request #80 from mikebrady/pipework

Flow control improvements and limited support for different backends. Formatted much of the source code.
This commit is contained in:
Mike Brady
2015-06-03 13:49:47 +01:00
29 changed files with 5225 additions and 5024 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
SUBDIRS = man
bin_PROGRAMS = shairport-sync
shairport_sync_SOURCES = shairport.c rtsp.c mdns.c mdns_external.c common.c rtp.c player.c alac.c audio.c audio_dummy.c audio_pipe.c
shairport_sync_SOURCES = shairport.c rtsp.c mdns.c mdns_external.c common.c rtp.c player.c alac.c audio.c audio_dummy.c audio_pipe.c audio_stdout.c
AM_CFLAGS = -Wno-multichar
+2
View File
@@ -12,6 +12,8 @@ When `make install` is executed, a sample configuration is installed or updated
If you enable metadata, please note that the option has changed somewhat. The option `-M` has a new long name equivalent: `--metadata-pipename` and the argument you provide must now be the full name of the metadata pipe, e.g. `-M /tmp/shairport-sync-metadata`.
Change the name of the setting "dac_buffer_desired_length" to "backend_buffer_desired_length"
* Enhancements
* Shairport Sync now reads settings from the configuration file `/etc/shairport-sync.conf`. This has settings for most command-line options and it's where any new settings will go. A default configuration file will be installed if one doesn't exist, and a sample file configuration file is always installed or updated. Details of settings are provided in the sample file. Shairport Sync relies on the `libconfig` library to read configuration files. For the present, you can disable the new feature (and save the space taken up by `libconfig`) by using the configure option `--without-configfile-support`.
* New command-line option `-c <file>` or `--configfile=<file>` allows you to specify a configuration file other than `/etc/shairport-sync.conf`.
+19 -23
View File
@@ -41,7 +41,7 @@ extern audio_output audio_pulse;
#ifdef CONFIG_ALSA
extern audio_output audio_alsa;
#endif
extern audio_output audio_dummy, audio_pipe;
extern audio_output audio_dummy, audio_pipe, audio_stdout;
static audio_output *outputs[] = {
#ifdef CONFIG_SNDIO
@@ -56,36 +56,32 @@ static audio_output *outputs[] = {
#ifdef CONFIG_AO
&audio_ao,
#endif
&audio_dummy,
&audio_pipe,
NULL
};
&audio_dummy, &audio_pipe, &audio_stdout, 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();
}
}
+24 -24
View File
@@ -15,33 +15,33 @@ typedef struct {
} audio_parameters;
typedef struct {
void (*help)(void);
char *name;
void (*help)(void);
char *name;
// start of program
int (*init)(int argc, char **argv, config_t* cfgp);
// at end of program
void (*deinit)(void);
// 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)();
void (*start)(int sample_rate);
// 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);
// 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);
+186 -159
View File
@@ -35,7 +35,7 @@
#include "audio.h"
static void help(void);
static int init(int argc, char **argv, config_t* cfgp);
static int init(int argc, char **argv);
static void deinit(void);
static void start(int sample_rate);
static void play(short buf[], int samples);
@@ -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,52 +81,72 @@ 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, config_t *cfgp) {
static int init(int argc, char **argv) {
const char *str;
int value;
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
// get settings from settings file first, allow them to be over-ridden by command line options
if (cfgp!=NULL) {
/* Get the Output Device Name. */
if(config_lookup_string(cfgp, "alsa.output_device", &str)) {
alsa_out_dev = (char*)str;
}
/* Get the Mixer Type setting. */
if(config_lookup_string(cfgp, "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(cfgp, "alsa.mixer_device", &str)) {
alsa_mix_dev = (char*)str;
}
/* Get the Mixer Control Name. */
if(config_lookup_string(cfgp, "alsa.mixer_control_name", &str)) {
alsa_mix_ctrl = (char*)str;
}
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 \"%d\". 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 < -66150) || (value > 66150))
die("Invalid alsa audio backend buffer latency offset \"%d\". It should be between -66150 and +66150, 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
@@ -135,37 +155,37 @@ static int init(int argc, char **argv, config_t *cfgp) {
// 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;
@@ -174,39 +194,41 @@ static int init(int argc, char **argv, config_t *cfgp) {
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;
}
@@ -214,20 +236,20 @@ static int init(int argc, char **argv, config_t *cfgp) {
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);
@@ -241,75 +263,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);
@@ -321,20 +347,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;
@@ -342,29 +369,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));
}
+74 -77
View File
@@ -24,7 +24,6 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <unistd.h>
#include <memory.h>
@@ -35,98 +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, config_t *cfgp) {
ao_initialize();
int driver = ao_default_driver_id();
ao_option *ao_opts = NULL;
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++;
// 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);
}
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!");
if (sample_rate != 44100)
die("unexpected sample rate!");
}
static void play(short buf[], int samples) {
ao_play(dev, (char*)buf, samples*4);
}
static void play(short buf[], int samples) { ao_play(dev, (char *)buf, samples * 4); }
static void stop(void) {
}
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
};
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};
+20 -44
View File
@@ -32,55 +32,31 @@
int Fs;
long long starttime, samples_played;
static int init(int argc, char **argv, config_t *cfgp) {
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;
printf("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) {
struct timeval tv;
static void play(short buf[], int samples) {}
// this is all a bit expensive but it's long-term stable.
gettimeofday(&tv, NULL);
static void stop(void) { debug(1, "dummy audio stopped\n"); }
long long nowtime = tv.tv_usec + 1e6*tv.tv_sec;
static void help(void) { printf(" There are no options for dummy audio.\n"); }
if (!starttime)
starttime = nowtime;
samples_played += samples;
long long finishtime = starttime + samples_played * 1e6 / Fs;
usleep(finishtime - nowtime);
}
static void stop(void) {
printf("dummy audio stopped\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};
+63 -49
View File
@@ -38,76 +38,90 @@ static int fd = -1;
char *pipename = NULL;
static void start(int sample_rate) {
debug(1,"Pipename to start is \"%s\"",pipename);
fd = open(pipename, O_WRONLY | O_NONBLOCK);
/*
if (fd < 0) {
perror("open");
die("could not open specified pipe for writing");
}
*/
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)
close(fd);
}
static int init(int argc, char **argv, config_t *cfgp) {
static int init(int argc, char **argv) {
const char *str;
int value;
if (cfgp!=NULL) {
/* Get the Output Pipename. */
const char *str;
if(config_lookup_string(cfgp, "pipe.name", &str)) {
pipename = (char*)str;
}
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;
}
/* Get the desired buffer size setting. */
if (config_lookup_int(config.cfg, "pipe.audio_backend_buffer_desired_length", &value)) {
if ((value < 0) || (value > 132300))
die("Invalid pipe audio backend buffer desired length \"%d\". It should be between 0 and 132300, default is 44100",
value);
else
config.audio_backend_buffer_desired_length = value;
}
/* Get the latency offset. */
if (config_lookup_int(config.cfg, "pipe.audio_backend_latency_offset", &value)) {
if ((value < -66150) || (value > 66150))
die("Invalid pipe audio backend buffer latency offset \"%d\". It should be between -66150 and +66150, default is 0",
value);
else
config.audio_backend_latency_offset = value;
}
}
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
// here, create the pipe
if (strcasecmp(pipename, "STDOUT") != 0)
if (mkfifo(pipename, 0644) && errno != EEXIST)
die("Could not create metadata FIFO %s", pipename);
die("Could not create output pipe \"%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)
close(fd);
if (pipename)
free(pipename);
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.\n");
printf(" pipe takes 1 argument: the name of the FIFO to write to.\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};
+68 -81
View File
@@ -24,7 +24,6 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <unistd.h>
#include <memory.h>
@@ -37,117 +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, config_t *cfgp) {
pulse_options.apname = config.apname;
static int init(int argc, char **argv) {
optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
argv--; // so we shift the arguments to satisfy getopt()
argc++;
pulse_options.apname = config.apname;
// 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);
}
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
};
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);
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));
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};
+39 -42
View File
@@ -23,65 +23,62 @@
static struct sio_hdl *sio;
static struct sio_par par;
static int init(int argc, char **argv, config_t *cfgp) {
sio = sio_open(SIO_DEVANY, SIO_PLAY, 0);
if (!sio)
die("sndio: cannot connect to sound server");
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_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");
if (!sio_setpar(sio, &par))
die("sndio: failed to set audio parameters");
if (!sio_getpar(sio, &par))
die("sndio: failed to get audio parameters");
return 0;
config.audio_backend_buffer_desired_length = 44100; // one second.
config.audio_backend_latency_offset = 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};
+99
View File
@@ -0,0 +1,99 @@
/*
* stdout output driver. This file is part of Shairport Sync.
* Copyright (c) Mike Brady 2015
* Based on pipe output driver
* Copyright (c) James Laird 2013
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <memory.h>
#include <stdlib.h>
#include <errno.h>
#include "common.h"
#include "audio.h"
static int fd = -1;
static void start(int sample_rate) {
fd = STDOUT_FILENO;
}
static void play(short buf[], int samples) {
int ignore = write(fd, buf, samples * 4);
}
static void stop(void) {
// don't close stdout
}
static int init(int argc, char **argv) {
const char *str;
int value;
config.audio_backend_buffer_desired_length = 44100; // one second.
config.audio_backend_latency_offset = 0;
if (config.cfg != NULL) {
/* Get the desired buffer size setting. */
if (config_lookup_int(config.cfg, "stdout.audio_backend_buffer_desired_length", &value)) {
if ((value < 0) || (value > 132300))
die("Invalid stdout audio backend buffer desired length \"%d\". It should be between 0 and 132300, default is 44100",
value);
else
config.audio_backend_buffer_desired_length = value;
}
/* Get the latency offset. */
if (config_lookup_int(config.cfg, "stdout.audio_backend_latency_offset", &value)) {
if ((value < -66150) || (value > 66150))
die("Invalid stdout audio backend buffer latency offset \"%d\". It should be between -66150 and +66150, default is 0",
value);
else
config.audio_backend_latency_offset = value;
}
}
return 0;
}
static void deinit(void) {
// don't close stdout
}
static void help(void) {
printf(" stdout takes no arguments\n");
}
audio_output audio_stdout = {.name = "stdout",
.help = &help,
.init = &init,
.deinit = &deinit,
.start = &start,
.stop = &stop,
.flush = NULL,
.delay = NULL,
.play = &play,
.volume = NULL,
.parameters = NULL};
+300 -285
View File
@@ -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);
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;
}
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;
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);
}
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;
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_context trsa;
const char *pers = "rsa_encrypt";
int 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;
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);
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_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;
// 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);
// 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);
#endif
return time_now_fp;
}
+55 -50
View File
@@ -10,76 +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 dac_buffer_queue_desired_length;
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();
@@ -95,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
@@ -113,6 +117,7 @@ shairport_cfg config;
char sender_name[1024];
char sender_ip[1024];
char album_name[1024]; // we might need this for picture diagnostics
config_t config_file_stuff;
void command_start(void);
void command_stop(void);
+44 -57
View File
@@ -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");
}
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_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;
}
if (config.mdns == NULL)
die("Could not establish mDNS advertisement!");
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!");
}
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);
}
}
+9 -9
View File
@@ -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
+74 -101
View File
@@ -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;
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_S_RUNNING:
register_service(c);
break;
case AVAHI_CLIENT_FAILURE:
case AVAHI_CLIENT_S_COLLISION:
die("avahi client failure");
case AVAHI_CLIENT_FAILURE:
case AVAHI_CLIENT_S_COLLISION:
die("avahi client failure");
case AVAHI_CLIENT_CONNECTING:
break;
}
case AVAHI_CLIENT_CONNECTING:
break;
}
}
static int avahi_register(char *srvname, int srvport) {
debug(1, "avahi: avahi_register.");
name = strdup(srvname);
port = 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;
}
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;
}
if (avahi_threaded_poll_start(tpoll) < 0) {
warn("couldn't start avahi tpoll thread");
return -1;
}
return 0;
return 0;
}
static void avahi_unregister(void) {
debug(1, "avahi: avahi_unregister.");
if (tpoll)
avahi_threaded_poll_stop(tpoll);
tpoll = NULL;
debug(1, "avahi: avahi_unregister.");
if (tpoll)
avahi_threaded_poll_stop(tpoll);
tpoll = NULL;
if (name)
free(name);
name = 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};
+44 -61
View File
@@ -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;
#ifdef CONFIG_METADATA
if (config.meta_dir)
record = recordwithmetadata;
else
const char *recordwithmetadata[] = {MDNS_RECORD_WITH_METADATA, NULL};
#endif
record = recordwithoutmetadata;
uint16_t length = 0;
const char **field;
char **record;
#ifdef CONFIG_METADATA
if (config.meta_dir)
record = recordwithmetadata;
else
#endif
record = recordwithoutmetadata;
// Concatenate string contained i record into buf.
uint16_t length = 0;
const char **field;
for (field = record; *field; field ++)
{
length += strlen(*field) + 1; // One byte for length each time
}
// Concatenate string contained i record into buf.
char *buf = malloc(length * sizeof(char));
if (buf == NULL)
{
warn("dns_sd: buffer record allocation failed");
return -1;
}
for (field = record; *field; field++) {
length += strlen(*field) + 1; // One byte for length each time
}
char *p = buf;
char *buf = malloc(length * sizeof(char));
if (buf == NULL) {
warn("dns_sd: buffer record allocation failed");
return -1;
}
for (field = record; *field; field ++)
{
char * newp = stpcpy(p + 1, *field);
*p = newp - p - 1;
p = newp;
}
char *p = buf;
DNSServiceErrorType error;
error = DNSServiceRegister(&service,
0,
kDNSServiceInterfaceIndexAny,
apname,
"_raop._tcp",
"",
NULL,
htons((uint16_t)port),
length,
buf,
NULL,
NULL);
for (field = record; *field; field++) {
char *newp = stpcpy(p + 1, *field);
*p = newp - p - 1;
p = newp;
}
free(buf);
DNSServiceErrorType error;
error = DNSServiceRegister(&service, 0, kDNSServiceInterfaceIndexAny, apname, "_raop._tcp", "",
NULL, htons((uint16_t)port), length, buf, NULL, NULL);
if (error == kDNSServiceErr_NoError)
return 0;
else
{
warn("dns-sd: DNSServiceRegister error %d", error);
return -1;
}
free(buf);
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};
+90 -107
View File
@@ -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;
}
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;
}
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);
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));
// 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
_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;
}
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 = argvwithoutmetadata;
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[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};
+87 -107
View File
@@ -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;
struct ifaddrs *ifalist;
struct ifaddrs *ifa;
svr = mdnsd_start();
if (svr == NULL) {
warn("tinysvcmdns: mdnsd_start() failed");
return -1;
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;
}
}
// 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;
if (ifa == NULL) {
warn("tinysvcmdns: no non-loopback ipv4 or ipv6 interface found");
return -1;
}
// 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;
// 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;
}
}
ifa = ifalist;
freeifaddrs(ifa);
// 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;
}
// 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 };
char *txtwithoutmetadata[] = {MDNS_RECORD_WITHOUT_METADATA, NULL};
#ifdef CONFIG_METADATA
char *txtwithmetadata[] = { MDNS_RECORD_WITH_METADATA, NULL };
#endif
char **txt;
char *txtwithmetadata[] = {MDNS_RECORD_WITH_METADATA, NULL};
#endif
char **txt;
#ifdef CONFIG_METADATA
if (config.meta_dir)
txt = txtwithmetadata;
else
#ifdef CONFIG_METADATA
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};
+645 -504
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -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
+462 -444
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -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;
+1015 -1015
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -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
+24 -8
View File
@@ -1,23 +1,23 @@
// Sample Configuration File for Shairport Sync
// Commented out settings are generally the defaults.
// Commented out settings are generally the defaults, except where noted.
// General Settings
general =
{
// name = "Shairport Sync Player"; // This is the name the service will advertise to iTunes. The default is "Shairport Sync on <hostname>"
// mdns_backend = "avahi"; // not used, not tested
// output_backend = "alsa"; // setting is not used, alsa is default
// output_backend = "alsa"; // alsa is default, other possibilities are "stdout", "pulse", "dummy". Only alsa supports synchronisation.
// port = 5000;
// password = "secret"; // default is no password
// interpolation = "basic"; // aka "stuffing". Default is "basic", alternative is "soxr"
// statistics = "no"; // print statistics in the log
// drift = 88; // allow this number of frames of drift before correcting it
// resync_threshold = 2205; // a synchronisation error greater than this will cause resynchronisation; 0 disables it
// log_verbosity = 0; // "0" means no verbosity, "3" is highest.
// ignore_volume_control = "no"; // set this to "yes" if you want the volume to be at 100% no matter what the source's volume control is set to.
// dac_buffer_desired_length = 6615; // If set too small, buffer underflow occurs on low-powered machines. Too long and the response times with software mixer become annoying.
// log_verbosity = 0; // "0" means no verbosity, "3" is most verbose.
// ignore_volume_control = "no"; // set this to "yes" if you want the volume to be at 100% no matter what the source's volume control is set to.
};
// Latencies for different sources.
latencies =
{
// default = 88200;
@@ -26,6 +26,8 @@ latencies =
// forkedDaapd = 99400;
};
// How to deal with metadata, including artwork
metadata =
{
// enabled = "no";
@@ -33,6 +35,7 @@ metadata =
// pipe_name = "/tmp/shairport-sync-metadata";
};
// Advanced parameters for controlling how a Shairport Sync runs
sessioncontrol =
{
// run_this_before_play_begins = "/path/to/application and args";
@@ -42,18 +45,31 @@ sessioncontrol =
// session_timeout = 120;
};
// These are parameters for the alsa back end, the only back end that currently works with shairport-sync
alsa =
//
// Back End Settings
//
// These are parameters for the alsa back end, the only back end that supports synchronisation
{
// output_device = "default";
// mixer_type = "software"; // "software" or "hardware"
// mixer_device = "default"; //actually, the mixer default is whatever the output_device is. Normally you wouldn't have to use this.
// mixer_control_name = "PCM"; // the name of the mixer to use -- there is no default.
// audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410.
// audio_backend_buffer_desired_length = 6615; // If set too small, buffer underflow occurs on low-powered machines. Too long and the response times with software mixer become annoying.
};
// These are parameters for the pipe back end, an experimental back end that directs output to a pipe.
pipe =
{
// name = "/dev/null";
// audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410.
// audio_backend_buffer_desired_length = 44100;
// name = "/path/to/pipe"; // there is no default pipe name for the output
};
// These are parameters for the stdout back end, an experimental back end that directs output to stdout.
stdout =
{
// audio_backend_latency_offset = 0; // Set this offset to compensate for a fixed delay in the audio back end. E.g. if the output device delays by 100 ms, set this to -4410.
// audio_backend_buffer_desired_length = 44100;
};
+609 -580
View File
File diff suppressed because it is too large Load Diff
+1094 -1117
View File
File diff suppressed because it is too large Load Diff
+70 -72
View File
@@ -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;
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;
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;
uint32_t ttl;
// for use in Questions only
char unicast_query;
// for use in Questions only
char unicast_query;
// for use in Answers only
char cache_flush;
// for use in Answers only
char cache_flush;
uint16_t rr_class;
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;
// 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;
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 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