Fix a bug causing problems in CYGWIN 64, fix a bug causing excessive resend requests, fix two possible lock problems, tidy up some backend code, quieten some debug messages, start using pthread_cancel, bring level with 3.2d55.

This commit is contained in:
Mike Brady
2018-05-19 09:30:22 +01:00
14 changed files with 452 additions and 382 deletions
+1 -1
View File
@@ -121,7 +121,7 @@ static void deinit(void) {
static void start(__attribute__((unused)) int sample_rate,
__attribute__((unused)) int sample_format) {}
static void play(short buf[], int samples) { ao_play(dev, (char *)buf, samples * 4); }
static void play(void *buf, int samples) { ao_play(dev, buf, samples * 4); }
static void stop(void) {}
+1 -1
View File
@@ -48,7 +48,7 @@ static void start(int sample_rate, __attribute__((unused)) int sample_format) {
debug(1, "dummy audio output started at Fs=%d Hz\n", sample_rate);
}
static void play(__attribute__((unused)) short buf[], __attribute__((unused)) int samples) {}
static void play(__attribute__((unused)) void *buf, __attribute__((unused)) int samples) {}
static void stop(void) { debug(1, "dummy audio stopped\n"); }
+5 -6
View File
@@ -198,22 +198,21 @@ static void start(__attribute__((unused)) int sample_rate,
pa_threaded_mainloop_unlock(mainloop);
}
static void play(short buf[], int samples) {
static void play(void *buf, int samples) {
// debug(1,"pa_play of %d samples.",samples);
char *bbuf = (char *)buf;
// copy the samples into the queue
size_t bytes_to_transfer = samples * 2 * 2;
size_t space_to_end_of_buffer = audio_umb - audio_eoq;
if (space_to_end_of_buffer >= bytes_to_transfer) {
memcpy(audio_eoq, bbuf, bytes_to_transfer);
memcpy(audio_eoq, buf, bytes_to_transfer);
audio_occupancy += bytes_to_transfer;
pthread_mutex_lock(&buffer_mutex);
audio_eoq += bytes_to_transfer;
pthread_mutex_unlock(&buffer_mutex);
} else {
memcpy(audio_eoq, bbuf, space_to_end_of_buffer);
bbuf += space_to_end_of_buffer;
memcpy(audio_lmb, bbuf, bytes_to_transfer - space_to_end_of_buffer);
memcpy(audio_eoq, buf, space_to_end_of_buffer);
buf += space_to_end_of_buffer;
memcpy(audio_lmb, buf, bytes_to_transfer - space_to_end_of_buffer);
pthread_mutex_lock(&buffer_mutex);
audio_occupancy += bytes_to_transfer;
pthread_mutex_unlock(&buffer_mutex);
+2 -2
View File
@@ -33,7 +33,7 @@ static int init(int, char **);
static void onmove_cb(void *, int);
static void deinit(void);
static void start(int, int);
static void play(short[], int);
static void play(void *, int);
static void stop(void);
static void onmove_cb(void *, int);
static int delay(long *);
@@ -222,7 +222,7 @@ static void start(__attribute__((unused)) int sample_rate,
pthread_mutex_unlock(&sndio_mutex);
}
static void play(short buf[], int frames) {
static void play(void *buf, int frames) {
if (frames > 0) {
pthread_mutex_lock(&sndio_mutex);
written += sio_write(hdl, buf, frames * framesize);
+2 -2
View File
@@ -167,7 +167,7 @@ static void start(int sample_rate, int sample_format) {
debug(1, "libsoundio output started\n");
}
static void play(short buf[], int samples) {
static void play(void *buf, int samples) {
// int err;
int free_bytes = soundio_ring_buffer_free_count(ring_buffer);
int written_bytes = 0;
@@ -181,7 +181,7 @@ static void play(short buf[], int samples) {
free_bytes);
if (write_bytes) {
memcpy(write_ptr, (char *)buf, write_bytes);
memcpy(write_ptr, buf, write_bytes);
written_bytes += write_bytes;
soundio_ring_buffer_advance_write_ptr(ring_buffer, write_bytes);
debug(3, "[<<---] Written to buffer : %d\n", written_bytes);
+1 -1
View File
@@ -43,7 +43,7 @@ static void start(__attribute__((unused)) int sample_rate,
fd = STDOUT_FILENO;
}
static void play(short buf[], int samples) {
static void play(void *buf, int samples) {
char errorstring[1024];
int warned = 0;
int rc = write(fd, buf, samples * 4);
+9 -9
View File
@@ -106,14 +106,14 @@ void mdns_unregister(void) {
void *mdns_dacp_monitor(char *dacp_id) {
void *reply = NULL;
if ((dacp_id!=NULL) && (*dacp_id!='\0')) {
if ((config.mdns) && (config.mdns->mdns_dacp_monitor)) {
reply = config.mdns->mdns_dacp_monitor(dacp_id);
if (reply == NULL) {
debug(1, "Error starting a DACP monitor.");
}
} else
debug(1, "Can't start a DACP monitor -- none registered.");
if ((dacp_id != NULL) && (*dacp_id != '\0')) {
if ((config.mdns) && (config.mdns->mdns_dacp_monitor)) {
reply = config.mdns->mdns_dacp_monitor(dacp_id);
if (reply == NULL) {
debug(1, "Error starting a DACP monitor.");
}
} else
debug(3, "Can't start a DACP monitor -- none registered.");
}
return reply;
}
@@ -122,7 +122,7 @@ void mdns_dacp_dont_monitor(void *userdata) {
if ((config.mdns) && (config.mdns->mdns_dacp_dont_monitor)) {
config.mdns->mdns_dacp_dont_monitor(userdata);
} else
debug(1, "Can't stop a DACP monitor -- none registered.");
debug(3, "Can't stop a DACP monitor -- none registered.");
}
void mdns_ls_backends(void) {
mdns_backend **b = NULL;
+10 -2
View File
@@ -364,9 +364,17 @@ static int avahi_register(char *srvname, int srvport) {
static void avahi_unregister(void) {
debug(1, "avahi: avahi_unregister.");
if (tpoll)
if (tpoll) {
avahi_threaded_poll_stop(tpoll);
tpoll = NULL;
if (client) {
avahi_client_free(client);
client = NULL;
};
avahi_threaded_poll_free(tpoll);
tpoll = NULL;
}
if (service_name)
free(service_name);
+234 -168
View File
@@ -106,6 +106,8 @@
// static abuf_t audio_buffer[BUFFER_FRAMES];
#define BUFIDX(seqno) ((seq_t)(seqno) % BUFFER_FRAMES)
void do_flush(int64_t timestamp, rtsp_conn_info *conn);
// make timestamps and seqnos definitely monotonic
// add an epoch to the timestamp. The monotonic timestamp guaranteed to start between 2^32 and 2^33
@@ -464,147 +466,176 @@ static void free_audio_buffers(rtsp_conn_info *conn) {
void player_put_packet(seq_t seqno, uint32_t actual_timestamp, int64_t timestamp, uint8_t *data,
int len, rtsp_conn_info *conn) {
if (pthread_rwlock_tryrdlock(&conn->player_thread_lock) == 0) {
if (conn->player_thread != NULL) {
// all timestamps are done at the output rate
// the "actual_timestamp" is the one that comes in the packet, and is carried over for debugging
// and checking only.
// all timestamps are done at the output rate
// the "actual_timestamp" is the one that comes in the packet, and is carried over for
// debugging
// and checking only.
int64_t ltimestamp = timestamp * conn->output_sample_ratio;
int64_t ltimestamp = timestamp * conn->output_sample_ratio;
// ignore a request to flush that has been made before the first packet...
if (conn->packet_count == 0) {
pthread_mutex_lock(&conn->flush_mutex);
conn->flush_requested = 0;
conn->flush_rtp_timestamp = 0;
pthread_mutex_unlock(&conn->flush_mutex);
}
pthread_mutex_lock(&conn->ab_mutex);
conn->packet_count++;
conn->time_of_last_audio_packet = get_absolute_time_in_fp();
if (conn->connection_state_to_output) { // if we are supposed to be processing these packets
// if (flush_rtp_timestamp != 0)
// debug(1,"Flush_rtp_timestamp is %u",flush_rtp_timestamp);
if ((conn->flush_rtp_timestamp != 0) && (ltimestamp <= conn->flush_rtp_timestamp)) {
debug(3,
"Dropping flushed packet in player_put_packet, seqno %u, timestamp %lld, flushing to "
"timestamp: %lld.",
seqno, ltimestamp, conn->flush_rtp_timestamp);
} else {
if ((conn->flush_rtp_timestamp != 0x0) &&
(ltimestamp > conn->flush_rtp_timestamp)) // if we have gone past the flush boundary time
conn->flush_rtp_timestamp = 0x0;
abuf_t *abuf = 0;
if (!conn->ab_synced) {
debug(3, "syncing to seqno %u.", seqno);
conn->ab_write = seqno;
conn->ab_read = seqno;
conn->ab_synced = 1;
// ignore a request to flush that has been made before the first packet...
if (conn->packet_count == 0) {
pthread_mutex_lock(&conn->flush_mutex);
conn->flush_requested = 0;
conn->flush_rtp_timestamp = 0;
pthread_mutex_unlock(&conn->flush_mutex);
}
// here, we should check for missing frames
if (!conn->ab_buffering) {
int j;
for (j = 1; j <= 8; j++) {
// check j times, after a short period of has elapsed, assuming 352 frames per packet
int back_step = (((250 * 44100) / 352) / 1000) * (j); // approx 250 ms intervals
int k;
for (k = -2; k <= 2; k++) {
if (back_step <
seq_diff(conn->ab_read, conn->ab_write,
conn->ab_read)) { // if it's within the range of frames in use...
int item_to_check = (conn->ab_write - back_step) & 0xffff;
seq_t next = item_to_check;
abuf_t *check_buf = conn->audio_buffer + BUFIDX(next);
if ((!check_buf->ready) &&
(check_buf->resend_level <
j)) { // prevent multiple requests from the same level of lookback
check_buf->resend_level = j;
if (config.disable_resend_requests == 0) {
rtp_request_resend(next, 1, conn);
if (j >= 6)
debug(2, "Resend request level #%d for packet %u in range %u to %u.", j, next,
conn->ab_read, conn->ab_write);
conn->resend_requests++;
pthread_mutex_lock(&conn->ab_mutex);
conn->packet_count++;
conn->time_of_last_audio_packet = get_absolute_time_in_fp();
if (conn->connection_state_to_output) { // if we are supposed to be processing these packets
// if (flush_rtp_timestamp != 0)
// debug(1,"Flush_rtp_timestamp is %u",flush_rtp_timestamp);
if ((conn->flush_rtp_timestamp != 0) && (ltimestamp <= conn->flush_rtp_timestamp)) {
debug(
3,
"Dropping flushed packet in player_put_packet, seqno %u, timestamp %lld, flushing to "
"timestamp: %lld.",
seqno, ltimestamp, conn->flush_rtp_timestamp);
} else {
if ((conn->flush_rtp_timestamp != 0x0) &&
(ltimestamp >
conn->flush_rtp_timestamp)) // if we have gone past the flush boundary time
conn->flush_rtp_timestamp = 0x0;
abuf_t *abuf = 0;
if (!conn->ab_synced) {
debug(3, "syncing to seqno %u.", seqno);
conn->ab_write = seqno;
conn->ab_read = seqno;
conn->ab_synced = 1;
}
// here, we should check for missing frames
int resend_interval = (((250 * 44100) / 352) / 1000); // approximately 250 ms intervals
const int number_of_resend_attempts = 8;
int latency_based_resend_interval =
(conn->latency) / (number_of_resend_attempts * conn->max_frames_per_packet);
if (latency_based_resend_interval > resend_interval)
resend_interval = latency_based_resend_interval;
if (conn->resend_interval != resend_interval) {
debug(2, "Resend interval for latency of %" PRId64 " frames is %d frames.",
conn->latency, resend_interval);
conn->resend_interval = resend_interval;
}
if (!conn->ab_buffering) {
int j;
for (j = 1; j <= number_of_resend_attempts; j++) {
// check j times, after a short period of has elapsed, assuming 352 frames per packet
int back_step = resend_interval * j;
int32_t sd = seq_diff(conn->ab_read, conn->ab_write, conn->ab_read);
int k;
for (k = -2; k <= 2; k++) {
if ((back_step + k) < sd) { // if it's within the range of frames in use...
int item_to_check = (conn->ab_write - (back_step + k)) & 0xffff;
seq_t next = item_to_check;
abuf_t *check_buf = conn->audio_buffer + BUFIDX(next);
if ((!check_buf->ready) &&
(check_buf->resend_level <
j)) { // prevent multiple requests from the same level of lookback
check_buf->resend_level = j;
if (config.disable_resend_requests == 0) {
rtp_request_resend(next, 1, conn);
if ((back_step + k + resend_interval) >= sd)
debug(2, "Last-ditch (#%d) resend request for packet %u in range %u to %u. "
"Looking back %d packets.",
j, next, conn->ab_read, conn->ab_write, back_step + k);
conn->resend_requests++;
}
}
}
}
}
}
if (conn->ab_write == seqno) { // expected packet
abuf = conn->audio_buffer + BUFIDX(seqno);
conn->ab_write = SUCCESSOR(seqno);
} else if (seq_order(conn->ab_write, seqno, conn->ab_read)) { // newer than expected
// if (ORDINATE(seqno)>(BUFFER_FRAMES*7)/8)
// debug(1,"An interval of %u frames has opened, with ab_read: %u, ab_write: %u and
// seqno:
// %u.",seq_diff(ab_read,seqno),ab_read,ab_write,seqno);
int32_t gap = seq_diff(conn->ab_write, seqno, conn->ab_read);
if (gap <= 0)
debug(1, "Unexpected gap size: %d.", gap);
int i;
for (i = 0; i < gap; i++) {
abuf = conn->audio_buffer + BUFIDX(seq_sum(conn->ab_write, i));
abuf->ready = 0; // to be sure, to be sure
abuf->resend_level = 0;
abuf->timestamp = 0;
abuf->given_timestamp = 0;
abuf->sequence_number = 0;
}
// debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write);
abuf = conn->audio_buffer + BUFIDX(seqno);
// rtp_request_resend(ab_write, gap);
// resend_requests++;
conn->ab_write = SUCCESSOR(seqno);
} else if (seq_order(conn->ab_read, seqno, conn->ab_read)) { // late but not yet played
conn->late_packets++;
abuf = conn->audio_buffer + BUFIDX(seqno);
/*
if (abuf->ready)
debug(1,"Late apparently duplicate packet received that is %d packets
late.",seq_diff(seqno, conn->ab_write, conn->ab_read));
else
debug(1,"Late packet received that is %d packets late.",seq_diff(seqno,
conn->ab_write, conn->ab_read));
*/
} else { // too late.
// debug(1,"Too late packet received that is %d packets late.",seq_diff(seqno,
// conn->ab_write, conn->ab_read));
conn->too_late_packets++;
}
// pthread_mutex_unlock(&ab_mutex);
if (abuf) {
int datalen = conn->max_frames_per_packet;
if (alac_decode(abuf->data, &datalen, data, len, conn) == 0) {
abuf->ready = 1;
abuf->length = datalen;
abuf->timestamp = ltimestamp;
abuf->given_timestamp = actual_timestamp;
abuf->sequence_number = seqno;
} else {
debug(1, "Bad audio packet detected and discarded.");
abuf->ready = 0;
abuf->resend_level = 0;
abuf->timestamp = 0;
abuf->given_timestamp = 0;
abuf->sequence_number = 0;
}
}
// pthread_mutex_lock(&ab_mutex);
}
int rc = pthread_cond_signal(&conn->flowcontrol);
if (rc)
debug(1, "Error signalling flowcontrol.");
}
if (conn->ab_write == seqno) { // expected packet
abuf = conn->audio_buffer + BUFIDX(seqno);
conn->ab_write = SUCCESSOR(seqno);
} else if (seq_order(conn->ab_write, seqno, conn->ab_read)) { // newer than expected
// if (ORDINATE(seqno)>(BUFFER_FRAMES*7)/8)
// debug(1,"An interval of %u frames has opened, with ab_read: %u, ab_write: %u and seqno:
// %u.",seq_diff(ab_read,seqno),ab_read,ab_write,seqno);
int32_t gap = seq_diff(conn->ab_write, seqno, conn->ab_read);
if (gap <= 0)
debug(1, "Unexpected gap size: %d.", gap);
int i;
for (i = 0; i < gap; i++) {
abuf = conn->audio_buffer + BUFIDX(seq_sum(conn->ab_write, i));
abuf->ready = 0; // to be sure, to be sure
abuf->resend_level = 0;
abuf->timestamp = 0;
abuf->given_timestamp = 0;
abuf->sequence_number = 0;
}
// debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write);
abuf = conn->audio_buffer + BUFIDX(seqno);
// rtp_request_resend(ab_write, gap);
// resend_requests++;
conn->ab_write = SUCCESSOR(seqno);
} else if (seq_order(conn->ab_read, seqno, conn->ab_read)) { // late but not yet played
conn->late_packets++;
abuf = conn->audio_buffer + BUFIDX(seqno);
/*
if (abuf->ready)
debug(1,"Late apparently duplicate packet received that is %d packets
late.",seq_diff(seqno, conn->ab_write, conn->ab_read));
else
debug(1,"Late packet received that is %d packets late.",seq_diff(seqno,
conn->ab_write, conn->ab_read));
*/
} else { // too late.
// debug(1,"Too late packet received that is %d packets late.",seq_diff(seqno,
// conn->ab_write, conn->ab_read));
conn->too_late_packets++;
}
// pthread_mutex_unlock(&ab_mutex);
if (abuf) {
int datalen = conn->max_frames_per_packet;
if (alac_decode(abuf->data, &datalen, data, len, conn) == 0) {
abuf->ready = 1;
abuf->length = datalen;
abuf->timestamp = ltimestamp;
abuf->given_timestamp = actual_timestamp;
abuf->sequence_number = seqno;
} else {
debug(1, "Bad audio packet detected and discarded.");
abuf->ready = 0;
abuf->resend_level = 0;
abuf->timestamp = 0;
abuf->given_timestamp = 0;
abuf->sequence_number = 0;
}
}
// pthread_mutex_lock(&ab_mutex);
pthread_mutex_unlock(&conn->ab_mutex);
} else {
debug(
1,
"No player thread while adding a player_put_packet calle was made -- packet discarded.");
}
int rc = pthread_cond_signal(&conn->flowcontrol);
if (rc)
debug(1, "Error signalling flowcontrol.");
pthread_rwlock_unlock(&conn->player_thread_lock);
} else {
debug(1, "player_put_packet discarded packet %d because the player thread was locked.", seqno);
}
pthread_mutex_unlock(&conn->ab_mutex);
}
int32_t rand_in_range(int32_t exclusive_range_limit) {
@@ -1200,15 +1231,13 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
} while (wait);
if (conn->player_thread_please_stop) {
debug(3, "buffer_get_frame exiting due to thread stop request.");
pthread_mutex_unlock(&conn->ab_mutex);
return 0;
}
// seq_t read = conn->ab_read;
// check if t+8, t+16, t+32, t+64, t+128, ... (buffer_start_fill / 2)
// packets have arrived... last-chance resend
if (!curframe->ready) {
// debug(1, "Supplying a silent frame for frame %u", read);
conn->missing_packets++;
@@ -1395,26 +1424,12 @@ typedef struct stats { // statistics for running averages
} stats_t;
static void *player_thread_func(void *arg) {
// note, the thread will be started up with the player_thread_lock locked. You must release it
// quickly.
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
conn->please_stop = 0;
conn->packet_count = 0;
conn->previous_random_number = 0;
conn->input_bytes_per_frame = 4;
conn->player_thread_please_stop = 0;
conn->decoder_in_use = 0;
conn->ab_buffering = 1;
conn->ab_synced = 0;
conn->first_packet_timestamp = 0;
conn->flush_requested = 0;
conn->fix_volume = 0x10000;
if (conn->latency == 0) {
debug(3, "No latency has (yet) been specified. Setting 88,200 (2 seconds) frames "
"as a default.");
conn->latency = 88200;
}
pthread_rwlock_wrlock(&conn->player_thread_lock);
int rc = pthread_mutex_init(&conn->ab_mutex, NULL);
if (rc)
@@ -1437,6 +1452,28 @@ static void *player_thread_func(void *arg) {
#endif
if (rc)
debug(1, "Error initialising flowcontrol condition variable.");
pthread_rwlock_unlock(&conn->player_thread_lock);
// it's safe now
conn->packet_count = 0;
conn->previous_random_number = 0;
conn->input_bytes_per_frame = 4;
conn->player_thread_please_stop = 0;
conn->decoder_in_use = 0;
conn->ab_buffering = 1;
conn->ab_synced = 0;
conn->first_packet_timestamp = 0;
conn->flush_requested = 0;
conn->fix_volume = 0x10000;
if (conn->latency == 0) {
debug(3, "No latency has (yet) been specified. Setting 88,200 (2 seconds) frames "
"as a default.");
conn->latency = 88200;
}
config.output->start(config.output_rate, config.output_format);
init_decoder((int32_t *)&conn->stream.fmtp,
@@ -1725,8 +1762,12 @@ static void *player_thread_func(void *arg) {
free(silence);
}
} else if (frames_to_drop) {
if (frames_to_drop > 3 * config.output_rate)
warn("Very large number of frames to drop: %" PRId64 ".", frames_to_drop);
if (frames_to_drop > 3 * config.output_rate) {
warn("Shome mhistake shurely: very large number of frames to drop: %" PRId64
" -- setting it to %" PRId64 ".",
frames_to_drop, 3 * config.output_rate);
frames_to_drop = 3 * config.output_rate;
}
debug(3, "%" PRId64 " frames to drop.", frames_to_drop);
frames_to_drop -= inframe->length;
if (frames_to_drop < 0)
@@ -2271,6 +2312,8 @@ static void *player_thread_func(void *arg) {
}
}
debug(3, "Connection %d: player thread main loop exit.", conn->connection_number);
if (config.statistics_requested) {
int rawSeconds = (int)difftime(time(NULL), playstart);
int elapsedHours = rawSeconds / 3600;
@@ -2286,25 +2329,26 @@ static void *player_thread_func(void *arg) {
mdns_dacp_dont_monitor(conn->dapo_private_storage);
#endif
debug(3, "Requesting output device to stop.");
if (config.output->stop)
config.output->stop();
debug(2, "Shut down audio, control and timing threads");
conn->please_stop = 1;
pthread_kill(rtp_audio_thread, SIGUSR1);
pthread_kill(rtp_control_thread, SIGUSR1);
pthread_kill(rtp_timing_thread, SIGUSR1);
debug(2, "Cancelling timing, control and audio threads");
pthread_cancel(rtp_timing_thread);
pthread_cancel(rtp_control_thread);
pthread_cancel(rtp_audio_thread);
debug(2, "Joining terminated threads.");
pthread_join(rtp_timing_thread, NULL);
debug(2, "timing thread joined");
pthread_join(rtp_audio_thread, NULL);
debug(2, "audio thread joined");
debug(2, "Timing thread terminated.");
pthread_join(rtp_control_thread, NULL);
debug(2, "control thread joined");
debug(2, "Control thread terminated.");
pthread_join(rtp_audio_thread, NULL);
debug(2, "Audio thread terminated.");
clear_reference_timestamp(conn);
conn->rtp_running = 0;
// usleep(100000); // allow this time to (?) allow the alsa subsystem to finish cleaning up after
// itself. 50 ms seems too short
debug(2, "Freeing audio buffers and decoders.");
free_audio_buffers(conn);
terminate_decoders(conn);
@@ -2322,7 +2366,7 @@ static void *player_thread_func(void *arg) {
if (rc)
debug(1, "Error destroying vol_mutex variable.");
debug(2, "Player thread exit on RTSP conversation thread %d.", conn->connection_number);
debug(2, "Connection %d: player thread terminated.", conn->connection_number);
if (conn->dacp_id) {
free(conn->dacp_id);
conn->dacp_id = NULL;
@@ -2333,7 +2377,7 @@ static void *player_thread_func(void *arg) {
free(tbuf);
if (sbuf)
free(sbuf);
return 0;
pthread_exit(NULL);
}
// takes the volume as specified by the airplay protocol
@@ -2571,7 +2615,7 @@ void player_volume(double airplay_volume, rtsp_conn_info *conn) {
player_volume_without_notification(airplay_volume, conn);
}
void player_flush(int64_t timestamp, rtsp_conn_info *conn) {
void do_flush(int64_t timestamp, rtsp_conn_info *conn) {
debug(3, "Flush requested up to %u. It seems as if 0 is special.", timestamp);
pthread_mutex_lock(&conn->flush_mutex);
conn->flush_requested = 1;
@@ -2588,10 +2632,24 @@ void player_flush(int64_t timestamp, rtsp_conn_info *conn) {
send_ssnc_metadata('pfls', NULL, 0, 1);
}
#endif
debug(3, "Flush request made.");
}
void player_flush(int64_t timestamp, rtsp_conn_info *conn) {
debug(3, "player_flush");
if (pthread_rwlock_tryrdlock(&conn->player_thread_lock) == 0) {
if (conn->player_thread != NULL)
do_flush(timestamp, conn);
else
debug(1, "Flush requested when player thread is gone.");
pthread_rwlock_unlock(&conn->player_thread_lock);
} else {
debug(3, "Can't acquire a read lock for a flush -- ignored.");
}
}
int player_play(rtsp_conn_info *conn) {
// need to use conn in place of streram below. Need to put the stream as a parameter to he
// need to use conn in place of stream below. Need to put the stream as a parameter to he
if (conn->player_thread != NULL)
die("Trying to create a second player thread for this RTSP session");
if (config.buffer_start_fill > BUFFER_FRAMES)
@@ -2612,25 +2670,33 @@ int player_play(rtsp_conn_info *conn) {
int rc = pthread_attr_setstacksize(&tattr, size);
if (rc)
debug(1, "Error setting stack size for player_thread: %s", strerror(errno));
// hack alert -- the player thread itself releases the player_thread_lock rwlock as soon as it's
// finished initialising.
pthread_create(pt, &tattr, player_thread_func, (void *)conn);
pthread_attr_destroy(&tattr);
return 0;
}
void player_stop(rtsp_conn_info *conn) {
int player_stop(rtsp_conn_info *conn) {
pthread_rwlock_wrlock(&conn->player_thread_lock);
if (conn->player_thread) {
conn->player_thread_please_stop = 1;
pthread_cond_signal(&conn->flowcontrol); // tell it to give up
pthread_kill(*conn->player_thread, SIGUSR1);
pthread_join(*conn->player_thread, NULL);
free(conn->player_thread);
conn->player_thread = NULL;
pthread_rwlock_unlock(&conn->player_thread_lock);
#ifdef CONFIG_METADATA
debug(2, "pend");
send_ssnc_metadata('pend', NULL, 0, 1);
#endif
command_stop();
free(conn->player_thread);
conn->player_thread = NULL;
return 0;
} else {
pthread_rwlock_unlock(&conn->player_thread_lock);
debug(3, "player thread of RTSP conversation %d is already deleted.", conn->connection_number);
return -1;
}
}
+15 -5
View File
@@ -46,7 +46,17 @@ typedef struct audio_buffer_entry { // decoded audio packets
} abuf_t;
// default buffer size
// needs to be a power of 2 because of the way BUFIDX(seqno) works
// This eeds to be a power of 2 because of the way BUFIDX(seqno) works.
// 512 is the minimum for normal operation -- it gives 512*352/44100 or just over 4 seconds of
// buffers.
// For at least 10 seconds, you need to go to 2048.
// Resend requests will be spaced out evenly in the latency period, subject to a minimum interval of
// about 0.25 seconds.
// Each buffer occupies 352*4 bytes plus about, say, 64 bytes of overhead in various places, say
// rougly 1,500 bytes per buffer.
// Thus, 2048 buffers will occupy about 3 megabytes -- no big deal in a normal machine but maybe a
// problem in an embedded device.
#define BUFFER_FRAMES 1024
typedef struct {
@@ -57,6 +67,7 @@ typedef struct {
typedef struct {
int connection_number; // for debug ID purposes, nothing else...
int resend_interval; // this is really just for debugging
int AirPlayVersion; // zero if not an AirPlay session. Used to help calculate latency
int64_t latency; // the actual latency used for this play session
int64_t minimum_latency; // set if an a=min-latency: line appears in the ANNOUNCE message; zero
@@ -70,10 +81,11 @@ typedef struct {
SOCKADDR remote, local;
int stop;
int running;
pthread_t thread;
pthread_t thread, timer_requester;
// pthread_t *ptp;
pthread_t *player_thread;
pthread_rwlock_t player_thread_lock; // used to control access by "outsiders"
abuf_t audio_buffer[BUFFER_FRAMES];
int max_frames_per_packet, input_num_channels, input_bit_depth, input_rate;
@@ -81,7 +93,6 @@ typedef struct {
int max_frame_size_change;
int64_t previous_random_number;
alac_file *decoder_info;
uint32_t please_stop;
uint64_t packet_count;
int connection_state_to_output;
int player_thread_please_stop;
@@ -168,7 +179,6 @@ typedef struct {
uint64_t local_to_remote_time_difference; // used to switch between local and remote clocks
int timing_sender_stop; // for asking the timing-sending thread to stop
int last_stuff_request;
int64_t play_segment_reference_frame;
@@ -191,7 +201,7 @@ typedef struct {
} rtsp_conn_info;
int player_play(rtsp_conn_info *conn);
void player_stop(rtsp_conn_info *conn);
int player_stop(rtsp_conn_info *conn);
void player_volume(double f, rtsp_conn_info *conn);
void player_volume_without_notification(double f, rtsp_conn_info *conn);
+97 -144
View File
@@ -48,8 +48,6 @@
uint64_t local_to_remote_time_jitters;
uint64_t local_to_remote_time_jitters_count;
void memory_barrier();
void rtp_initialise(rtsp_conn_info *conn) {
conn->rtp_time_of_last_resend_request_error_fp = 0;
conn->rtp_running = 0;
@@ -67,10 +65,15 @@ void rtp_terminate(rtsp_conn_info *conn) {
debug(1, "Error destroying reference_time_mutex variable.");
}
void *rtp_audio_receiver(void *arg) {
debug(3, "Audio receiver -- Server RTP thread starting.");
void rtp_audio_receiver_cleanup_handler(void *arg) {
debug(3, "Audio Receiver Cleanup.");
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
close(conn->audio_socket);
debug(3, "Audio Receiver Cleanup Successful.");
}
// we inherit the signal mask (SIGUSR1)
void *rtp_audio_receiver(void *arg) {
pthread_cleanup_push(rtp_audio_receiver_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
int32_t last_seqno = -1;
@@ -87,17 +90,7 @@ void *rtp_audio_receiver(void *arg) {
float stat_M2 = 0.0;
ssize_t nread;
while (conn->please_stop == 0) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(conn->audio_socket, &readfds);
do {
memory_barrier();
} while (conn->please_stop == 0 &&
pselect(conn->audio_socket + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0);
if (conn->please_stop != 0) {
break;
}
while (1) {
nread = recv(conn->audio_socket, packet, sizeof(packet), 0);
uint64_t local_time_now_fp = get_absolute_time_in_fp();
@@ -181,17 +174,26 @@ void *rtp_audio_receiver(void *arg) {
}
}
/*
debug(3, "Audio receiver -- Server RTP thread interrupted. terminating.");
close(conn->audio_socket);
*/
return NULL;
debug(1, "Audio receiver thread \"normal\" exit -- this can't happen. Hah!");
pthread_cleanup_pop(0); // don't execute anything here.
debug(2, "Audio receiver thread exit.");
pthread_exit(NULL);
}
void rtp_control_handler_cleanup_handler(void *arg) {
debug(3, "Control Receiver Cleanup.");
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
close(conn->control_socket);
debug(3, "Control Receiver Cleanup Successful.");
}
void *rtp_control_receiver(void *arg) {
// we inherit the signal mask (SIGUSR1)
debug(3, "Control receiver -- Server RTP thread starting.");
pthread_cleanup_push(rtp_control_handler_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
conn->reference_timestamp = 0; // nothing valid received yet
@@ -200,18 +202,7 @@ void *rtp_control_receiver(void *arg) {
uint64_t remote_time_of_sync;
int64_t sync_rtp_timestamp;
ssize_t nread;
while (conn->please_stop == 0) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(conn->control_socket, &readfds);
do {
memory_barrier();
} while (conn->please_stop == 0 &&
pselect(conn->control_socket + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0);
if (conn->please_stop != 0) {
break;
}
while (1) {
nread = recv(conn->control_socket, packet, sizeof(packet), 0);
// local_time_now = get_absolute_time_in_fp();
// clock_gettime(CLOCK_MONOTONIC,&tn);
@@ -225,47 +216,47 @@ void *rtp_control_receiver(void *arg) {
ssize_t plen = nread;
if (packet[1] == 0xd4) { // sync data
/*
// the following stanza is for debugging only -- normally commented out.
{
char obf[4096];
char *obfp = obf;
int obfc;
for (obfc = 0; obfc < plen; obfc++) {
snprintf(obfp, 3, "%02X", packet[obfc]);
obfp += 2;
};
*obfp = 0;
// the following stanza is for debugging only -- normally commented out.
{
char obf[4096];
char *obfp = obf;
int obfc;
for (obfc = 0; obfc < plen; obfc++) {
snprintf(obfp, 3, "%02X", packet[obfc]);
obfp += 2;
};
*obfp = 0;
// get raw timestamp information
// I think that a good way to understand these timestamps is that
// (1) the rtlt below is the timestamp of the frame that should be playing at the
client-time specified in the packet if there was no delay
// and (2) that the rt below is the timestamp of the frame that should be playing
at
the client-time specified in the packet on this device taking account of the delay
// Thus, (3) the latency can be calculated by subtracting the second from the
first.
// There must be more to it -- there something missing.
// get raw timestamp information
// I think that a good way to understand these timestamps is that
// (1) the rtlt below is the timestamp of the frame that should be playing at the
// client-time specified in the packet if there was no delay
// and (2) that the rt below is the timestamp of the frame that should be playing
// at the client-time specified in the packet on this device taking account of
// the delay
// Thus, (3) the latency can be calculated by subtracting the second from the
// first.
// There must be more to it -- there something missing.
// In addition, it seems that if the value of the short represented by the second
pair
of bytes in the packe is 7
// then an extra time lag is expected to be added, presumably by the AirPort
Express.
Best guess is that this delay is 11,025 frames.
// In addition, it seems that if the value of the short represented by the second
// pair of bytes in the packe is 7
// then an extra time lag is expected to be added, presumably by
// the AirPort Express.
// uint32_t rtlt = nctohl(&packet[4]); // raw timestamp less latency
// uint32_t rt = nctohl(&packet[16]); // raw timestamp
// Best guess is that this delay is 11,025 frames.
// uint32_t fl = nctohs(&packet[2]); //
// uint32_t rtlt = nctohl(&packet[4]); // raw timestamp less latency
// uint32_t rt = nctohl(&packet[16]); // raw timestamp
// debug(1,"Sync Packet of %d bytes received: \"%s\", flags: %d, timestamps %u and
%u,
giving a latency of %d frames.",plen,obf,fl,rt,rtlt,rt-rtlt);
// debug(1,"Monotonic timestamps are: %" PRId64 " and %" PRId64 "
respectively.",monotonic_timestamp(rt, conn),monotonic_timestamp(rtlt, conn));
}
// uint32_t fl = nctohs(&packet[2]); //
// debug(1,"Sync Packet of %d bytes received: \"%s\", flags: %d, timestamps %u and
%u,
giving a latency of %d frames.",plen,obf,fl,rt,rtlt,rt-rtlt);
// debug(1,"Monotonic timestamps are: %" PRId64 " and %" PRId64 "
respectively.",monotonic_timestamp(rt, conn),monotonic_timestamp(rtlt, conn));
}
*/
if (conn->local_to_remote_time_difference) { // need a time packet to be interchanged
// first...
@@ -406,15 +397,13 @@ void *rtp_control_receiver(void *arg) {
debug(1, "Control Receiver -- error receiving a packet.");
}
}
debug(3, "Control RTP thread interrupted. terminating.");
close(conn->control_socket);
return NULL;
debug(1, "Control RTP thread \"normal\" exit -- this can't happen. Hah!");
pthread_cleanup_pop(0); // don't execute anything here.
debug(2, "Control RTP thread exit.");
pthread_exit(NULL);
}
void *rtp_timing_sender(void *arg) {
debug(3, "Timing sender thread starting.");
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
struct timing_request {
char leader;
@@ -434,9 +423,7 @@ void *rtp_timing_sender(void *arg) {
req.seqno = htons(7);
conn->time_ping_count = 0;
// we inherit the signal mask (SIGUSR1)
while (conn->timing_sender_stop == 0) {
while (1) {
// debug(1,"Send a timing request");
if (!conn->rtp_running)
@@ -456,63 +443,45 @@ void *rtp_timing_sender(void *arg) {
msgsize = sizeof(struct sockaddr_in6);
}
#endif
fd_set writefds;
FD_ZERO(&writefds);
FD_SET(conn->timing_socket, &writefds);
do {
memory_barrier();
} while (conn->timing_sender_stop == 0 &&
pselect(conn->timing_socket + 1, NULL, &writefds, NULL, NULL, &pselect_sigset) <= 0);
if (conn->timing_sender_stop != 0) {
break;
}
if ((config.diagnostic_drop_packet_fraction == 0.0) ||
(drand48() > config.diagnostic_drop_packet_fraction)) {
if (sendto(conn->timing_socket, &req, sizeof(req), 0,
(struct sockaddr *)&conn->rtp_client_timing_socket, msgsize) == -1) {
char em[1024];
strerror_r(errno, em, sizeof(em));
debug(1, "Error %d using send-to to the timing socket: \"%s\".", errno, em);
}
} else {
debug(3, "Timing Sender Thread -- dropping outgoing packet to simulate bad network.");
}
request_number++;
// this is to deal with the possibility of missing a timing_sender_stop signal.
// if the signal came in just before the usleep, then it wouldn't cause the sleep to end.
// so, we will wait a maximum time of the wait_interval
int wait_time;
int wait_interval = 20000; // 20 milliseconds
if (request_number <= 4)
wait_time = 500000;
usleep(500000); // these are thread cancellation points
else
wait_time = 3000000;
while ((wait_time > 0) && (conn->timing_sender_stop == 0)) {
usleep(wait_interval);
wait_time -= wait_interval;
}
usleep(3000000);
}
debug(3, "rtp_timing_sender thread interrupted. terminating.");
return NULL;
debug(3, "rtp_timing_sender thread interrupted. This should never happen.");
pthread_exit(NULL);
}
void rtp_timing_receiver_cleanup_handler(void *arg) {
debug(3, "Timing Receiver Cleanup.");
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
pthread_cancel(conn->timer_requester);
pthread_join(conn->timer_requester, NULL);
close(conn->timing_socket);
debug(3, "Timing Receiver Cleanup Successful.");
}
void *rtp_timing_receiver(void *arg) {
debug(3, "Timing receiver -- Server RTP thread starting.");
// we inherit the signal mask (SIGUSR1)
pthread_cleanup_push(rtp_timing_receiver_cleanup_handler, arg);
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
uint8_t packet[2048];
ssize_t nread;
conn->timing_sender_stop = 0;
pthread_t timer_requester;
pthread_create(&timer_requester, NULL, &rtp_timing_sender, arg);
pthread_create(&conn->timer_requester, NULL, &rtp_timing_sender, arg);
// struct timespec att;
uint64_t distant_receive_time, distant_transmit_time, arrival_time, return_time;
local_to_remote_time_jitters = 0;
@@ -523,17 +492,7 @@ void *rtp_timing_receiver(void *arg) {
uint64_t first_local_to_remote_time_difference = 0;
// uint64_t first_local_to_remote_time_difference_time;
// uint64_t l2rtd = 0;
while (conn->please_stop == 0) {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(conn->timing_socket, &readfds);
do {
memory_barrier();
} while (conn->please_stop == 0 &&
pselect(conn->timing_socket + 1, &readfds, NULL, NULL, NULL, &pselect_sigset) <= 0);
if (conn->please_stop != 0) {
break;
}
while (1) {
nread = recv(conn->timing_socket, packet, sizeof(packet), 0);
if (nread >= 0) {
@@ -562,9 +521,9 @@ void *rtp_timing_receiver(void *arg) {
return_time = arrival_time - conn->departure_time;
uint64_t rtus = (return_time * 1000000) >> 32;
// uint64_t rtus = (return_time * 1000000) >> 32;
if (rtus < 300000) {
if (((return_time * 1000000) >> 32) < 300000) {
// debug(2,"Synchronisation ping return time is %f milliseconds.",(rtus*1.0)/1000);
@@ -727,7 +686,7 @@ void *rtp_timing_receiver(void *arg) {
//(return_time*1000000)>>32);
} else {
debug(2, "Time ping turnaround time: %lld us -- it looks like a timing ping was lost.",
rtus);
(return_time * 1000000) >> 32);
}
} else {
debug(1, "Timing port -- Unknown RTP packet of type 0x%02X length %d.", packet[1], nread);
@@ -740,17 +699,10 @@ void *rtp_timing_receiver(void *arg) {
}
}
debug(3, "Timing thread interrupted. terminating.");
conn->timing_sender_stop = 1;
void *retval;
pthread_kill(timer_requester, SIGUSR1);
debug(3, "Wait for timer requester to exit.");
pthread_join(timer_requester, &retval);
debug(3, "Closed and terminated timer requester thread.");
debug(3, "Timing RTP thread terminated.");
close(conn->timing_socket);
return NULL;
debug(1, "Timing Receiver RTP thread \"normal\" exit -- this can't happen. Hah!");
pthread_cleanup_pop(0); // don't execute anything here.
debug(2, "Timing Receiver RTP thread exit.");
pthread_exit(NULL);
}
static uint16_t bind_port(int ip_family, const char *self_ip_address, uint32_t scope_id,
@@ -808,8 +760,6 @@ static uint16_t bind_port(int ip_family, const char *self_ip_address, uint32_t s
struct sockaddr_in *sa = (struct sockaddr_in *)&local;
sport = ntohs(sa->sin_port);
}
fcntl(local_socket, F_SETFL, O_NONBLOCK);
*sock = local_socket;
return sport;
}
@@ -862,8 +812,8 @@ void rtp_setup(SOCKADDR *local, SOCKADDR *remote, uint16_t cport, uint16_t tport
inet_ntop(conn->connection_ip_family, self_addr, conn->self_ip_string,
sizeof(conn->self_ip_string));
debug(2, "SETUP connection from %s to self at %s on RTSP conversation thread %d.",
conn->client_ip_string, conn->self_ip_string, conn->connection_number);
debug(2, "Connection %d: SETUP -- Connection from %s to self at %s.",
conn->connection_number,conn->client_ip_string, conn->self_ip_string);
// set up a the record of the remote's control socket
struct addrinfo hints;
@@ -978,7 +928,7 @@ void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn) {
}
#endif
uint64_t time_of_sending_fp = get_absolute_time_in_fp();
uint64_t resend_error_backoff_time = (uint64_t)1 << (32 - 1); // half a second
uint64_t resend_error_backoff_time = (uint64_t)1 << (32 - 4); // one sixteenth of a second
if ((conn->rtp_time_of_last_resend_request_error_fp == 0) ||
((time_of_sending_fp - conn->rtp_time_of_last_resend_request_error_fp) >
resend_error_backoff_time)) {
@@ -988,16 +938,19 @@ void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn) {
(struct sockaddr *)&conn->rtp_client_control_socket, msgsize) == -1) {
char em[1024];
strerror_r(errno, em, sizeof(em));
debug(1,
"Error %d using send-to to an audio socket: \"%s\". Backing off for 0.5 seconds.",
debug(1, "Error %d using send-to to an audio socket: \"%s\". Backing off for 1/16th of a "
"second.",
errno, em);
conn->rtp_time_of_last_resend_request_error_fp = time_of_sending_fp;
} else {
conn->rtp_time_of_last_resend_request_error_fp = 0;
}
} else {
debug(3, "Dropping resend request packet to simulate a bad network. Backing off for 0.5 "
"seconds.");
debug(
3,
"Dropping resend request packet to simulate a bad network. Backing off for 1/16th of a "
"second.");
conn->rtp_time_of_last_resend_request_error_fp = time_of_sending_fp;
}
} else {
+42 -23
View File
@@ -697,7 +697,7 @@ static void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
p = strchr(p, '=');
if (p) {
rtptime = uatoi(p + 1); // unsigned integer -- up to 2^32-1
rtptime--;
// rtptime--;
// debug(1,"RTSP Flush Requested by handle_record: %u.",rtptime);
player_flush(rtptime, conn);
}
@@ -762,12 +762,12 @@ static void handle_flush(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *
}
static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) {
debug(2, "Connection %d: SETUP", conn->connection_number);
debug(3, "Connection %d: SETUP", conn->connection_number);
uint16_t cport, tport;
char *ar = msg_get_header(req, "Active-Remote");
if (ar) {
debug(2, "Active-Remote string seen: \"%s\".", ar);
debug(2, "Connection %d: SETUP -- Active-Remote string seen: \"%s\".", conn->connection_number, ar);
// get the active remote
char *p;
conn->dacp_active_remote = strtoul(ar, &p, 10);
@@ -775,13 +775,13 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *
send_metadata('ssnc', 'acre', ar, strlen(ar), req, 1);
#endif
} else {
debug(2, "Note: no Active-Remote information the SETUP Record.");
debug(2, "Connection %d: SETUP -- Note: no Active-Remote information the SETUP Record.",conn->connection_number);
conn->dacp_active_remote = 0;
}
ar = msg_get_header(req, "DACP-ID");
if (ar) {
debug(2, "DACP-ID string seen: \"%s\".", ar);
debug(2, "Connection %d: SETUP -- DACP-ID string seen: \"%s\".",conn->connection_number, ar);
if (conn->dacp_id) // this is in case SETUP was previously called
free(conn->dacp_id);
conn->dacp_id = strdup(ar);
@@ -789,7 +789,7 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *
send_metadata('ssnc', 'daid', ar, strlen(ar), req, 1);
#endif
} else {
debug(2, "Note: no DACP-ID string information in the SETUP Record.");
debug(2, "Connection %d: SETUP doesn't include DACP-ID string information.",conn->connection_number);
if (conn->dacp_id) // this is in case SETUP was previously called
free(conn->dacp_id);
conn->dacp_id = NULL;
@@ -797,14 +797,14 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *
char *hdr = msg_get_header(req, "Transport");
if (!hdr) {
debug(1, "SETUP doesn't contain a Transport header.");
debug(1, "Connection %d: SETUP doesn't contain a Transport header.",conn->connection_number);
goto error;
}
char *p;
p = strstr(hdr, "control_port=");
if (!p) {
debug(1, "SETUP doesn't specify a control_port.");
debug(1, "Connection %d: SETUP doesn't specify a control_port.",conn->connection_number);
goto error;
}
p = strchr(p, '=') + 1;
@@ -812,7 +812,7 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *
p = strstr(hdr, "timing_port=");
if (!p) {
debug(1, "SETUP doesn't specify a timing_port.");
debug(1, "Connection %d: SETUP doesn't specify a timing_port.",conn->connection_number);
goto error;
}
p = strchr(p, '=') + 1;
@@ -820,19 +820,19 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *
if (conn->rtp_running) {
if ((conn->remote_control_port != cport) || (conn->remote_timing_port != tport)) {
warn("Duplicate SETUP message with different control (old %u, new %u) or timing (old %u, new "
"%u) ports! This is probably fatal!",
warn("Connection %d: Duplicate SETUP message with different control (old %u, new %u) or timing (old %u, new "
"%u) ports! This is probably fatal!",conn->connection_number,
conn->remote_control_port, cport, conn->remote_timing_port, tport);
} else {
warn("Duplicate SETUP message with the same control (%u) and timing (%u) ports. This is "
"probably not fatal.",
warn("Connection %d: Duplicate SETUP message with the same control (%u) and timing (%u) ports. This is "
"probably not fatal.",conn->connection_number,
conn->remote_control_port, conn->remote_timing_port);
}
} else {
rtp_setup(&conn->local, &conn->remote, cport, tport, conn);
}
if (conn->local_audio_port == 0) {
debug(1, "SETUP seems to specify a null audio port.");
debug(1, "Connection %d: SETUP seems to specify a null audio port.",conn->connection_number);
goto error;
}
@@ -851,7 +851,7 @@ static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *
return;
error:
warn("Error in setup request -- unlocking play lock on RTSP conversation thread %d.",
warn("Connection %d: SETUP -- Error in setup request -- unlocking play lock.",
conn->connection_number);
playing_conn = NULL;
pthread_mutex_unlock(&play_lock);
@@ -1861,6 +1861,12 @@ authenticate:
static void *rtsp_conversation_thread_func(void *pconn) {
rtsp_conn_info *conn = pconn;
// create the player thread lock.
int rwli = pthread_rwlock_init(&conn->player_thread_lock, NULL);
if (rwli != 0)
die("Error %d initialising player_thread_lock for conversation thread %d.", rwli,
conn->connection_number);
rtp_initialise(conn);
rtsp_message *req, *resp;
@@ -1943,6 +1949,7 @@ static void *rtsp_conversation_thread_func(void *pconn) {
if (tstop) {
debug(3, "Synchronously terminate playing thread of RTSP conversation thread %d.",
conn->connection_number);
if (conn->player_thread)
debug(1, "RTSP Channel unexpectedly closed or a serious error occured -- closing the "
"player thread.");
@@ -1965,10 +1972,16 @@ static void *rtsp_conversation_thread_func(void *pconn) {
playing_conn = NULL;
pthread_mutex_unlock(&play_lock);
}
debug(2, "RTSP conversation thread %d terminated.", conn->connection_number);
// please_shutdown = 0;
debug(2, "Connection %d: RTSP thread terminated.", conn->connection_number);
conn->running = 0;
return NULL;
// release the player_thread_lock
int rwld = pthread_rwlock_destroy(&conn->player_thread_lock);
if (rwld)
debug(1, "Error %d destroying player_thread_lock for conversation thread %d.", rwld,
conn->connection_number);
pthread_exit(NULL);
}
/*
@@ -2088,8 +2101,8 @@ void rtsp_listen_loop(void) {
int acceptfd;
struct timeval tv;
while (1) {
tv.tv_sec = 300;
do {
tv.tv_sec = 60;
tv.tv_usec = 0;
for (i = 0; i < nsock; i++)
@@ -2182,7 +2195,13 @@ void rtsp_listen_loop(void) {
conn->running = 1; // this must happen before the thread is tracked
track_thread(conn);
}
}
perror("select");
die("fell out of the RTSP select loop");
} while (1);
mdns_unregister();
if (sockfd)
free(sockfd);
// perror("select");
// die("fell out of the RTSP select loop");
}
+1 -1
View File
@@ -48,7 +48,7 @@ general =
// as "org.gnome.ShairportSync" on the whichever bus you specify here: "system" (default) or "session".
};
// Advanced parameters for controlling how a Shairport Sync runs
// Advanced parameters for controlling how Shairport Sync runs a play session
sessioncontrol =
{
// run_this_before_play_begins = "/full/path/to/application and args"; // make sure the application has executable permission. If it's a script, include the shebang (#!/bin/...) on the first line
+32 -17
View File
@@ -274,6 +274,8 @@ int parse_options(int argc, char **argv) {
optind = j;
optCon = poptGetContext(NULL, optind, (const char **)argv, optionsTable, 0);
if (optCon == NULL)
die("Can not get a secondary popt context.");
poptSetOtherOptionHelp(optCon, "[OPTIONS]* ");
/* Now do options processing just to get a debug level */
@@ -318,6 +320,8 @@ int parse_options(int argc, char **argv) {
die("%s: %s", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
}
poptFreeContext(optCon);
if ((daemonisewith) && (daemonisewithout))
die("Select either daemonize_with_pid_file or daemonize_without_pid_file -- you have selected "
"both!");
@@ -888,6 +892,8 @@ int parse_options(int argc, char **argv) {
optind = j;
optCon = poptGetContext(NULL, optind, (const char **)argv, optionsTable, 0);
if (optCon == NULL)
die("Can not get a popt context.");
poptSetOtherOptionHelp(optCon, "[OPTIONS]* ");
/* Now do options processing, get portname */
@@ -935,6 +941,7 @@ int parse_options(int argc, char **argv) {
die("%s: %s", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
}
poptFreeContext(optCon);
#ifdef CONFIG_METADATA
if ((config.metadata_enabled == 1) && (config.metadata_pipename == NULL))
config.metadata_pipename = strdup("/tmp/shairport-sync-metadata");
@@ -956,6 +963,10 @@ int parse_options(int argc, char **argv) {
char hostname[100];
gethostname(hostname, 100);
char *i1 = str_replace(raw_service_name, "%h", hostname);
if (raw_service_name) {
free(raw_service_name);
raw_service_name = NULL;
}
if ((hostname[0] >= 'a') && (hostname[0] <= 'z'))
hostname[0] = hostname[0] - 0x20; // convert a lowercase first letter into a capital letter
char *i2 = str_replace(i1, "%H", hostname);
@@ -1041,13 +1052,15 @@ void shairport_startup_complete(void) {
}
}
const char *pid_file_proc(void) {
char fn[8192];
snprintf(fn, sizeof(fn), "%s/%s.pid", config.computed_piddir,
daemon_pid_file_ident ? daemon_pid_file_ident : "unknown");
// debug(1,"fn \"%s\".",fn);
return strdup(fn);
const char *pid_file_proc(char *fn, size_t max_length) {
if (fn) {
snprintf(fn, max_length, "%s/%s.pid", config.computed_piddir,
daemon_pid_file_ident ? daemon_pid_file_ident : "unknown");
// debug(1,"fn \"%s\".",fn);
} else {
debug(1, "the sise of the buffer for the PID file path is zero.");
}
return fn;
}
void exit_function() {
@@ -1168,11 +1181,6 @@ int main(int argc, char **argv) {
return 1;
}
// Point to a function to help locate where the PID file will go
// We always use this function because the default location
// is unsatisfactory. By default we want to use /var/run/shairport-sync/.
daemon_pid_file_proc = pid_file_proc;
/* Set indentification string for the daemon for both syslog and PID file */
daemon_pid_file_ident = daemon_log_ident = daemon_ident_from_argv0(argv[0]);
@@ -1350,10 +1358,16 @@ int main(int argc, char **argv) {
// audio_backend_latency_offset instead.
if (config.userSuppliedLatency) {
inform("The default latency setting is deprecated, as Shairport Sync can now get the correct "
"latency from the source.");
inform("Please remove this setting and use the relevant audio_backend_latency_offset setting, "
"if necessary, to compensate for delays elsewhere.");
inform("The fixed latency setting is deprecated, as Shairport Sync gets the correct "
"latency automatically from the source.");
inform("Use the audio_backend_latency_offset_in_seconds setting "
"instead to compensate for timing issues.");
if ((config.userSuppliedLatency != 0) &&
((config.userSuppliedLatency < 4410) ||
(config.userSuppliedLatency > BUFFER_FRAMES * 352 - 22050)))
die("An out-of-range fixed latency has been specified. It must be between 4410 and %d (at "
"44100 frames per second).",
BUFFER_FRAMES * 352 - 22050);
}
/* print out version */
@@ -1366,10 +1380,11 @@ int main(int argc, char **argv) {
debug(1, "Can't print the version information!");
}
char pid_file_path_string[4096] = "\0";
/* Print out options */
debug(1, "statistics_requester status is %d.", config.statistics_requested);
debug(1, "daemon status is %d.", config.daemonise);
debug(1, "deamon pid file is \"%s\".", pid_file_proc());
debug(1, "deamon pid file is \"%s\".", pid_file_proc(pid_file_path_string, 4096));
debug(1, "rtsp listening port is %d.", config.port);
debug(1, "udp base port is %d.", config.udp_port_base);
debug(1, "udp port range is %d.", config.udp_port_range);