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:
+1
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 = ¶meters; // 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,¤t_avail,¤t_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, ¤t_avail, ¤t_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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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};
|
||||
|
||||
@@ -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};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+1094
-1117
File diff suppressed because it is too large
Load Diff
+70
-72
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user