Squashed commit of the following:

Add three parameters to the backend play() function call -- (1) a flag indicating whether the samples are timed or not. If timed, (2) the timestamp and (3) the local time at which the first frame should be heard.
    Update missing pipewire library message.
    Makefile.am fix.
    Update the SHM version and fix the SHM name so that it works with FreeBSD.
    Remove redundant (?) AC_HEADER_STDC check.
    Fix compilation and installation under FreeBSD -- changes to allow building in a separate directory broke the FreeBSD build process.
    Add AC_CHECK_LIB for gcrypt in case PKG_CHECK_MODULES fails to find it.
    Find gcrypt using pkg-config
    Add code to detect when the RTSP channel goes idle for a period, and use it to reset clocks and timings, etc. so that SPS resumes correctly where the source has gone to sleep while playing a realtime stream and has subsequently woken up.
    Don't exit if a UDP Clock Control packet is empty. Also check minimum packet size.
    Remove some very experimental code which may cause memory and port leaks.
    Strip file path from the filename used in debug messages, information messages, warnings and fatal error messages.
    BB fix to Makefile.am -- overwriting the DBUS stuff with MPRIS definitions, duh.
    Fix some problems building on FreeBSD and tidy up the use of the "sed" editor.
    Fix some problems building on picore.
    Fix an uninitialised variable
    start using the "nqptp" SMI
This commit is contained in:
Mike Brady
2022-09-12 15:19:52 +01:00
parent b178e0e0a5
commit fd880056fb
28 changed files with 1292 additions and 1152 deletions
+6 -7
View File
@@ -56,7 +56,8 @@ pthread_mutex_t activity_monitor_mutex;
pthread_cond_t activity_monitor_cv;
void going_active(int block) {
// debug(1, "activity_monitor: state transitioning to \"active\" with%s blocking", block ? "" : "out");
// debug(1, "activity_monitor: state transitioning to \"active\" with%s blocking", block ? "" :
// "out");
if (config.cmd_active_start)
command_execute(config.cmd_active_start, "", block);
#ifdef CONFIG_METADATA
@@ -121,18 +122,16 @@ void activity_monitor_signify_activity(int active) {
// So, if the active end procedure is on a timer, it will be executed when the
// timeout occurs and the "blocking" status is ignored.
if ((state == am_inactive) && (player_state == ps_active)) {
state = am_active;
pthread_mutex_unlock(&activity_monitor_mutex);
going_active(
config.cmd_blocking);
going_active(config.cmd_blocking);
} else if ((state == am_active) && (player_state == ps_inactive) &&
(config.active_state_timeout == 0.0)) {
state = am_inactive;
pthread_mutex_unlock(&activity_monitor_mutex);
going_inactive(
config.cmd_blocking);
going_inactive(config.cmd_blocking);
} else {
pthread_mutex_unlock(&activity_monitor_mutex);
}
@@ -179,7 +178,7 @@ void *activity_monitor_thread_code(void *arg) {
// debug(1,"am_state: am_active");
while (player_state != ps_inactive)
pthread_cond_wait(&activity_monitor_cv, &activity_monitor_mutex);
// if it's not already am_inactive, the it should be beginning to time out...
if (state != am_inactive) {
state = am_timing_out;
+14 -1
View File
@@ -4,6 +4,19 @@
#include <libconfig.h>
#include <stdint.h>
// clang-format off
// Play samples provided may be from the source, in which case they will be timed
// or they may be generated by Shairport Sync, in which case they will not be timed.
// Typically these would be samples of silence, which may be dithered, sent during the lead-in to
// the start of the material, or inserted instead of a missing frame, or after a flush.
// clang-format on
typedef enum {
play_samples_are_untimed = 0, // typically the samples are (possibly dithered) silence
play_samples_are_timed, // timed and numbered.
} play_samples_type;
typedef struct {
double current_volume_dB;
int32_t minimum_volume_dB;
@@ -24,7 +37,7 @@ typedef struct {
void (*start)(int sample_rate, int sample_format);
// block of samples
int (*play)(void *buf, int samples);
int (*play)(void *buf, int samples, int sample_type, uint32_t timestamp, uint64_t playtime);
void (*stop)(void);
// may be null if no implemented
+82 -78
View File
@@ -52,32 +52,34 @@ typedef struct {
int frame_size;
} format_record;
static void help(void);
static int init(int argc, char **argv);
static void deinit(void);
static void start(int i_sample_rate, int i_sample_format);
static int play(void *buf, int samples);
static void stop(void);
static void flush(void);
int delay(long *the_delay);
int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time, uint64_t *the_delay,
uint64_t *frames_sent_to_dac);
void *alsa_buffer_monitor_thread_code(void *arg);
static void volume(double vol);
void do_volume(double vol);
int prepare(void);
int do_play(void *buf, int samples);
static void parameters(audio_parameters *info);
int mute(int do_mute); // returns true if it actually is allowed to use the mute
static double set_volume;
static int output_method_signalled = 0; // for reporting whether it's using mmap or not
int output_method_signalled = 0; // for reporting whether it's using mmap or not
int delay_type_notified = -1; // for controlling the reporting of whether the output device can do
// precision delays (e.g. alsa->pulsaudio virtual devices can't)
int use_monotonic_clock = 0; // this value will be set when the hardware is initialised
static void help(void);
static int init(int argc, char **argv);
static void deinit(void);
static void start(int i_sample_rate, int i_sample_format);
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime);
static void stop(void);
static void flush(void);
static int delay(long *the_delay);
static int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time,
uint64_t *the_delay, uint64_t *frames_sent_to_dac);
static void *alsa_buffer_monitor_thread_code(void *arg);
static void volume(double vol);
static void do_volume(double vol);
static int prepare(void);
static int do_play(void *buf, int samples);
static void parameters(audio_parameters *info);
static int mute(int do_mute); // returns true if it actually is allowed to use the mute
static double set_volume;
audio_output audio_alsa = {
.name = "alsa",
.help = &help,
@@ -98,8 +100,8 @@ audio_output audio_alsa = {
.volume = NULL, // a function will be provided if it can do hardware volume
.parameters = NULL}; // a function will be provided if it can do hardware volume
static pthread_mutex_t alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t alsa_mixer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t alsa_mixer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_t alsa_buffer_monitor_thread;
@@ -121,7 +123,7 @@ long stall_monitor_frame_count; // set to delay at start of time, increm
uint64_t stall_monitor_error_threshold; // if the time is longer than this, it's
// an error
static snd_output_t *output = NULL;
snd_output_t *output = NULL;
int frame_size; // in bytes for interleaved stereo
int alsa_device_initialised; // boolean to ensure the initialisation is only
@@ -133,25 +135,25 @@ yndk_type precision_delay_available_status =
snd_pcm_t *alsa_handle = NULL;
int alsa_handle_status =
ENODEV; // if alsa_handle is NULL, this should say why with a unix error code
static snd_pcm_hw_params_t *alsa_params = NULL;
static snd_pcm_sw_params_t *alsa_swparams = NULL;
static snd_ctl_t *ctl = NULL;
static snd_ctl_elem_id_t *elem_id = NULL;
static snd_mixer_t *alsa_mix_handle = NULL;
static snd_mixer_elem_t *alsa_mix_elem = NULL;
static snd_mixer_selem_id_t *alsa_mix_sid = NULL;
static long alsa_mix_minv, alsa_mix_maxv;
static long alsa_mix_mindb, alsa_mix_maxdb;
snd_pcm_hw_params_t *alsa_params = NULL;
snd_pcm_sw_params_t *alsa_swparams = NULL;
snd_ctl_t *ctl = NULL;
snd_ctl_elem_id_t *elem_id = NULL;
snd_mixer_t *alsa_mix_handle = NULL;
snd_mixer_elem_t *alsa_mix_elem = NULL;
snd_mixer_selem_id_t *alsa_mix_sid = NULL;
long alsa_mix_minv, alsa_mix_maxv;
long alsa_mix_mindb, alsa_mix_maxdb;
static char *alsa_out_dev = "default";
static char *alsa_mix_dev = NULL;
static char *alsa_mix_ctrl = NULL;
static int alsa_mix_index = 0;
static int has_softvol = 0;
char *alsa_out_dev = "default";
char *alsa_mix_dev = NULL;
char *alsa_mix_ctrl = NULL;
int alsa_mix_index = 0;
int has_softvol = 0;
int64_t dither_random_number_store = 0;
static int volume_set_request = 0; // set when an external request is made to set the volume.
int volume_set_request = 0; // set when an external request is made to set the volume.
int mixer_volume_setting_gives_mute = 0; // set when it is discovered that
// particular mixer volume setting
@@ -164,10 +166,10 @@ int volume_based_mute_is_active =
// use this to allow the use of snd_pcm_writei or snd_pcm_mmap_writei
snd_pcm_sframes_t (*alsa_pcm_write)(snd_pcm_t *, const void *, snd_pcm_uframes_t) = snd_pcm_writei;
int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
yndk_type *using_update_timestamps);
int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
yndk_type *using_update_timestamps);
static int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
yndk_type *using_update_timestamps);
static int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
yndk_type *using_update_timestamps);
// use this to allow the use of standard or precision delay calculations, with standard the, uh,
// standard.
@@ -182,7 +184,7 @@ int (*delay_and_status)(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
// If you want it to check again, set precision_delay_available_status to YNDK_DONT_KNOW
// first.
int precision_delay_available() {
static int precision_delay_available() {
if (precision_delay_available_status == YNDK_DONT_KNOW) {
// this is very crude -- if the device is a hardware device, then it's assumed the delay is
// precise
@@ -247,16 +249,16 @@ int precision_delay_available() {
int alsa_characteristics_already_listed = 0;
static snd_pcm_uframes_t period_size_requested, buffer_size_requested;
static int set_period_size_request, set_buffer_size_request;
snd_pcm_uframes_t period_size_requested, buffer_size_requested;
int set_period_size_request, set_buffer_size_request;
static uint64_t frames_sent_for_playing;
uint64_t frames_sent_for_playing;
// set to true if there has been a discontinuity between the last reported frames_sent_for_playing
// and the present reported frames_sent_for_playing
// Note that it will be set when the device is opened, as any previous figures for
// frames_sent_for_playing (which Shairport Sync might hold) would be invalid.
static int frames_sent_break_occurred;
int frames_sent_break_occurred;
static void help(void) {
printf(" -d output-device set the output device, default is \"default\".\n"
@@ -270,10 +272,10 @@ static void help(void) {
debug(2, "error %d executing a script to list alsa hardware device names", r);
}
void set_alsa_out_dev(char *dev) { alsa_out_dev = dev; }
void set_alsa_out_dev(char *dev) { alsa_out_dev = dev; } // ugh -- not static!
// assuming pthread cancellation is disabled
int open_mixer() {
static int open_mixer() {
int response = 0;
if (alsa_mix_ctrl != NULL) {
debug(3, "Open Mixer");
@@ -317,7 +319,7 @@ int open_mixer() {
}
// assuming pthread cancellation is disabled
void close_mixer() {
static void close_mixer() {
if (alsa_mix_handle) {
snd_mixer_close(alsa_mix_handle);
alsa_mix_handle = NULL;
@@ -325,7 +327,7 @@ void close_mixer() {
}
// assuming pthread cancellation is disabled
void do_snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *mix_elem, double vol) {
static void do_snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *mix_elem, double vol) {
if (snd_mixer_selem_set_playback_dB_all(mix_elem, vol, 0) != 0) {
debug(1, "Can't set playback volume accurately to %f dB.", vol);
if (snd_mixer_selem_set_playback_dB_all(mix_elem, vol, -1) != 0)
@@ -386,7 +388,7 @@ sps_format_t auto_format_check_sequence[] = {
// assuming pthread cancellation is disabled
// if do_auto_setting is true and auto format or auto speed has been requested,
// select the settings as appropriate and store them
int actual_open_alsa_device(int do_auto_setup) {
static int actual_open_alsa_device(int do_auto_setup) {
// the alsa mutex is already acquired when this is called
const snd_pcm_uframes_t minimal_buffer_headroom =
352 * 2; // we accept this much headroom in the hardware buffer, but we'll
@@ -867,7 +869,7 @@ int actual_open_alsa_device(int do_auto_setup) {
return 0;
}
int open_alsa_device(int do_auto_setup) {
static int open_alsa_device(int do_auto_setup) {
int result;
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
@@ -876,7 +878,7 @@ int open_alsa_device(int do_auto_setup) {
return result;
}
int prepare_mixer() {
static int prepare_mixer() {
int response = 0;
// do any alsa device initialisation (general case)
// at present, this is only needed if a hardware mixer is being used
@@ -991,7 +993,7 @@ int prepare_mixer() {
return response;
}
int alsa_device_init() { return prepare_mixer(); }
static int alsa_device_init() { return prepare_mixer(); }
static int init(int argc, char **argv) {
// for debugging
@@ -1383,7 +1385,7 @@ static void deinit(void) {
pthread_setcancelstate(oldState, NULL);
}
int set_mute_state() {
static int set_mute_state() {
int response = 1; // some problem expected, e.g. no mixer or not allowed to use it or disconnected
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
@@ -1436,8 +1438,8 @@ static void start(__attribute__((unused)) int i_sample_rate,
}
}
int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
yndk_type *using_update_timestamps) {
static int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
yndk_type *using_update_timestamps) {
int ret = alsa_handle_status;
if (using_update_timestamps)
*using_update_timestamps = YNDK_NO;
@@ -1470,8 +1472,8 @@ int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
return ret;
}
int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
yndk_type *using_update_timestamps) {
static int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
yndk_type *using_update_timestamps) {
snd_pcm_state_t state_temp = SND_PCM_STATE_DISCONNECTED;
snd_pcm_sframes_t delay_temp = 0;
if (using_update_timestamps)
@@ -1635,7 +1637,7 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
return ret;
}
int delay(long *the_delay) {
static int delay(long *the_delay) {
// returns 0 if the device is in a valid state -- SND_PCM_STATE_RUNNING or
// SND_PCM_STATE_PREPARED
// or SND_PCM_STATE_DRAINING
@@ -1667,8 +1669,8 @@ int delay(long *the_delay) {
return ret;
}
int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time, uint64_t *the_delay,
uint64_t *frames_sent_to_dac) {
static int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time,
uint64_t *the_delay, uint64_t *frames_sent_to_dac) {
// returns 0 if the device is in a valid state -- SND_PCM_STATE_RUNNING or
// SND_PCM_STATE_PREPARED
// or SND_PCM_STATE_DRAINING.
@@ -1709,7 +1711,7 @@ int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time,
return ret;
}
/*
int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played) {
static int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played) {
// elapsed_time is in nanoseconds
int response = 0; // zero means okay
if (measurement_data_is_valid) {
@@ -1724,7 +1726,7 @@ int get_rate_information(uint64_t *elapsed_time, uint64_t *frames_played) {
}
*/
int do_play(void *buf, int samples) {
static int do_play(void *buf, int samples) {
// assuming the alsa_mutex has been acquired
// debug(3,"audio_alsa play called.");
int oldState;
@@ -1809,7 +1811,7 @@ int do_play(void *buf, int samples) {
return ret;
}
int do_open(int do_auto_setup) {
static int do_open(int do_auto_setup) {
int ret = 0;
if (alsa_backend_state != abm_disconnected)
debug(1, "alsa: do_open() -- opening the output device when it is already "
@@ -1838,7 +1840,7 @@ int do_open(int do_auto_setup) {
return ret;
}
int do_close() {
static int do_close() {
debug(2, "alsa: do_close()");
if (alsa_backend_state == abm_disconnected)
debug(1, "alsa: do_close() -- closing the output device when it is already "
@@ -1863,7 +1865,7 @@ int do_close() {
return derr;
}
int sub_flush() {
static int sub_flush() {
if (alsa_backend_state == abm_disconnected)
debug(1, "alsa: do_flush() -- closing the output device when it is already "
"disconnected");
@@ -1882,7 +1884,9 @@ int sub_flush() {
return derr;
}
int play(void *buf, int samples) {
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
// play() will change the state of the alsa_backend_mode to abm_playing
// also, if the present alsa_backend_state is abm_disconnected, then first the
@@ -1919,7 +1923,7 @@ int play(void *buf, int samples) {
return ret;
}
int prepare(void) {
static int prepare(void) {
// this will leave the DAC open / connected.
int ret = 0;
@@ -1973,8 +1977,8 @@ static void parameters(audio_parameters *info) {
info->maximum_volume_dB = alsa_mix_maxdb;
}
void do_volume(double vol) { // caller is assumed to have the alsa_mutex when
// using this function
static void do_volume(double vol) { // caller is assumed to have the alsa_mutex when
// using this function
debug(3, "Setting volume db to %f.", vol);
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
@@ -2014,7 +2018,7 @@ void do_volume(double vol) { // caller is assumed to have the alsa_mutex when
pthread_setcancelstate(oldState, NULL);
}
void volume(double vol) {
static void volume(double vol) {
volume_set_request = 1; // an external request has been made to set the volume
do_volume(vol);
}
@@ -2038,7 +2042,7 @@ linear_volume;
}
*/
int mute(int mute_state_requested) { // these would be for external reasons, not
static int mute(int mute_state_requested) { // these would be for external reasons, not
// because of the
// state of the backend.
mute_requested_externally = mute_state_requested; // request a mute for external reasons
@@ -2046,13 +2050,13 @@ int mute(int mute_state_requested) { // these would be for extern
return set_mute_state();
}
/*
void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void
static void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void
*arg) {
debug(1, "alsa: alsa_buffer_monitor_thread_cleanup_function called.");
}
*/
void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
static void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
int frame_count = 0;
int error_count = 0;
int error_detected = 0;
+3 -1
View File
@@ -147,7 +147,9 @@ static void start(__attribute__((unused)) int sample_rate,
// debug(1,"libao start");
}
static int play(void *buf, int samples) {
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
int response = 0;
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
+21 -2
View File
@@ -4,7 +4,7 @@
* All rights reserved.
*
* Modifications for audio synchronisation
* and related work, copyright (c) Mike Brady 2014
* and related work, copyright (c) Mike Brady 2014 -- 2022
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
@@ -30,6 +30,7 @@
#include "audio.h"
#include "common.h"
#include <inttypes.h> // for PRId64 and friends
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
@@ -41,8 +42,26 @@ static void deinit(void) {}
static void start(int sample_rate, __attribute__((unused)) int sample_format) {
debug(1, "dummy audio output started at %d frames per second.", sample_rate);
}
// clang-format off
// Here is a brief explanation of the parameters:
// sample_type is 'play_samples_are_untimed' or 'play_samples_are_timed' defined in audio.h.
// untimed samples are typically silence used by Shairport Sync itself to set up synchronisation or to fill for missing packets of audio -- the timestamp and playtime are zero.
// timed samples come from the client.
// timestamp is the RTP timestamp given to the first frame by the client
// playtime is when the first frame should to be played. It is given in "local time".
// local time is CLOCK_MONOTONIC_RAW if available, or CLOCK_MONOTONIC otherwise, expressed in nanoseconds as an unsigned 64-bit integer (See get_absolute_time_in_ns() in common.c)
// clang-format on
static int play(__attribute__((unused)) void *buf, __attribute__((unused)) int samples,
int sample_type, uint32_t timestamp, uint64_t playtime) {
// sample code using some of the extra information
if (sample_type == play_samples_are_timed) {
int64_t lead_time = playtime - get_absolute_time_in_ns();
debug(1, "leadtime for frame %u is %" PRId64 " nanoseconds, i.e. %f seconds.", timestamp,
lead_time, 0.000000001 * lead_time);
}
static int play(__attribute__((unused)) void *buf, __attribute__((unused)) int samples) {
return 0;
}
+43 -49
View File
@@ -1,6 +1,6 @@
/*
* jack output driver. This file is part of Shairport Sync.
* Copyright (c) 2019 Mike Brady <mikebrady@iercom.net>,
* Copyright (c) 2019 -- 2022 Mike Brady <4265913+mikebrady@users.noreply.github.com>,
* Jörn Nettingsmeier <nettings@luchtbeweging.nl>
*
* All rights reserved.
@@ -40,49 +40,25 @@ typedef jack_default_audio_sample_t sample_t;
#define jack_sample_size sizeof(sample_t)
// Two-channel, 32bit audio:
static const int bytes_per_frame = NPORTS * jack_sample_size;
const int bytes_per_frame = NPORTS * jack_sample_size;
static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER;
int jack_init(int, char **);
void jack_deinit(void);
void jack_start(int, int);
int play(void *, int);
void jack_stop(void);
int jack_delay(long *);
void jack_flush(void);
audio_output audio_jack = {.name = "jack",
.help = NULL,
.init = &jack_init,
.deinit = &jack_deinit,
.prepare = NULL,
.start = &jack_start,
.stop = NULL,
.is_running = NULL,
.flush = &jack_flush,
.delay = &jack_delay,
.stats = NULL,
.play = &play,
.volume = NULL,
.parameters = NULL,
.mute = NULL};
pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER;
// This also affects deinterlacing.
// So make it exactly the number of incoming audio channels!
static jack_port_t *port[NPORTS];
static const char *port_name[NPORTS] = {"out_L", "out_R"};
jack_port_t *port[NPORTS];
const char *port_name[NPORTS] = {"out_L", "out_R"};
static jack_client_t *client;
static jack_nframes_t sample_rate;
static jack_nframes_t jack_latency;
jack_client_t *client;
jack_nframes_t sample_rate;
jack_nframes_t jack_latency;
static jack_ringbuffer_t *jackbuf;
static int flush_please = 0;
jack_ringbuffer_t *jackbuf;
int flush_please = 0;
static jack_latency_range_t latest_latency_range[NPORTS];
static int64_t time_of_latest_transfer;
jack_latency_range_t latest_latency_range[NPORTS];
int64_t time_of_latest_transfer;
#ifdef CONFIG_SOXR
typedef struct soxr_quality {
@@ -90,9 +66,9 @@ typedef struct soxr_quality {
const char *name;
} soxr_quality_t;
static soxr_quality_t soxr_quality_table[] = {{SOXR_VHQ, "very high"}, {SOXR_HQ, "high"},
{SOXR_MQ, "medium"}, {SOXR_LQ, "low"},
{SOXR_QQ, "quick"}, {-1, NULL}};
soxr_quality_t soxr_quality_table[] = {{SOXR_VHQ, "very high"}, {SOXR_HQ, "high"},
{SOXR_MQ, "medium"}, {SOXR_LQ, "low"},
{SOXR_QQ, "quick"}, {-1, NULL}};
static int parse_soxr_quality_name(const char *name) {
for (soxr_quality_t *s = soxr_quality_table; s->name != NULL; ++s) {
@@ -103,9 +79,9 @@ static int parse_soxr_quality_name(const char *name) {
return -1;
}
static soxr_t soxr = NULL;
static soxr_quality_spec_t quality_spec;
static soxr_io_spec_t io_spec;
soxr_t soxr = NULL;
soxr_quality_spec_t quality_spec;
soxr_io_spec_t io_spec;
#endif
static inline sample_t sample_conv(short sample) {
@@ -204,7 +180,7 @@ static void error(const char *desc) { warn("JACK error: \"%s\"", desc); }
// This is the function JACK will call in case of a non-critical event in the library.
static void info(const char *desc) { inform("JACK information: \"%s\"", desc); }
int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) {
static int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) {
int i;
int bufsz = -1;
config.audio_backend_latency_offset = 0;
@@ -332,7 +308,7 @@ int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) char **a
return 0;
}
void jack_deinit() {
static void jack_deinit() {
pthread_mutex_lock(&client_mutex);
if (jack_deactivate(client))
warn("Error deactivating jack client");
@@ -348,7 +324,7 @@ void jack_deinit() {
#endif
}
void jack_start(int i_sample_rate, __attribute__((unused)) int i_sample_format) {
static void jack_start(int i_sample_rate, __attribute__((unused)) int i_sample_format) {
// Nothing to do, JACK client has already been set up at jack_init().
// Also, we have no say over the sample rate or sample format of JACK,
// We convert the 16bit samples to float, and die if the sample rate is != 44k1 without soxr.
@@ -367,13 +343,13 @@ void jack_start(int i_sample_rate, __attribute__((unused)) int i_sample_format)
#endif
}
void jack_flush() {
static void jack_flush() {
debug(2, "Only the consumer can safely flush a lock-free ringbuffer. Asking the"
" process callback to do it...");
flush_please = 1;
}
int jack_delay(long *the_delay) {
static int jack_delay(long *the_delay) {
// Semantics change: we now look at the last transfer into the lock-free
// ringbuffer, not into the jack buffers directly (because locking those would
// violate real-time constraints). On average, that should lead to just a
@@ -399,7 +375,9 @@ int jack_delay(long *the_delay) {
return 0;
}
int play(void *buf, int samples) {
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
jack_ringbuffer_data_t v[2] = {0};
size_t i, j, c;
jack_nframes_t thisbuf;
@@ -445,3 +423,19 @@ int play(void *buf, int samples) {
}
return 0;
}
audio_output audio_jack = {.name = "jack",
.help = NULL,
.init = &jack_init,
.deinit = &jack_deinit,
.prepare = NULL,
.start = &jack_start,
.stop = NULL,
.is_running = NULL,
.flush = &jack_flush,
.delay = &jack_delay,
.stats = NULL,
.play = &play,
.volume = NULL,
.parameters = NULL,
.mute = NULL};
+3 -1
View File
@@ -223,7 +223,9 @@ static void start(__attribute__((unused)) int sample_rate,
pa_threaded_mainloop_unlock(mainloop);
}
static int play(void *buf, int samples) {
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
// debug(1,"pa_play of %d samples.",samples);
// copy the samples into the queue
size_t bytes_to_transfer = samples * 2 * 2;
+3 -1
View File
@@ -66,7 +66,9 @@ static void start(__attribute__((unused)) int sample_rate,
}
}
static int play(void *buf, int samples) {
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
// if the file is not open, try to open it.
char errorstring[1024];
if (fd == -1) {
+6 -4
View File
@@ -1,6 +1,6 @@
/*
* Asynchronous Pipewire Backend. This file is part of Shairport Sync.
* Copyright (c) Shairport Sync 2021
* Copyright (c) Shairport Sync 2021--2022
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
@@ -452,8 +452,10 @@ static void flush() {
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
}
static int play(void *buf, int samples) {
struct pw_buffer *pw_buffer;
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
struct pw_buffer *pw_buffer = NULL;
struct spa_buffer *spa_buffer;
struct spa_data *spa_data;
int ret;
@@ -522,4 +524,4 @@ audio_output audio_pw = {.name = "pw",
.play = &play,
.volume = NULL,
.parameters = NULL,
.mute = NULL};
.mute = NULL};
+27 -85
View File
@@ -28,42 +28,12 @@
#include <string.h>
#include <unistd.h>
static void help(void);
static int init(int, char **);
static void onmove_cb(void *, int);
static void deinit(void);
static void start(int, int);
static int play(void *, int);
static void stop(void);
static void onmove_cb(void *, int);
static int delay(long *);
static void flush(void);
int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time, uint64_t *the_delay,
uint64_t *frames_sent_to_dac);
audio_output audio_sndio = {.name = "sndio",
.help = &help,
.init = &init,
.deinit = &deinit,
.prepare = NULL,
.start = &start,
.stop = &stop,
.is_running = NULL,
.flush = &flush,
.delay = &delay,
.stats = NULL,
.play = &play,
.volume = NULL,
.parameters = NULL,
.mute = NULL};
static pthread_mutex_t sndio_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct sio_hdl *hdl;
static int framesize;
static size_t played;
static size_t written;
uint64_t time_of_last_onmove_cb;
uint64_t corrected_time_of_last_onmove_cb;
int at_least_one_onmove_cb_seen;
struct sio_par par;
@@ -90,18 +60,12 @@ static struct sndio_formats formats[] = {{"S8", SPS_FORMAT_S8, 44100, 8, 1, 1, S
static void help() { printf(" -d output-device set the output device [default*|...]\n"); }
uint64_t get_uptime_in_ns() {
uint64_t time_now_ns;
struct timespec tn;
clock_gettime(CLOCK_UPTIME_PRECISE, &tn);
uint64_t tnnsec = tn.tv_sec;
tnnsec = tnnsec * 1000000000;
uint64_t tnjnsec = tn.tv_nsec;
time_now_ns = tnnsec + tnjnsec;
return time_now_ns;
void onmove_cb(__attribute__((unused)) void *arg, int delta) {
time_of_last_onmove_cb = get_absolute_time_in_ns();
at_least_one_onmove_cb_seen = 1;
played += delta;
}
static int init(int argc, char **argv) {
int found, opt, round, rate, bufsz;
unsigned int i;
@@ -136,7 +100,7 @@ static int init(int argc, char **argv) {
devname = SIO_DEVANY;
if (config_lookup_int(config.cfg, "sndio.rate", &rate)) {
if (rate % 44100 == 0 && rate >= 44100 && rate <= 352800) {
par.rate = rate;
par.rate = rate;
} else {
die("sndio: output rate must be a multiple of 44100 and 44100 <= rate <= "
"352800");
@@ -188,15 +152,15 @@ static int init(int argc, char **argv) {
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
// pthread_mutex_lock(&sndio_mutex);
debug(1, "sndio: output device name is \"%s\".", devname);
debug(1, "sndio: rate: %u.",par.rate);
debug(1, "sndio: bits: %u.",par.bits);
debug(1, "sndio: rate: %u.", par.rate);
debug(1, "sndio: bits: %u.", par.bits);
hdl = sio_open(devname, SIO_PLAY, 0);
if (!hdl)
die("sndio: cannot open audio device");
written = played = 0;
time_of_last_onmove_cb = 0;
time_of_last_onmove_cb = 0;
at_least_one_onmove_cb_seen = 0;
for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
@@ -265,7 +229,9 @@ static void start(__attribute__((unused)) int sample_rate,
pthread_cleanup_pop(1); // unlock the mutex
}
static int play(void *buf, int frames) {
static int play(void *buf, int frames, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
if (frames > 0) {
// pthread_mutex_lock(&sndio_mutex);
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
@@ -285,20 +251,13 @@ static void stop() {
pthread_cleanup_pop(1); // unlock the mutex
}
static void onmove_cb(__attribute__((unused)) void *arg, int delta) {
time_of_last_onmove_cb = get_uptime_in_ns(); // this is not (?) adjusted ("disciplined") by NTP
corrected_time_of_last_onmove_cb = get_monotonic_time_in_ns(); // this is ("disciplined") by NTP
at_least_one_onmove_cb_seen = 1;
played += delta;
}
int get_delay(long *delay) {
int response = 0;
size_t estimated_extra_frames_output = 0;
if (at_least_one_onmove_cb_seen) { // when output starts, the onmove_cb callback will be made
// calculate the difference in time between now and when the last callback occurred,
// and use it to estimate the frames that would have been output
uint64_t time_difference = get_uptime_in_ns() - time_of_last_onmove_cb;
uint64_t time_difference = get_absolute_time_in_ns() - time_of_last_onmove_cb;
uint64_t frame_difference = (time_difference * par.rate) / 1000000000;
estimated_extra_frames_output = frame_difference;
// sanity check -- total estimate can not exceed frames written.
@@ -332,35 +291,18 @@ static void flush() {
pthread_cleanup_pop(1); // unlock the mutex
}
/*
// this doesn't seem to be very accurate, so not using it.
int stats(uint64_t *raw_measurement_time, uint64_t *corrected_measurement_time, uint64_t *the_delay,
uint64_t *frames_sent_to_dac) {
// returns 0 if the device is in a valid state
// returns the actual delay if running or 0 if prepared in *the_delay
// returns the present estimated value of frames played
// otherwise return a non-zero value
int ret = 0;
*the_delay = 0;
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState); // make this un-cancellable
long my_delay = 0; // initialised to stop a compiler warning
pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1);
*raw_measurement_time = time_of_last_onmove_cb;
*corrected_measurement_time = corrected_time_of_last_onmove_cb; // this is ("disciplined") by NTP
get_delay(&my_delay);
ret = frames_sent_break_occurred; // will be zero unless an error like an underrun occurred
frames_sent_break_occurred = 0; // reset it.
if (frames_sent_to_dac != NULL)
*frames_sent_to_dac = frames_sent_for_playing;
pthread_cleanup_pop(1); // unlock the mutex
pthread_setcancelstate(oldState, NULL);
uint64_t hd = my_delay; // note: snd_pcm_sframes_t is a long
*the_delay = hd;
if (at_least_one_onmove_cb_seen == 0)
ret = 1;
return ret;
}
*/
audio_output audio_sndio = {.name = "sndio",
.help = &help,
.init = &init,
.deinit = &deinit,
.prepare = NULL,
.start = &start,
.stop = &stop,
.is_running = NULL,
.flush = &flush,
.delay = &delay,
.stats = NULL,
.play = &play,
.volume = NULL,
.parameters = NULL,
.mute = NULL};
+3 -1
View File
@@ -164,7 +164,9 @@ static void start(int sample_rate, int sample_format) {
debug(1, "libsoundio output started\n");
}
static int play(void *buf, int samples) {
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
// int err;
int free_bytes = soundio_ring_buffer_free_count(ring_buffer);
int written_bytes = 0;
+3 -1
View File
@@ -43,7 +43,9 @@ static void start(__attribute__((unused)) int sample_rate,
fd = STDOUT_FILENO;
}
static int play(void *buf, int samples) {
static int play(void *buf, int samples, __attribute__((unused)) int sample_type,
__attribute__((unused)) uint32_t timestamp,
__attribute__((unused)) uint64_t playtime) {
char errorstring[1024];
int warned = 0;
int rc = write(fd, buf, samples * 4);
+36 -24
View File
@@ -62,12 +62,12 @@
#endif
#ifdef COMPILE_FOR_OSX
#include <net/if_dl.h>
#include <net/if_types.h>
#include <netinet/in.h>
#include <CoreServices/CoreServices.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <netinet/in.h>
#endif
#ifdef CONFIG_OPENSSL
@@ -455,9 +455,10 @@ char *generate_preliminary_string(char *buffer, size_t buffer_length, double tss
return insertion_point;
}
void _die(const char *filename, const int linenumber, const char *format, ...) {
void _die(const char *thefilename, const int linenumber, const char *format, ...) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
char b[16384];
b[0] = 0;
char *s;
@@ -468,9 +469,12 @@ void _die(const char *filename, const int linenumber, const char *format, ...) {
uint64_t time_since_last_debug_message = time_now - ns_time_at_last_debug_message;
ns_time_at_last_debug_message = time_now;
pthread_mutex_unlock(&debug_timing_lock);
char *basec = strdup(thefilename);
char *filename = basename(basec);
s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / 1000000000,
1.0 * time_since_last_debug_message / 1000000000, filename,
linenumber, " *fatal error: ");
free(basec);
} else {
strncpy(b, "fatal error: ", sizeof(b));
s = b + strlen(b);
@@ -485,7 +489,7 @@ void _die(const char *filename, const int linenumber, const char *format, ...) {
exit(EXIT_FAILURE);
}
void _warn(const char *filename, const int linenumber, const char *format, ...) {
void _warn(const char *thefilename, const int linenumber, const char *format, ...) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
char b[16384];
@@ -498,9 +502,12 @@ void _warn(const char *filename, const int linenumber, const char *format, ...)
uint64_t time_since_last_debug_message = time_now - ns_time_at_last_debug_message;
ns_time_at_last_debug_message = time_now;
pthread_mutex_unlock(&debug_timing_lock);
char *basec = strdup(thefilename);
char *filename = basename(basec);
s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / 1000000000,
1.0 * time_since_last_debug_message / 1000000000, filename,
linenumber, " *warning: ");
free(basec);
} else {
strncpy(b, "warning: ", sizeof(b));
s = b + strlen(b);
@@ -513,11 +520,12 @@ void _warn(const char *filename, const int linenumber, const char *format, ...)
pthread_setcancelstate(oldState, NULL);
}
void _debug(const char *filename, const int linenumber, int level, const char *format, ...) {
void _debug(const char *thefilename, const int linenumber, int level, const char *format, ...) {
if (level > debuglev)
return;
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
char b[16384];
b[0] = 0;
pthread_mutex_lock(&debug_timing_lock);
@@ -526,9 +534,12 @@ void _debug(const char *filename, const int linenumber, int level, const char *f
uint64_t time_since_last_debug_message = time_now - ns_time_at_last_debug_message;
ns_time_at_last_debug_message = time_now;
pthread_mutex_unlock(&debug_timing_lock);
char *basec = strdup(thefilename);
char *filename = basename(basec);
char *s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / 1000000000,
1.0 * time_since_last_debug_message / 1000000000, filename,
linenumber, " ");
free(basec);
va_list args;
va_start(args, format);
vsnprintf(s, sizeof(b) - (s - b), format, args);
@@ -537,7 +548,7 @@ void _debug(const char *filename, const int linenumber, int level, const char *f
pthread_setcancelstate(oldState, NULL);
}
void _inform(const char *filename, const int linenumber, const char *format, ...) {
void _inform(const char *thefilename, const int linenumber, const char *format, ...) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
char b[16384];
@@ -550,9 +561,12 @@ void _inform(const char *filename, const int linenumber, const char *format, ...
uint64_t time_since_last_debug_message = time_now - ns_time_at_last_debug_message;
ns_time_at_last_debug_message = time_now;
pthread_mutex_unlock(&debug_timing_lock);
char *basec = strdup(thefilename);
char *filename = basename(basec);
s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / 1000000000,
1.0 * time_since_last_debug_message / 1000000000, filename,
linenumber, " ");
free(basec);
} else {
s = b;
}
@@ -1242,11 +1256,11 @@ uint64_t get_monotonic_time_in_ns() {
if (sTimebaseInfo.denom == 0)
die("could not initialise Mac timebase info in get_monotonic_time_in_ns().");
// 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
time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom;
// this gives us nanoseconds
time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom;
#endif
return time_now_ns;
@@ -1306,8 +1320,8 @@ uint64_t get_absolute_time_in_ns() {
if (sTimebaseInfo.denom == 0)
die("could not initialise Mac timebase info in get_absolute_time_in_ns().");
// this gives us nanoseconds
time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom;
// this gives us nanoseconds
time_now_ns = time_now_mach * sTimebaseInfo.numer / sTimebaseInfo.denom;
#endif
return time_now_ns;
@@ -1520,21 +1534,20 @@ int _debug_mutex_lock(pthread_mutex_t *mutex, useconds_t dally_time, const char
return pthread_mutex_lock(mutex);
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
if (debuglevel != 0)
if (debuglevel != 0)
_debug(filename, line, 3, "mutex_lock \"%s\".", mutexname); // only if you really ask for it!
int result = sps_pthread_mutex_timedlock(mutex, dally_time);
if (result == ETIMEDOUT) {
_debug(filename, line, debuglevel,
"mutex_lock \"%s\" failed to lock after %f ms -- now waiting unconditionally to lock it.",
mutexname, dally_time * 1E-3);
_debug(
filename, line, debuglevel,
"mutex_lock \"%s\" failed to lock after %f ms -- now waiting unconditionally to lock it.",
mutexname, dally_time * 1E-3);
result = pthread_mutex_lock(mutex);
if (result == 0)
_debug(filename, line, debuglevel,
" ...mutex_lock \"%s\" locked successfully.",
mutexname);
_debug(filename, line, debuglevel, " ...mutex_lock \"%s\" locked successfully.", mutexname);
else
_debug(filename, line, debuglevel,
" ...mutex_lock \"%s\" exited with error code: %u", mutexname, result);
_debug(filename, line, debuglevel, " ...mutex_lock \"%s\" exited with error code: %u",
mutexname, result);
}
pthread_setcancelstate(oldState, NULL);
return result;
@@ -1954,7 +1967,7 @@ int get_device_id(uint8_t *id, int int_length) {
} else {
t = id;
int found = 0;
for (ifa = ifaddr; ((ifa != NULL) && (found == 0)); ifa = ifa->ifa_next) {
#ifdef AF_PACKET
if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET)) {
@@ -1980,7 +1993,6 @@ int get_device_id(uint8_t *id, int int_length) {
}
#endif
#endif
}
freeifaddrs(ifaddr);
}
+1 -1
View File
@@ -37,7 +37,7 @@ typedef enum {
typedef enum {
TOE_normal,
TOE_emergency,
TOE_dbus // a dbus request was made -- don't wait for the dbus thread to exit
TOE_dbus // a dbus request was made -- don't wait for the dbus thread to exit
} type_of_exit_type;
#define sps_extra_code_output_stalled 32768
+2 -7
View File
@@ -75,21 +75,18 @@ fi
AC_ARG_WITH([dummy],[AS_HELP_STRING([--with-dummy],[include the dummy audio back end])])
if test "x$with_dummy" = "xyes" ; then
AC_MSG_RESULT(include the dummy audio back end)
AC_DEFINE([CONFIG_DUMMY], 1, [Include a fake audio backend.])
fi
AM_CONDITIONAL([USE_DUMMY], [test "x$with_dummy" = "xyes" ])
AC_ARG_WITH([stdout],[AS_HELP_STRING([--with-stdout],[include the stdout audio back end])])
if test "x$with_stdout" = "xyes" ; then
AC_MSG_RESULT(include the stdout audio back end)
AC_DEFINE([CONFIG_STDOUT], 1, [Include an audio backend to output to standard output (stdout).])
fi
AM_CONDITIONAL([USE_STDOUT], [test "x$with_stdout" = "xyes"])
AC_ARG_WITH([pipe],[AS_HELP_STRING([--with-pipe],[include the pipe audio back end])])
if test "x$with_pipe" = "xyes" ; then
AC_MSG_RESULT(include the pipe audio back end)
AC_DEFINE([CONFIG_PIPE], 1, [Include an audio backend to output to a unix pipe.])
fi
AM_CONDITIONAL([USE_PIPE], [test "x$with_pipe" = "xyes" ])
@@ -320,9 +317,9 @@ if test "x$with_pw" = "xyes" ; then
PKG_CHECK_MODULES(
[PIPEWIRE], [libpipewire-0.3 >= 0.3.24],
[CFLAGS="${PIPEWIRE_CFLAGS} ${CFLAGS} -Wno-missing-field-initializers" LIBS="${PIPEWIRE_LIBS} ${LIBS}"],
[AC_MSG_ERROR(Pipewire support requires the libpipewire-dev library!)])
[AC_MSG_ERROR(Pipewire support requires the libpipewire library -- libpipewire-0.3-dev suggested!)])
else
AC_CHECK_LIB([pipewire], [pw_stream_queue_buffer], , AC_MSG_ERROR(Pipewire support requires the libpipewire-dev library.))
AC_CHECK_LIB([pipewire], [pw_stream_queue_buffer], , AC_MSG_ERROR(Pipewire support requires the libpipewire library -- libpipewire-0.3-dev suggested!))
fi
fi
AM_CONDITIONAL([USE_PW], [test "x$with_pw" = "xyes"])
@@ -409,7 +406,6 @@ AM_CONDITIONAL([USE_METADATA], [test "x$with_metadata" = "xyes"])
AC_ARG_WITH(airplay-2, [AS_HELP_STRING([--with-airplay-2],[Build for AirPlay 2])])
if test "x$with_airplay_2" = "xyes" ; then
AC_DEFINE([CONFIG_AIRPLAY_2], 1, [Build for AirPlay 2])
AC_MSG_RESULT(>>Include libraries required for AirPlay 2)
PKG_CHECK_MODULES([libplist], [libplist >= 2.0.0],[CFLAGS="${libplist_CFLAGS} ${CFLAGS}" LIBS="${libplist_LIBS} ${LIBS}"],[
PKG_CHECK_MODULES([libplist], [libplist-2.0 >= 2.0.0],[CFLAGS="${libplist_CFLAGS} ${CFLAGS}" LIBS="${libplist_LIBS} ${LIBS}"],[
AC_MSG_ERROR(AirPlay 2 support requires libplist 2.0.0 or later -- search for pkg libplist-dev on Debian or libplist-2.2.0 or later on FreeBSD!)
@@ -456,7 +452,6 @@ fi
AM_CONDITIONAL([HAVE_XMLTOMAN], [test -n "$XMLTOMAN"])
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([getopt_long.h])
AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h mach/mach.h memory.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h syslog.h unistd.h])
+2 -1
View File
@@ -206,7 +206,8 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
pthread_cleanup_push(addrinfo_cleanup, (void *)&res);
// only do this one at a time -- not sure it is necessary, but better safe than sorry
// int mutex_reply = sps_pthread_mutex_timedlock(&dacp_conversation_lock, 2000000, command, 1);
// int mutex_reply = sps_pthread_mutex_timedlock(&dacp_conversation_lock, 2000000, command,
// 1);
int mutex_reply = debug_mutex_lock(&dacp_conversation_lock, 2000000, 1);
// int mutex_reply = pthread_mutex_lock(&dacp_conversation_lock);
if (mutex_reply == 0) {
+8 -9
View File
@@ -572,17 +572,17 @@ gboolean notify_drift_tolerance_callback(ShairportSync *skeleton,
}
gboolean notify_volume_callback(ShairportSync *skeleton,
__attribute__((unused)) gpointer user_data) {
__attribute__((unused)) gpointer user_data) {
gdouble iv = shairport_sync_get_volume(skeleton);
if (((iv >= -30.0) && (iv <= 0.0)) || (iv == -144.0)) {
debug(2, ">> setting volume to %7.4f.", iv);
lock_player();
config.airplay_volume = iv;
if (playing_conn != NULL)
player_volume(iv, playing_conn);
unlock_player();
} else {
debug(1, ">> invalid volume: %f. Ignored.", iv);
shairport_sync_set_volume(skeleton, config.airplay_volume);
@@ -805,8 +805,7 @@ static gboolean on_handle_remote_command(ShairportSync *skeleton, GDBusMethodInv
return TRUE;
}
static gboolean on_handle_drop_session(ShairportSync *skeleton,
GDBusMethodInvocation *invocation,
static gboolean on_handle_drop_session(ShairportSync *skeleton, GDBusMethodInvocation *invocation,
__attribute__((unused)) gpointer user_data) {
if (playing_conn != NULL)
debug(1, ">> stopping current play session");
@@ -862,16 +861,16 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
G_CALLBACK(notify_loudness_threshold_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::drift-tolerance",
G_CALLBACK(notify_drift_tolerance_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::volume",
G_CALLBACK(notify_volume_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::volume", G_CALLBACK(notify_volume_callback),
NULL);
g_signal_connect(shairportSyncSkeleton, "handle-quit", G_CALLBACK(on_handle_quit), NULL);
g_signal_connect(shairportSyncSkeleton, "handle-remote-command",
G_CALLBACK(on_handle_remote_command), NULL);
g_signal_connect(shairportSyncSkeleton, "handle-drop-session",
G_CALLBACK(on_handle_drop_session), NULL);
g_signal_connect(shairportSyncSkeleton, "handle-drop-session", G_CALLBACK(on_handle_drop_session),
NULL);
g_signal_connect(shairportSyncDiagnosticsSkeleton, "notify::verbosity",
G_CALLBACK(notify_verbosity_callback), NULL);
+4 -5
View File
@@ -47,11 +47,10 @@
#include <avahi-client/lookup.h>
#include <avahi-common/alternative.h>
void threaded_poll_unlock(void *arg) { avahi_threaded_poll_unlock((AvahiThreadedPoll *)arg); }
#define pthread_avahi_threaded_poll_lock_and_push(mu) \
avahi_threaded_poll_lock(mu); \
#define pthread_avahi_threaded_poll_lock_and_push(mu) \
avahi_threaded_poll_lock(mu); \
pthread_cleanup_push(threaded_poll_unlock, (void *)mu)
#define check_avahi_response(debugLevelArg, veryUnLikelyArgumentName) \
@@ -347,8 +346,8 @@ static int avahi_update(char **txt_records, char **secondary_txt_records) {
else
selected_interface = AVAHI_IF_UNSPEC;
pthread_avahi_threaded_poll_lock_and_push(tpoll);
//avahi_threaded_poll_lock(tpoll);
//pthread_cleanup_push(threaded_poll_unlock, (void *)tpoll)
// avahi_threaded_poll_lock(tpoll);
// pthread_cleanup_push(threaded_poll_unlock, (void *)tpoll)
if (txt_records != NULL) {
if (text_record_string_list)
+6 -6
View File
@@ -81,9 +81,9 @@ static int fork_execvp(const char *file, char *const argv[]) {
}
static int mdns_external_avahi_register(char *ap1name, __attribute__((unused)) char *ap2name,
__attribute__((unused)) int port,
__attribute__((unused)) char **txt_records,
__attribute__((unused)) char **secondary_txt_records) {
__attribute__((unused)) int port,
__attribute__((unused)) char **txt_records,
__attribute__((unused)) char **secondary_txt_records) {
char mdns_port[6];
snprintf(mdns_port, sizeof(mdns_port), "%d", config.port);
@@ -123,9 +123,9 @@ static int mdns_external_avahi_register(char *ap1name, __attribute__((unused)) c
}
static int mdns_external_dns_sd_register(char *ap1name, __attribute__((unused)) char *ap2name,
__attribute__((unused)) int port,
__attribute__((unused)) char **txt_records,
__attribute__((unused)) char **secondary_txt_records) {
__attribute__((unused)) int port,
__attribute__((unused)) char **txt_records,
__attribute__((unused)) char **secondary_txt_records) {
char mdns_port[6];
snprintf(mdns_port, sizeof(mdns_port), "%d", config.port);
+2 -2
View File
@@ -39,8 +39,8 @@
static struct mdnsd *svr = NULL;
static int mdns_tinysvcmdns_register(char *ap1name, __attribute__((unused)) char *ap2name, int port,
__attribute__((unused)) char **txt_records,
__attribute__((unused)) char **secondary_txt_records) {
__attribute__((unused)) char **txt_records,
__attribute__((unused)) char **secondary_txt_records) {
struct ifaddrs *ifalist;
struct ifaddrs *ifa;
+3 -1
View File
@@ -20,7 +20,9 @@
#ifndef NQPTP_SHM_STRUCTURES_H
#define NQPTP_SHM_STRUCTURES_H
#define NQPTP_SHM_STRUCTURES_VERSION 7
#define NQPTP_INTERFACE_NAME "/nqptp"
#define NQPTP_SHM_STRUCTURES_VERSION 8
#define NQPTP_CONTROL_PORT 9000
// The control port expects a UDP packet with the first space-delimited string
+186 -90
View File
@@ -4,7 +4,7 @@
* All rights reserved.
*
* Modifications for audio synchronisation, AirPlay 2
* and related work, copyright (c) Mike Brady 2014 -- 2021
* and related work, copyright (c) Mike Brady 2014 -- 2022
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person
@@ -119,7 +119,7 @@ int32_t modulo_32_offset(uint32_t from, uint32_t to) { return to - from; }
void do_flush(uint32_t timestamp, rtsp_conn_info *conn);
static void ab_resync(rtsp_conn_info *conn) {
void ab_resync(rtsp_conn_info *conn) {
int i;
for (i = 0; i < BUFFER_FRAMES; i++) {
conn->audio_buffer[i].ready = 0;
@@ -326,7 +326,7 @@ static int init_alac_decoder(int32_t fmtp[12], rtsp_conn_info *conn) {
// We are going to go on that basis
// clang-format on
// clang-format on
alac_file *alac;
@@ -408,7 +408,7 @@ void get_audio_buffer_size_and_occupancy(unsigned int *size, unsigned int *occup
void player_put_packet(int original_format, seq_t seqno, uint32_t actual_timestamp, uint8_t *data,
int len, rtsp_conn_info *conn) {
// if it's original format, it has a valid seqno and must be decoded
// otherwise, it can take the next seqno and doesn't need decoding.
@@ -897,6 +897,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
conn->flush_rtp_timestamp = 0;
debug_mutex_unlock(&conn->flush_mutex, 0);
}
debug_mutex_lock(&conn->flush_mutex, 1000, 0);
pthread_cleanup_push(mutex_unlock, &conn->flush_mutex);
if (conn->flush_requested == 1) {
@@ -921,40 +922,92 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
abuf_t *firstPacket = conn->audio_buffer + BUFIDX(conn->ab_read);
abuf_t *lastPacket = conn->audio_buffer + BUFIDX(conn->ab_write - 1);
if ((firstPacket != NULL) && (firstPacket->ready)) {
// discard flushes more than 10 seconds into the future -- they are probably bogus
uint32_t first_frame_in_buffer = firstPacket->given_timestamp;
int32_t offset_from_first_frame =
(int32_t)(conn->flush_rtp_timestamp - first_frame_in_buffer);
if (offset_from_first_frame > (int)conn->input_rate * 10) {
debug(
1,
"flush request: sanity check -- flush frame %u is too far into the future from "
"the first frame %u -- discarded.",
conn->flush_rtp_timestamp, first_frame_in_buffer);
drop_request = 1;
} else {
if ((lastPacket != NULL) && (lastPacket->ready)) {
// we have enough information to check if the flush is needed or can be discarded
uint32_t last_frame_in_buffer =
lastPacket->given_timestamp + lastPacket->length - 1;
// now we have to work out if the flush frame is in the buffer
// if it is later than the end of the buffer, flush everything and keep the
// request active. if it is in the buffer, we need to flush part of the buffer.
// Actually we flush the entire buffer and drop the request. if it is before the
// buffer, no flush is needed. Drop the request.
if (offset_from_first_frame > 0) {
int32_t offset_to_last_frame =
(int32_t)(last_frame_in_buffer - conn->flush_rtp_timestamp);
if (offset_to_last_frame >= 0) {
int32_t offset_from_first_frame = conn->flush_rtp_timestamp - first_frame_in_buffer;
if ((lastPacket != NULL) && (lastPacket->ready)) {
// we have enough information to check if the flush is needed or can be discarded
uint32_t last_frame_in_buffer =
lastPacket->given_timestamp + lastPacket->length - 1;
// clang-format off
// Now we have to work out if the flush frame is in the buffer.
// If it is later than the end of the buffer, flush everything and keep the
// request active.
// If it is in the buffer, we need to flush part of the buffer.
// (Actually we flush the entire buffer and drop the request.)
// If it is before the buffer, no flush is needed. Drop the request.
// clang-format on
if (offset_from_first_frame > 0) {
int32_t offset_to_last_frame = last_frame_in_buffer - conn->flush_rtp_timestamp;
if (offset_to_last_frame >= 0) {
debug(2,
"flush request: flush frame %u active -- buffer contains %u frames, from "
"%u to %u.",
conn->flush_rtp_timestamp,
last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer,
last_frame_in_buffer);
// We need to drop all complete frames leading up to the frame containing
// the flush request frame.
int32_t offset_to_flush_frame = 0;
abuf_t *current_packet = NULL;
do {
current_packet = conn->audio_buffer + BUFIDX(conn->ab_read);
if (current_packet != NULL) {
uint32_t last_frame_in_current_packet =
current_packet->given_timestamp + current_packet->length - 1;
offset_to_flush_frame =
conn->flush_rtp_timestamp - last_frame_in_current_packet;
if (offset_to_flush_frame > 0) {
debug(2,
"flush to %u request: flush buffer %u, from "
"%u to %u. ab_write is: %u.",
conn->flush_rtp_timestamp, conn->ab_read,
current_packet->given_timestamp,
current_packet->given_timestamp + current_packet->length - 1,
conn->ab_write);
conn->ab_read++;
}
} else {
debug(1, "NULL current_packet");
}
} while ((current_packet == NULL) || (offset_to_flush_frame > 0));
// now remove any frames from the buffer that are before the flush frame itself.
int32_t frames_to_remove =
conn->flush_rtp_timestamp - current_packet->given_timestamp;
if (frames_to_remove > 0) {
debug(2, "%u frames to remove from current buffer", frames_to_remove);
void *dest = (void *)current_packet->data;
void *source = dest + conn->input_bytes_per_frame * frames_to_remove;
size_t frames_remaining = (current_packet->length - frames_to_remove);
memmove(dest, source, frames_remaining * conn->input_bytes_per_frame);
current_packet->given_timestamp = conn->flush_rtp_timestamp;
current_packet->length = frames_remaining;
}
debug(
2,
"flush request: flush frame %u complete -- buffer contains %u frames, from "
"%u to %u -- flushed to %u in buffer %u, with %u frames remaining.",
conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1,
first_frame_in_buffer, last_frame_in_buffer,
current_packet->given_timestamp, conn->ab_read,
last_frame_in_buffer - current_packet->given_timestamp + 1);
drop_request = 1;
} else {
if (conn->flush_rtp_timestamp == last_frame_in_buffer + 1) {
debug(
2,
"flush request: flush frame %u active -- buffer contains %u frames, from "
"flush request: flush frame %u completed -- buffer contained %u frames, "
"from "
"%u to %u",
conn->flush_rtp_timestamp,
last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer,
last_frame_in_buffer);
drop_request = 1;
flush_needed = 1;
} else {
debug(2,
"flush request: flush frame %u pending -- buffer contains %u frames, "
@@ -963,18 +1016,17 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
conn->flush_rtp_timestamp,
last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer,
last_frame_in_buffer);
flush_needed = 1;
}
} else {
debug(2,
"flush request: flush frame %u expired -- buffer contains %u frames, "
"from %u "
"to %u",
conn->flush_rtp_timestamp,
last_frame_in_buffer - first_frame_in_buffer + 1, first_frame_in_buffer,
last_frame_in_buffer);
drop_request = 1;
flush_needed = 1;
}
} else {
debug(2,
"flush request: flush frame %u expired -- buffer contains %u frames, "
"from %u "
"to %u",
conn->flush_rtp_timestamp, last_frame_in_buffer - first_frame_in_buffer + 1,
first_frame_in_buffer, last_frame_in_buffer);
drop_request = 1;
}
}
}
@@ -999,14 +1051,56 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
dac_delay = 0;
}
if (drop_request) {
debug(2, "flush request: request dropped.");
conn->flush_requested = 0;
conn->flush_rtp_timestamp = 0;
conn->flush_output_flushed = 0;
}
pthread_cleanup_pop(1); // unlock the conn->flush_mutex
// skip out-of-date frames, and even more if we haven't seen the first frame
int out_of_date = 1;
uint32_t should_be_frame;
uint64_t time_to_aim_for = local_time_now;
uint64_t desired_lead_time = 120000000;
if (conn->first_packet_timestamp == 0)
time_to_aim_for = time_to_aim_for + desired_lead_time;
while ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0) && (out_of_date != 0)) {
abuf_t *thePacket = conn->audio_buffer + BUFIDX(conn->ab_read);
if ((thePacket != NULL) && (thePacket->ready)) {
local_time_to_frame(time_to_aim_for, &should_be_frame, conn);
// debug(1,"should_be frame is %u.",should_be_frame);
int32_t frame_difference = thePacket->given_timestamp - should_be_frame;
if (frame_difference < 0) {
debug(2, "Dropping out of date packet %u with timestamp %u. Lead time is %f seconds.",
conn->ab_read, thePacket->given_timestamp,
frame_difference * 1.0 / 44100.0 + desired_lead_time * 0.000000001);
conn->ab_read++;
} else {
if (conn->first_packet_timestamp == 0)
debug(2, "Accepting packet %u with timestamp %u. Lead time is %f seconds.",
conn->ab_read, thePacket->given_timestamp,
frame_difference * 1.0 / 44100.0 + desired_lead_time * 0.000000001);
out_of_date = 0;
}
} else {
debug(2, "Packet %u empty or not ready.", conn->ab_read);
conn->ab_read++;
}
}
if (conn->ab_synced) {
curframe = conn->audio_buffer + BUFIDX(conn->ab_read);
if (curframe != NULL) {
uint64_t should_be_time;
frame_to_local_time(curframe->given_timestamp, &should_be_time, conn);
int64_t time_difference = should_be_time - local_time_now;
debug(3, "Check packet from buffer %u, timestamp %u, %f seconds ahead.", conn->ab_read,
curframe->given_timestamp, 0.000000001 * time_difference);
} else {
debug(3, "Check packet from buffer %u, empty.", conn->ab_read);
}
if ((conn->ab_read != conn->ab_write) &&
(curframe->ready)) { // it could be synced and empty, under
@@ -1034,13 +1128,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
conn->first_packet_timestamp =
curframe->given_timestamp; // we will keep buffering until we are
// supposed to start playing this
#ifdef CONFIG_METADATA
// say we have started receiving frames here
debug(2, "pffr");
send_ssnc_metadata(
'pffr', NULL, 0,
0); // "first frame received", but don't wait if the queue is locked
#endif
// Here, calculate when we should start playing. We need to know when to allow the
// packets to be sent to the player.
@@ -1064,24 +1152,16 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
int64_t lt = conn->first_packet_time_to_play - local_time_now;
if (lt < 100000000) {
debug(2,
"Connection %d: Short lead time for first frame %" PRId64
": %f seconds. Flushing 0.5 seconds",
conn->connection_number, conn->first_packet_timestamp, lt * 0.000000001);
do_flush(conn->first_packet_timestamp + 5 * 4410, conn);
} else {
debug(2, "Connection %d: Lead time for first frame %" PRId64 ": %f seconds.",
conn->connection_number, conn->first_packet_timestamp, lt * 0.000000001);
}
/*
int64_t lateness = local_time_now - conn->first_packet_time_to_play;
if (lateness > 0) {
debug(1, "First packet is %" PRId64 " nanoseconds late! Flushing 0.5
seconds", lateness);
}
*/
// can't be too late because we skipped late packets already, FLW.
debug(2, "Connection %d: Lead time for first frame %" PRId64 ": %f seconds.",
conn->connection_number, conn->first_packet_timestamp, lt * 0.000000001);
#ifdef CONFIG_METADATA
// say we have started receiving frames here
debug(2, "pffr");
send_ssnc_metadata(
'pffr', NULL, 0,
0); // "first frame received", but don't wait if the queue is locked
#endif
}
if (conn->first_packet_time_to_play != 0) {
@@ -1168,7 +1248,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
conn->previous_random_number = generate_zero_frames(
silence, fs, config.output_format, conn->enable_dither,
conn->previous_random_number);
config.output->play(silence, fs);
config.output->play(silence, fs, play_samples_are_untimed, 0, 0);
debug(3, "Sent %" PRId64 " frames of silence", fs);
free(silence);
have_sent_prefiller_silence = 1;
@@ -1213,7 +1293,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
conn->previous_random_number =
generate_zero_frames(silence, fs, config.output_format, conn->enable_dither,
conn->previous_random_number);
config.output->play(silence, fs);
config.output->play(silence, fs, play_samples_are_untimed, 0, 0);
free(silence);
}
frame_gap -= fs;
@@ -1299,7 +1379,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
// reset_input_flow_metrics(conn); // don't do a full flush parameters reset
conn->initial_reference_time = 0;
conn->initial_reference_timestamp = 0;
conn->first_packet_timestamp = 0; // make sure the first packet isn't late
conn->first_packet_timestamp = 0; // make sure the first packet isn't late
}
do_wait = 1;
}
@@ -1311,9 +1391,9 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
if (conn->input_rate == 0)
die("input_rate is zero -- should never happen!");
uint64_t time_to_wait_for_wakeup_ns =
1000000000 / conn->input_rate; // this is time period of one frame
1000000000 / conn->input_rate; // this is time period of one frame
time_to_wait_for_wakeup_ns *= 12 * 352; // two full 352-frame packets
time_to_wait_for_wakeup_ns /= 3; // two thirds of a packet time
time_to_wait_for_wakeup_ns /= 3; // two thirds of a packet time
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
uint64_t time_of_wakeup_ns = get_realtime_in_ns() + time_to_wait_for_wakeup_ns;
@@ -1580,7 +1660,7 @@ int ap2_realtime_synced_stream_statistics_print_profile[] = {2, 2, 2, 0, 2, 1,
int ap2_realtime_nosync_stream_statistics_print_profile[] = {2, 0, 0, 0, 2, 1, 1, 2, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0};
int ap2_realtime_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 0, 2, 1, 1, 2, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0};
int ap2_buffered_synced_stream_statistics_print_profile[] = {2, 2, 2, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 2, 2, 0, 0};
int ap2_buffered_synced_stream_statistics_print_profile[] = {2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 2, 2, 0, 0};
int ap2_buffered_nosync_stream_statistics_print_profile[] = {2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0};
int ap2_buffered_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0};
// clang-format on
@@ -1644,8 +1724,8 @@ void player_thread_cleanup_handler(void *arg) {
":%02" PRId64 ". "
"Output: %0.2f (raw), %0.2f (corrected) "
"frames per second.",
conn->connection_number, elapsedHours, elapsedMin, elapsedSec,
conn->raw_frame_rate, conn->corrected_frame_rate);
conn->connection_number, elapsedHours, elapsedMin, elapsedSec, conn->raw_frame_rate,
conn->corrected_frame_rate);
else
inform("Connection %d: Playback Stopped. Total playing time %02" PRId64 ":%02" PRId64
":%02" PRId64 ".",
@@ -1746,8 +1826,10 @@ void *player_thread_func(void *arg) {
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
uint64_t previous_frames_played = 0; // initialised to avoid a "possibly uninitialised" warning
uint64_t previous_raw_measurement_time = 0; // initialised to avoid a "possibly uninitialised" warning
uint64_t previous_corrected_measurement_time = 0; // initialised to avoid a "possibly uninitialised" warning
uint64_t previous_raw_measurement_time =
0; // initialised to avoid a "possibly uninitialised" warning
uint64_t previous_corrected_measurement_time =
0; // initialised to avoid a "possibly uninitialised" warning
int previous_frames_played_valid = 0;
// pthread_cleanup_push(player_thread_initial_cleanup_handler, arg);
@@ -2146,7 +2228,8 @@ void *player_thread_func(void *arg) {
conn->previous_random_number = generate_zero_frames(
silence, conn->max_frames_per_packet * conn->output_sample_ratio,
config.output_format, conn->enable_dither, conn->previous_random_number);
config.output->play(silence, conn->max_frames_per_packet * conn->output_sample_ratio);
config.output->play(silence, conn->max_frames_per_packet * conn->output_sample_ratio,
play_samples_are_untimed, 0, 0);
free(silence);
}
} else if (conn->play_number_after_flush < 10) {
@@ -2168,7 +2251,8 @@ void *player_thread_func(void *arg) {
conn->previous_random_number = generate_zero_frames(
silence, conn->max_frames_per_packet * conn->output_sample_ratio,
config.output_format, conn->enable_dither, conn->previous_random_number);
config.output->play(silence, conn->max_frames_per_packet * conn->output_sample_ratio);
config.output->play(silence, conn->max_frames_per_packet * conn->output_sample_ratio,
play_samples_are_untimed, 0, 0);
free(silence);
}
} else {
@@ -2458,7 +2542,8 @@ void *player_thread_func(void *arg) {
#endif
statistics_item("Nominal FPS", "%*.2f", 11, conn->remote_frame_rate);
statistics_item("Received FPS", "%*.2f", 12, conn->input_frame_rate);
// only make the next two columns appear if we are getting stats information from the back end
// only make the next two columns appear if we are getting stats information from
// the back end
if (config.output->stats) {
if (conn->frame_rate_valid) {
statistics_item("Output FPS (r)", "%*.2f", 14, conn->raw_frame_rate);
@@ -2543,7 +2628,8 @@ void *player_thread_func(void *arg) {
conn->connection_number);
}
} else {
if (resp != -EBUSY) // delay errors can be reported if the device is (hopefully temporarily) busy
if (resp != -EBUSY) // delay errors can be reported if the device is (hopefully
// temporarily) busy
debug(1, "Delay error %d when checking running latency.", resp);
}
}
@@ -2572,10 +2658,11 @@ void *player_thread_func(void *arg) {
// interpreted as a signed number, should yield the difference _and_ the ordering.
sync_error = should_be_frame - will_be_frame; // this is done in int64_t form
// int64_t t_ping = should_be_frame - conn->anchor_rtptime;
// if (t_ping < 0)
// debug(1, "Frame %" PRIu64 " is %" PRId64 " frames before anchor time %" PRIu64 ".", should_be_frame, -t_ping, conn->anchor_rtptime);
// debug(1, "Frame %" PRIu64 " is %" PRId64 " frames before anchor time %" PRIu64 ".",
// should_be_frame, -t_ping, conn->anchor_rtptime);
// sign-extend the r-bit unsigned int calculation by treating it as an r-bit signed
// integer
@@ -2628,7 +2715,8 @@ void *player_thread_func(void *arg) {
"final sync adjustment: %" PRId64
" silent frames added with a bias of %" PRId64 " frames.",
-sync_error, first_frame_early_bias);
config.output->play(final_adjustment_silence, final_adjustment_length_sized);
config.output->play(final_adjustment_silence, final_adjustment_length_sized,
play_samples_are_untimed, 0, 0);
free(final_adjustment_silence);
} else {
warn("Failed to allocate memory for a final_adjustment_silence buffer of %d "
@@ -2690,11 +2778,12 @@ void *player_thread_func(void *arg) {
(int64_t)(config.resyncthreshold * config.output_rate); // number of samples
if ((sync_error > 0) && (sync_error > filler_length)) {
debug(1,
"Large positive sync error of %" PRId64
"Large positive (i.e. late) sync error of %" PRId64
" frames (%f seconds), at frame: %" PRIu32 ".",
sync_error, (sync_error * 1.0) / config.output_rate,
inframe->given_timestamp);
//debug(1, "%" PRId64 " frames sent to DAC. DAC buffer contains %" PRId64 " frames.",
// debug(1, "%" PRId64 " frames sent to DAC. DAC buffer contains %" PRId64 "
// frames.",
// frames_sent_for_play, actual_delay);
// the sync error is output frames, but we have to work out how many source frames
// to drop there may be a multiple (the conn->output_sample_ratio) of output frames
@@ -2714,11 +2803,11 @@ void *player_thread_func(void *arg) {
} else if ((sync_error < 0) && ((-sync_error) > filler_length)) {
debug(1,
"Large negative sync error of %" PRId64
"Large negative (i.e. early) sync error of %" PRId64
" frames (%f seconds), at frame: %" PRIu32 ".",
sync_error, (sync_error * 1.0) / config.output_rate,
inframe->given_timestamp);
debug(1, "%" PRId64 " frames sent to DAC. DAC buffer contains %" PRId64 " frames.",
debug(3, "%" PRId64 " frames sent to DAC. DAC buffer contains %" PRId64 " frames.",
frames_sent_for_play, actual_delay);
int64_t silence_length = -sync_error;
if (silence_length > (filler_length * 5))
@@ -2732,7 +2821,8 @@ void *player_thread_func(void *arg) {
conn->enable_dither, conn->previous_random_number);
debug(2, "Play a silence of %d frames.", silence_length_sized);
config.output->play(long_silence, silence_length_sized);
config.output->play(long_silence, silence_length_sized, play_samples_are_untimed,
0, 0);
free(long_silence);
} else {
warn("Failed to allocate memory for a long_silence buffer of %d frames for a "
@@ -2917,7 +3007,10 @@ void *player_thread_func(void *arg) {
generate_zero_frames(conn->outbuf, play_samples, config.output_format,
conn->enable_dither, conn->previous_random_number);
}
config.output->play(conn->outbuf, play_samples);
uint64_t should_be_time;
frame_to_local_time(inframe->given_timestamp, &should_be_time, conn);
config.output->play(conn->outbuf, play_samples, play_samples_are_timed,
inframe->given_timestamp, should_be_time);
}
}
@@ -2962,7 +3055,10 @@ void *player_thread_func(void *arg) {
generate_zero_frames(conn->outbuf, play_samples, config.output_format,
conn->enable_dither, conn->previous_random_number);
}
config.output->play(conn->outbuf, play_samples); // remove the (short*)!
uint64_t should_be_time;
frame_to_local_time(inframe->given_timestamp, &should_be_time, conn);
config.output->play(conn->outbuf, play_samples, play_samples_are_timed,
inframe->given_timestamp, should_be_time);
}
}
+6 -1
View File
@@ -128,7 +128,6 @@ typedef struct {
int is_encrypted;
} pair_cipher_bundle; // cipher context and buffers
typedef struct {
struct pair_setup_context *setup_ctx;
struct pair_verify_context *verify_ctx;
@@ -151,6 +150,7 @@ typedef struct flush_request_t {
typedef struct {
int connection_number; // for debug ID purposes, nothing else...
int resend_interval; // this is really just for debugging
int rtsp_link_is_idle; // if true, this indicates if the client asleep
char *UserAgent; // free this on teardown
int AirPlayVersion; // zero if not an AirPlay session. Used to help calculate latency
int latency_warning_issued;
@@ -287,6 +287,9 @@ typedef struct {
// this is what connects an rtp timestamp to the remote time
int udp_clock_is_initialised;
int udp_clock_sender_is_initialised;
int anchor_remote_info_is_valid;
// these can be modified if the master clock changes over time
@@ -426,6 +429,8 @@ void get_audio_buffer_size_and_occupancy(unsigned int *size, unsigned int *occup
int32_t modulo_32_offset(uint32_t from, uint32_t to);
void ab_resync(rtsp_conn_info *conn);
int player_prepare_to_play(rtsp_conn_info *conn);
int player_play(rtsp_conn_info *conn);
int player_stop(rtsp_conn_info *conn);
+1 -3
View File
@@ -39,12 +39,10 @@
#endif
#define __STDC_FORMAT_MACROS
#include "common.h"
#include "ptp-utilities.h"
#include <inttypes.h>
#include <unistd.h>
#include "nqptp-shm-structures.h"
#include "ptp-utilities.h"
int shm_fd;
void *mapped_addr = NULL;
+1
View File
@@ -27,6 +27,7 @@
#define __PTP_UTILITIES_H
#include "config.h"
#include "nqptp-shm-structures.h"
#include <stdint.h>
int ptp_get_clock_info(uint64_t *actual_clock_id, uint64_t *time_of_sample, uint64_t *raw_offset,
+709 -711
View File
File diff suppressed because it is too large Load Diff
+97 -48
View File
@@ -96,7 +96,6 @@
#include "dbus-service.h"
#endif
#include "mdns.h"
// mDNS advertisement strings
@@ -502,14 +501,9 @@ int pc_queue_get_item(pc_queue *the_queue, void *the_stuff) {
#endif
void lock_player() {
debug_mutex_lock(&playing_conn_lock, 1000000, 3);
}
void unlock_player() {
debug_mutex_unlock(&playing_conn_lock, 3);
}
void lock_player() { debug_mutex_lock(&playing_conn_lock, 1000000, 3); }
void unlock_player() { debug_mutex_unlock(&playing_conn_lock, 3); }
int have_play_lock(rtsp_conn_info *conn) {
int response = 0;
@@ -552,12 +546,11 @@ void release_play_lock(rtsp_conn_info *conn) {
debug(2, "Connection %d: play lock released.", conn->connection_number);
else
debug(2, "Play lock released.");
playing_conn = NULL; // let it go
playing_conn = NULL; // let it go
}
unlock_player();
}
// make conn the playing_conn, and kill the current session if permitted
int get_play_lock(rtsp_conn_info *conn, int allow_session_interruption) {
if (conn != NULL)
@@ -630,7 +623,7 @@ int get_play_lock(rtsp_conn_info *conn, int allow_session_interruption) {
if (conn != NULL)
debug(2, "Connection %d: Got player lock.", conn->connection_number);
else
debug(2, "Player released.");
debug(2, "Player released.");
response = 0;
}
return response;
@@ -1220,7 +1213,18 @@ static ssize_t write_encrypted(rtsp_conn_info *conn, const void *buf, size_t cou
*/
#endif
ssize_t read_from_rtsp_connection(rtsp_conn_info *conn, void *buf, size_t count) {
ssize_t timed_read_from_rtsp_connection(rtsp_conn_info *conn, uint64_t wait_time, void *buf,
size_t count) {
struct timeval tv;
tv.tv_sec = wait_time / 1000000000; // seconds
tv.tv_usec = (wait_time % 1000000000) / 1000; // microseconds
if (setsockopt(conn->fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) != 0) {
char errorstring[1024];
strerror_r(errno, (char *)errorstring, sizeof(errorstring));
debug(1, "could not set time limit on timed_read_from_rtsp_connection -- error %d \"%s\".",
errno, errorstring);
}
#ifdef CONFIG_AIRPLAY_2
if (conn->ap2_pairing_context.control_cipher_bundle.cipher_ctx) {
conn->ap2_pairing_context.control_cipher_bundle.is_encrypted = 1;
@@ -1233,6 +1237,34 @@ ssize_t read_from_rtsp_connection(rtsp_conn_info *conn, void *buf, size_t count)
#endif
}
ssize_t read_from_rtsp_connection(rtsp_conn_info *conn, void *buf, size_t count) {
// first try to read with a timeout, to see if there is any traffic...
ssize_t response = timed_read_from_rtsp_connection(conn, 4000000000L, buf, count);
if ((response == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
if (conn->rtsp_link_is_idle == 0) {
conn->rtsp_link_is_idle = 1;
#ifdef CONFIG_AIRPLAY_2
conn->last_anchor_info_is_valid = 0;
#endif
conn->anchor_remote_info_is_valid = 0;
conn->udp_clock_sender_is_initialised = 0;
conn->udp_clock_is_initialised = 0;
debug(1, "Connection %d: RTSP connection is idle.", conn->connection_number);
}
response = timed_read_from_rtsp_connection(conn, 0, buf, count);
}
if (conn->rtsp_link_is_idle == 1) {
conn->local_to_remote_time_difference_measurement_time = 0;
conn->local_to_remote_time_difference = 0;
conn->rtsp_link_is_idle = 0;
conn->first_packet_timestamp = 0;
conn->input_frame_rate_starting_point_is_valid = 0;
ab_resync(conn);
debug(1, "Connection %d: RTSP connection traffic has resumed.", conn->connection_number);
}
return response;
}
enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_message **the_packet) {
*the_packet = NULL; // need this for error handling
@@ -1418,6 +1450,7 @@ int msg_write_response(rtsp_conn_info *conn, rtsp_message *resp) {
{403, "Unauthorized"},
{404, "Not Found"},
{451, "Unavailable"},
{456, "Header Field Not Valid for Resource"},
{500, "Internal Server Error"},
{501, "Not Implemented"}};
// 451 is really "Unavailable For Legal Reasons"!
@@ -1482,7 +1515,8 @@ int msg_write_response(rtsp_conn_info *conn, rtsp_message *resp) {
#ifdef CONFIG_AIRPLAY_2
ssize_t reply;
if (conn->ap2_pairing_context.control_cipher_bundle.is_encrypted) {
reply = write_encrypted(conn->fd, &conn->ap2_pairing_context.control_cipher_bundle, pkt, p - pkt);
reply =
write_encrypted(conn->fd, &conn->ap2_pairing_context.control_cipher_bundle, pkt, p - pkt);
} else {
reply = write(conn->fd, pkt, p - pkt);
}
@@ -1909,7 +1943,8 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
pthread_cleanup_push(mutex_unlock, &conn->flush_mutex);
conn->ap2_rate = rate;
if ((rate & 1) != 0) {
debug(2, "Connection %d: Start playing, with anchor clock %" PRIx64 ".", conn->connection_number, conn->networkTimeTimelineID);
debug(2, "Connection %d: Start playing, with anchor clock %" PRIx64 ".",
conn->connection_number, conn->networkTimeTimelineID);
activity_monitor_signify_activity(1);
conn->ap2_play_enabled = 1;
} else {
@@ -2313,6 +2348,9 @@ void handle_configure(rtsp_conn_info *conn __attribute__((unused)),
void handle_feedback(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message *req,
__attribute__((unused)) rtsp_message *resp) {
debug(2, "Connection %d: POST %s Content-Length %d", conn->connection_number, req->path,
req->contentlength);
debug_log_rtsp_message(2, NULL, req);
if (conn->airplay_stream_category == remote_control_stream) {
plist_t array_plist = plist_new_array();
@@ -2349,7 +2387,9 @@ void handle_feedback(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message
void handle_command(__attribute__((unused)) rtsp_conn_info *conn, rtsp_message *req,
__attribute__((unused)) rtsp_message *resp) {
debug_log_rtsp_message(3, "POST /command", req);
debug(2, "Connection %d: POST %s Content-Length %d", conn->connection_number, req->path,
req->contentlength);
debug_log_rtsp_message(2, NULL, req);
if (rtsp_message_contains_plist(req)) {
plist_t command_dict = NULL;
plist_from_memory(req->content, req->contentlength, &command_dict);
@@ -2468,12 +2508,11 @@ void handle_setpeers(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp
char timing_list_message[4096];
timing_list_message[0] = 'T';
timing_list_message[1] = 0;
// ensure the client itself is first -- it's okay if it's duplicated later
strncat(timing_list_message, " ",
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
strncat(timing_list_message, " ", sizeof(timing_list_message) - 1 - strlen(timing_list_message));
strncat(timing_list_message, (const char *)&conn->client_ip_string,
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
plist_t addresses_array = NULL;
plist_from_memory(req->content, req->contentlength, &addresses_array);
@@ -2542,32 +2581,36 @@ void teardown_phase_two(rtsp_conn_info *conn) {
// we are being asked to disconnect
// this can be called more than once on the same connection --
// by the player itself but also by the play seesion being killed
debug(2, "Connection %d: TEARDOWN a %s connection.", conn->connection_number,
get_category_string(conn->airplay_stream_category));
debug(2, "Connection %d: TEARDOWN %s connection.", conn->connection_number,
get_category_string(conn->airplay_stream_category));
if (conn->airplay_stream_category == remote_control_stream) {
if (conn->rtp_data_thread) {
debug(2, "Connection %d (RC): TEARDOWN Delete Data Thread.", conn->connection_number);
debug(2, "Connection %d: TEARDOWN %s Delete Data Thread.", conn->connection_number,
get_category_string(conn->airplay_stream_category));
pthread_cancel(*conn->rtp_data_thread);
pthread_join(*conn->rtp_data_thread, NULL);
free(conn->rtp_data_thread);
conn->rtp_data_thread = NULL;
}
debug(2, "Connection %d: TEARDOWN Close Data Socket.", conn->connection_number);
if (conn->data_socket) {
debug(2, "Connection %d: TEARDOWN %s Close Data Socket.", conn->connection_number,
get_category_string(conn->airplay_stream_category));
close(conn->data_socket);
conn->data_socket = 0;
}
}
if (conn->rtp_event_thread) {
debug(2, "Connection %d: TEARDOWN Delete Event Thread.", conn->connection_number);
debug(2, "Connection %d: TEARDOWN %s Delete Event Thread.", conn->connection_number,
get_category_string(conn->airplay_stream_category));
pthread_cancel(*conn->rtp_event_thread);
pthread_join(*conn->rtp_event_thread, NULL);
free(conn->rtp_event_thread);
conn->rtp_event_thread = NULL;
}
debug(2, "Connection %d: TEARDOWN Close Event Socket.", conn->connection_number);
if (conn->event_socket) {
debug(2, "Connection %d: TEARDOWN %s Close Event Socket.", conn->connection_number,
get_category_string(conn->airplay_stream_category));
close(conn->event_socket);
conn->event_socket = 0;
}
@@ -2611,16 +2654,20 @@ void handle_teardown_2(rtsp_conn_info *conn, __attribute__((unused)) rtsp_messag
plist_t streams = plist_dict_get_item(messagePlist, "streams");
if (streams) {
debug(2, "Connection %d: TEARDOWN a %s.", conn->connection_number,
debug(2, "Connection %d: TEARDOWN %s Close the stream.", conn->connection_number,
get_category_string(conn->airplay_stream_category));
// we are being asked to close a stream
teardown_phase_one(conn);
plist_free(streams);
debug(2, "Connection %d: TEARDOWN phase one complete", conn->connection_number);
debug(2, "Connection %d: TEARDOWN %s Close the stream complete", conn->connection_number,
get_category_string(conn->airplay_stream_category));
} else {
debug(2, "Connection %d: TEARDOWN %s Close the connection.", conn->connection_number,
get_category_string(conn->airplay_stream_category));
teardown_phase_one(conn); // try to do phase one anyway
teardown_phase_two(conn);
debug(2, "Connection %d: TEARDOWN phase two complete", conn->connection_number);
debug(2, "Connection %d: TEARDOWN %s Close the connection complete", conn->connection_number,
get_category_string(conn->airplay_stream_category));
}
//} else {
// warn("Connection %d TEARDOWN received without having the player (no ANNOUNCE?)",
@@ -2672,7 +2719,6 @@ void handle_teardown(rtsp_conn_info *conn, __attribute__((unused)) rtsp_message
}
void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
// TODO -- don't know what this is for in AP2
debug_log_rtsp_message(2, "FLUSH request", req);
debug(3, "Connection %d: FLUSH", conn->connection_number);
char *p = NULL;
@@ -2698,11 +2744,7 @@ void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
send_metadata('ssnc', 'flsr', NULL, 0, NULL, 0);
#endif
// hack -- ignore it for airplay 2
#ifdef CONFIG_AIRPLAY_2
if (conn->airplay_type != ap_2)
#endif
player_flush(rtptime, conn); // will not crash even it there is no player thread.
player_flush(rtptime, conn); // will not crash even it there is no player thread.
resp->respcode = 200;
} else {
@@ -2839,10 +2881,9 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
// ensure the client itself is first -- it's okay if it's duplicated later
strncat(timing_list_message, " ",
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
strncat(timing_list_message, (const char *)&conn->client_ip_string,
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
sizeof(timing_list_message) - 1 - strlen(timing_list_message));
plist_t timing_peer_info = plist_dict_get_item(messagePlist, "timingPeerInfo");
if (timing_peer_info) {
@@ -2988,7 +3029,11 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
debug(2, "Connection %d SETUP (RC): TCP Remote Control event port opened: %u.",
conn->connection_number, conn->local_event_port);
if (conn->rtp_event_thread != NULL)
debug(1, "previous rtp_event_thread allocation not freed, it seems.");
debug(1,
"Connection %d SETUP (RC): previous rtp_event_thread allocation not freed, it "
"seems.",
conn->connection_number);
conn->rtp_event_thread = malloc(sizeof(pthread_t));
if (conn->rtp_event_thread == NULL)
die("Couldn't allocate space for pthread_t");
@@ -3209,8 +3254,10 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
plist_dict_set_item(setupResponsePlist, "streams", streams_array);
resp->respcode = 200;
} else if (conn->airplay_stream_category == remote_control_stream) {
debug(2, "Connection %d (RC): SETUP: Remote Control Stream received.", conn->connection_number);
debug(2, "Connection %d (RC): SETUP: Remote Control Stream received from %s.",
conn->connection_number, conn->client_ip_string);
debug_log_rtsp_message(2, "Remote Control Stream SETUP incoming message", req);
/*
// get a port to use as an data port
// bind a new TCP port and get a socket
conn->local_data_port = 0; // any port
@@ -3226,10 +3273,9 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
debug(1, "Connection %d SETUP (RC): TCP Remote Control data port opened: %u.",
conn->connection_number, conn->local_data_port);
if (conn->rtp_data_thread != NULL)
debug(1, "previous rtp_data_thread allocation not freed, it seems.");
conn->rtp_data_thread = malloc(sizeof(pthread_t));
if (conn->rtp_data_thread == NULL)
die("Couldn't allocate space for pthread_t");
debug(1, "Connection %d SETUP (RC): previous rtp_data_thread allocation not freed, it
seems.", conn->connection_number); conn->rtp_data_thread = malloc(sizeof(pthread_t)); if
(conn->rtp_data_thread == NULL) die("Couldn't allocate space for pthread_t");
pthread_create(conn->rtp_data_thread, NULL, &rtp_data_receiver, (void *)conn);
@@ -3241,6 +3287,7 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
plist_t coreResponseArray = plist_new_array();
plist_array_append_item(coreResponseArray, coreResponseDict);
plist_dict_set_item(setupResponsePlist, "streams", coreResponseArray);
*/
resp->respcode = 200;
} else {
debug(1, "Connection %d: SETUP: Stream received but no airplay category set. Nothing done.",
@@ -3430,7 +3477,7 @@ void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *req,
if (dbus_service_is_running()) {
shairport_sync_set_volume(shairportSyncSkeleton, config.airplay_volume);
}
#endif
#endif
} else if (strncmp(cp, "progress: ", strlen("progress: ")) ==
0) { // this can be sent even when metadata is not solicited
@@ -4171,7 +4218,7 @@ static void handle_get_parameter(__attribute__((unused)) rtsp_conn_info *conn, r
if ((req->content) && (req->contentlength == strlen("volume\r\n")) &&
strstr(req->content, "volume") == req->content) {
debug(1, "Connection %d: Current volume (%.6f) requested", conn->connection_number,
debug(2, "Connection %d: Current volume (%.6f) requested", conn->connection_number,
config.airplay_volume);
char *p = malloc(128); // will be automatically deallocated with the response is deleted
if (p) {
@@ -4401,11 +4448,13 @@ static void handle_announce(rtsp_conn_info *conn, rtsp_message *req, rtsp_messag
if ((paesiv == NULL) && (prsaaeskey == NULL)) {
// debug(1,"Unencrypted session requested?");
conn->stream.encrypted = 0;
} else {
} else if ((paesiv != NULL) && (prsaaeskey != NULL)){
conn->stream.encrypted = 1;
// debug(1,"Encrypted session requested");
} else {
warn("Invalid Announce message -- missing paesiv or prsaaeskey.");
goto out;
}
if (conn->stream.encrypted) {
int len, keylen;
uint8_t *aesiv = base64_dec(paesiv, &len);
+14 -12
View File
@@ -159,7 +159,7 @@ int has_fltp_capable_aac_decoder(void) {
const enum AVSampleFormat *p = codec->sample_fmts;
if (p != NULL) {
while ((has_capability == 0) && (*p != AV_SAMPLE_FMT_NONE)) {
if (*p == AV_SAMPLE_FMT_FLTP)
if (*p == AV_SAMPLE_FMT_FLTP)
has_capability = 1;
p++;
}
@@ -1379,7 +1379,7 @@ int parse_options(int argc, char **argv) {
config.appName, temporary_airplay_id);
// debug(1, "smi name: \"%s\"", shared_memory_interface_name);
config.nqptp_shared_memory_interface_name = strdup(shared_memory_interface_name);
config.nqptp_shared_memory_interface_name = strdup(NQPTP_INTERFACE_NAME);
char apids[6 * 2 + 5 + 1]; // six pairs of digits, 5 colons and a NUL
apids[6 * 2 + 5] = 0; // NUL termination
@@ -1454,9 +1454,9 @@ int parse_options(int argc, char **argv) {
// now, do the substitutions in the service name
char hostname[100];
gethostname(hostname, 100);
// strip off a terminating .<anything>, e.g. .local from the hostname
char *last_dot = strrchr(hostname,'.');
char *last_dot = strrchr(hostname, '.');
if (last_dot != NULL)
*last_dot = '\0';
@@ -1574,10 +1574,10 @@ void exit_function() {
if (g_main_loop) {
debug(2, "Stopping D-Bus Loop Thread");
g_main_loop_quit(g_main_loop);
// If the request to exit has come from the D-Bus system,
// If the request to exit has come from the D-Bus system,
// the D-Bus Loop Thread will not exit until the request is completed
// so don't wait for it
// so don't wait for it
if (type_of_exit_cleanup != TOE_dbus)
pthread_join(dbus_thread, NULL);
}
@@ -2023,10 +2023,10 @@ int main(int argc, char **argv) {
apfh = apfh >> 32;
uint32_t apf32 = apf;
uint32_t apfh32 = apfh;
debug(1, "startup in Airplay 2 mode with features 0x%" PRIx32 ",0x%" PRIx32 " on device \"%s\".",
debug(1, "startup in AirPlay 2 mode, with features 0x%" PRIx32 ",0x%" PRIx32 " on device \"%s\".",
apf32, apfh32, config.airplay_device_id);
#else
debug(1, "startup in Airplay 1 mode.");
debug(1, "startup in classic Airplay (aka \"AirPlay 1\") mode.");
#endif
// control-c (SIGINT) cleanly
@@ -2180,8 +2180,9 @@ int main(int argc, char **argv) {
debug(1, "mdns backend \"%s\".", strnull(config.mdns_name));
debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency);
debug(1, "interpolation setting is \"%s\".",
config.packet_stuffing == ST_basic ? "basic"
: config.packet_stuffing == ST_soxr ? "soxr" : "auto");
config.packet_stuffing == ST_basic ? "basic"
: config.packet_stuffing == ST_soxr ? "soxr"
: "auto");
debug(1, "interpolation soxr_delay_threshold is %d.", config.soxr_delay_threshold);
debug(1, "resync time is %f seconds.", config.resyncthreshold);
debug(1, "allow a session to be interrupted: %d.", config.allow_session_interruption);
@@ -2338,7 +2339,8 @@ int main(int argc, char **argv) {
ptp_send_control_message_string("T"); // get nqptp to create the named shm interface
usleep(ptp_wait_interval_us);
ptp_check_times++;
} while ((ptp_shm_interface_open() != 0) && (ptp_check_times < (10000000 / ptp_wait_interval_us)));
} while ((ptp_shm_interface_open() != 0) &&
(ptp_check_times < (10000000 / ptp_wait_interval_us)));
if (ptp_shm_interface_open() != 0) {
die("Can't access NQPTP! Is it installed and running?");