#include <syslog.h>
#endif
-
-
#ifdef CONFIG_ALSA
void set_alsa_out_dev(char *);
#endif
char *version_string = malloc(1024);
if (version_string) {
#ifdef CONFIG_USE_GIT_VERSION_STRING
- if (git_version_string[0] != '\0')
- strcpy(version_string, git_version_string);
- else
+ if (git_version_string[0] != '\0')
+ strcpy(version_string, git_version_string);
+ else
#endif
- strcpy(version_string, PACKAGE_VERSION);
+ strcpy(version_string, PACKAGE_VERSION);
#ifdef CONFIG_AIRPLAY_2
- strcat(version_string, "-AirPlay2");
+ strcat(version_string, "-AirPlay2");
#endif
#ifdef CONFIG_APPLE_ALAC
strcat(version_string, "-alac");
struct ifaddrs *ifa = NULL;
int i = 0;
uint8_t *t = id;
- for (i = 0; i < int_length ; i++) {
+ for (i = 0; i < int_length; i++) {
*t++ = 0;
}
char *configfile;
char *regtype; // The regtype is the service type followed by the protocol, separated by a dot, by
// default “_raop._tcp.” for AirPlay 1.
- char *regtype2; // The regtype is the service type followed by the protocol, separated by a dot, by
- // default “_raop._tcp.” for AirPlay 2.
- char *interface; // a string containg the interface name, or NULL if nothing specified
- int interface_index; // only valid if the interface string is non-NULL
+ char *regtype2; // The regtype is the service type followed by the protocol, separated by a dot,
+ // by default “_raop._tcp.” for AirPlay 2.
+ char *interface; // a string containg the interface name, or NULL if nothing specified
+ int interface_index; // only valid if the interface string is non-NULL
double audio_backend_buffer_desired_length; // this will be the length in seconds of the
// audio backend buffer -- the DAC buffer for ALSA
double audio_backend_buffer_interpolation_threshold_in_seconds; // below this, soxr interpolation
extern char git_version_string[];
#endif
-
#endif // _COMMON_H
#ifdef __cplusplus
for (b = mdns_backends; *b; b++) {
if (strcmp((*b)->name, config.mdns_name) != 0) // Not the one we are looking for
continue;
- int error = (*b)->mdns_register(ap1_service_name, config.service_name, config.port, txt_records, secondary_txt_records);
+ int error = (*b)->mdns_register(ap1_service_name, config.service_name, config.port,
+ txt_records, secondary_txt_records);
if (error >= 0) {
config.mdns = *b;
}
} else {
// default -- pick the first back end
for (b = mdns_backends; *b; b++) {
- int error = (*b)->mdns_register(ap1_service_name, config.service_name, config.port, txt_records, secondary_txt_records);
+ int error = (*b)->mdns_register(ap1_service_name, config.service_name, config.port,
+ txt_records, secondary_txt_records);
if (error >= 0) {
config.mdns = *b;
break;
typedef struct {
char *name;
- int (*mdns_register)(char *ap1name, char *ap2name, int port, char **txt_records, char **secondary_txt_records);
+ int (*mdns_register)(char *ap1name, char *ap2name, int port, char **txt_records,
+ char **secondary_txt_records);
void (*mdns_unregister)(void);
void (*mdns_dacp_monitor_start)();
void (*mdns_dacp_monitor_set_id)(const char *);
selected_interface = AVAHI_IF_UNSPEC;
if (ap2_text_record_string_list) {
ret = avahi_entry_group_add_service_strlst(group, selected_interface, AVAHI_PROTO_UNSPEC, 0,
- ap2_service_name, config.regtype2, NULL, NULL, port,
- ap2_text_record_string_list);
+ ap2_service_name, config.regtype2, NULL, NULL,
+ port, ap2_text_record_string_list);
}
if ((ret == 0) && (text_record_string_list)) {
ret = avahi_entry_group_add_service_strlst(group, selected_interface, AVAHI_PROTO_UNSPEC, 0,
service_name, config.regtype, NULL, NULL, port,
text_record_string_list);
}
- if (ret == 0) {
- ret = avahi_entry_group_commit(group);
- debug(2, "avahi: avahi_entry_group_commit %d", ret);
- if (ret < 0)
- debug(1, "avahi: avahi_entry_group_commit failed");
- } else if (ret < 0) {
- debug(1, "avahi: avahi_entry_group_add_service failed");
- } else {
- debug(1, "avahi: unexpected positive return");
- }
+ if (ret == 0) {
+ ret = avahi_entry_group_commit(group);
+ debug(2, "avahi: avahi_entry_group_commit %d", ret);
+ if (ret < 0)
+ debug(1, "avahi: avahi_entry_group_commit failed");
+ } else if (ret < 0) {
+ debug(1, "avahi: avahi_entry_group_add_service failed");
+ } else {
+ debug(1, "avahi: unexpected positive return");
+ }
}
}
}
}
-static int avahi_register(char *ap1name, char *ap2name, int srvport, char **txt_records,char **secondary_txt_records) {
+static int avahi_register(char *ap1name, char *ap2name, int srvport, char **txt_records,
+ char **secondary_txt_records) {
// debug(1, "avahi_register.");
service_name = strdup(ap1name);
if (ap2name != NULL)
text_record_string_list = avahi_string_list_new_from_array((const char **)txt_records, -1);
if (secondary_txt_records != NULL)
- ap2_text_record_string_list = avahi_string_list_new_from_array((const char **)secondary_txt_records, -1);
+ ap2_text_record_string_list =
+ avahi_string_list_new_from_array((const char **)secondary_txt_records, -1);
port = srvport;
} else
debug(1, "avahi attempt to free NULL text_record_string_list");
-
if (ap2_text_record_string_list) {
debug(2, "avahi free ap_2text_record_string_list");
avahi_string_list_free(ap2_text_record_string_list);
int flush_needed = 0;
int drop_request = 0;
if (conn->flush_requested == 1) {
- if (conn->flush_rtp_timestamp == 0) {
- debug(1, "flush request: flush frame 0 -- flush assumed to be needed.");
- flush_needed = 1;
- drop_request = 1;
- } else {
- debug(1,"flush");
- if ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0)) {
- 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,
+ if (conn->flush_rtp_timestamp == 0) {
+ debug(1, "flush request: flush frame 0 -- flush assumed to be needed.");
+ flush_needed = 1;
+ drop_request = 1;
+ } else {
+ if ((conn->ab_synced) && ((conn->ab_write - conn->ab_read) > 0)) {
+ 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) {
- debug(2,
+ 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) {
+ 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);
- drop_request = 1;
- flush_needed = 1;
+ drop_request = 1;
+ flush_needed = 1;
+ } else {
+ debug(2,
+ "flush request: flush frame %u pending -- 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);
+ flush_needed = 1;
+ }
} else {
- debug(
- 2,
- "flush request: flush frame %u pending -- 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);
- flush_needed = 1;
+ 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;
}
- } 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;
}
}
}
+ } else {
+ debug(3,
+ "flush request: flush frame %u -- buffer not synced or empty: synced: %d, "
+ "ab_read: "
+ "%u, ab_write: %u",
+ conn->flush_rtp_timestamp, conn->ab_synced, conn->ab_read, conn->ab_write);
+ conn->flush_requested = 0; // remove the request
+ // leave flush request pending and don't do a buffer flush, because there isn't one
}
- } else {
- debug(
- 3,
- "flush request: flush frame %u -- buffer not synced or empty: synced: %d, ab_read: "
- "%u, ab_write: %u",
- conn->flush_rtp_timestamp, conn->ab_synced, conn->ab_read, conn->ab_write);
- conn->flush_requested = 0; // remove the request
- // leave flush request pending and don't do a buffer flush, because there isn't one
}
}
- }
if (flush_needed) {
debug(2, "flush request: flush done.");
ab_resync(conn); // no cancellation points
int64_t lt = conn->first_packet_time_to_play - local_time_now;
- debug(1, "Connection %d: Lead time for first frame %" PRId64 ": %f seconds.",
+ 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;
pthread_setcancelstate(oldState, NULL);
double initial_volume = config.airplay_volume; // default
- if (conn->initial_airplay_volume_set) // if we have been given an initial volume
+ if (conn->initial_airplay_volume_set) // if we have been given an initial volume
initial_volume = conn->initial_airplay_volume;
// set the default volume to whatever it was before, as stored in the config airplay_volume
debug(2, "Set initial volume to %f.", initial_volume);
// remove the bias when reporting the error to make it the true error
- debug(1,
+ debug(2,
"first frame sync error (positive --> late): %" PRId64
" frames, %.3f mS at %d frames per second output.",
sync_error + first_frame_early_bias,
int64_t filler_length =
(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 " frames (%f seconds), with frame: %u.",
- sync_error, (sync_error * 1.0) / config.output_rate, inframe->given_timestamp);
+ debug(1,
+ "Large positive sync error of: %" PRId64
+ " frames (%f seconds), with frame: %u.",
+ sync_error, (sync_error * 1.0) / config.output_rate,
+ inframe->given_timestamp);
int64_t local_frames_to_drop = sync_error / conn->output_sample_ratio;
uint32_t frames_to_drop_sized = local_frames_to_drop;
do_flush(inframe->given_timestamp + frames_to_drop_sized, conn);
debug(1,
"Large negative sync error of: %" PRId64
" frames (%f seconds), with frame: %" PRIu32 ".",
- sync_error, (sync_error * 1.0) / config.output_rate,inframe->given_timestamp);
+ sync_error, (sync_error * 1.0) / config.output_rate,
+ inframe->given_timestamp);
int64_t silence_length = -sync_error;
if (silence_length > (filler_length * 5))
silence_length = filler_length * 5;
ap2_buffer plain_buf;
} ap2_pairing;
-/*
-typedef struct file_cipher_context {
- struct pair_cipher_context *cipher_context;
- int active; // can be created during a pair setup but not activated until next read
- int fd;
- void *input_plaintext_buffer;
- void *input_plaintext_buffer_toq;
- size_t input_plaintext_buffer_bytes_occupied;
-} file_cipher_context;
-*/
+// flush requests are stored in order of flushFromSeq
+// on the basis that block numbers are monotonic modulo 2^24
+typedef struct flush_request_t {
+ int flushNow; // if true, the flushFrom stuff is invalid
+ uint32_t flushFromSeq;
+ uint32_t flushFromTS;
+ uint32_t flushUntilSeq;
+ uint32_t flushUntilTS;
+ struct flush_request_t *next;
+} flush_request_t;
+
#endif
typedef struct {
uint64_t last_anchor_time_of_update;
ssize_t ap2_audio_buffer_size;
+ flush_request_t *flush_requests; // if non-null, there are flush requests, mutex protected
int ap2_flush_requested;
int ap2_flush_from_valid;
uint32_t ap2_flush_from_rtp_timestamp;
return response;
}
-int ptp_get_clock_info(uint64_t *actual_clock_id, uint64_t *raw_offset, uint64_t *mastership_start_time) {
+int ptp_get_clock_info(uint64_t *actual_clock_id, uint64_t *raw_offset,
+ uint64_t *mastership_start_time) {
int response = clock_ok;
pthread_cleanup_debug_mutex_lock(&ptp_access_mutex, 10000, 1);
if (actual_clock_id != NULL)
#include "config.h"
#include <stdint.h>
-int ptp_get_clock_info(uint64_t *actual_clock_id, uint64_t *raw_offset, uint64_t *mastership_start_time);
+int ptp_get_clock_info(uint64_t *actual_clock_id, uint64_t *raw_offset,
+ uint64_t *mastership_start_time);
void ptp_send_control_message_string(const char *msg);
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
// 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 packet 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.
-
+
uint32_t rtlt = nctohl(&packet[4]); // raw timestamp less latency
uint32_t rt = nctohl(&packet[16]); // raw timestamp
-
+
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 "
debug(2, "Connection %d: Set Anchor Clock: %" PRIx64 ".", conn->connection_number, clock_id);
conn->anchor_clock_is_new = 1;
}
- // debug(1,"set anchor info clock: %" PRIx64", rtptime: %u, networktime: %" PRIx64 ".", clock_id, rtptime, networktime);
+ // debug(1,"set anchor info clock: %" PRIx64", rtptime: %u, networktime: %" PRIx64 ".", clock_id,
+ // rtptime, networktime);
conn->anchor_remote_info_is_valid = 1;
conn->anchor_rtptime = rtptime;
conn->anchor_time = networktime;
// wait at least this time before using the new master clock
if (duration_of_mastership < 700000000) {
response = clock_not_ready;
- } else if ((duration_of_mastership > 5000000000) || (conn->last_anchor_info_is_valid == 0)) {
+ } else if ((duration_of_mastership > 5000000000) ||
+ (conn->last_anchor_info_is_valid == 0)) {
// use the master clock if it's at least this old or we have no alternative
// and at least it is the minimum age.
conn->last_anchor_rtptime = conn->anchor_rtptime;
conn->last_anchor_time_of_update = get_absolute_time_in_ns();
conn->last_anchor_info_is_valid = 1;
if (conn->anchor_clock_is_new != 0)
- debug(1,"Connection %d: New anchor clock %" PRIx64 " recognised.", conn->connection_number, conn->anchor_clock);
+ debug(2, "Connection %d: New anchor clock %" PRIx64 " recognised.",
+ conn->connection_number, conn->anchor_clock);
conn->anchor_clock_is_new = 0;
}
} else {
// so, if the anchor has not changed, it must be that the master clock has changed
if (conn->anchor_clock_is_new != 0)
- debug(1,"Connection %d: Anchor clock has changed to %" PRIx64 ", master clock is: %" PRIx64 ".", conn->connection_number, conn->anchor_clock, actual_clock_id);
+ debug(1,
+ "Connection %d: Anchor clock has changed to %" PRIx64 ", master clock is: %" PRIx64
+ ".",
+ conn->connection_number, conn->anchor_clock, actual_clock_id);
if ((conn->last_anchor_info_is_valid != 0) && (conn->anchor_clock_is_new == 0)) {
int64_t time_since_last_update =
get_absolute_time_in_ns() - conn->last_anchor_time_of_update;
if (time_since_last_update > 5000000000) {
- debug(1, "Connection %d: Master clock has changed to %" PRIx64 ".", conn->connection_number, actual_clock_id);
+ debug(1, "Connection %d: Master clock has changed to %" PRIx64 ".",
+ conn->connection_number, actual_clock_id);
// here we adjust the time of the anchor rtptime
// we know its local time, so we use the new clocks's offset to
// calculate what time that must be on the new clock
conn->anchor_clock = actual_clock_id;
}
} else {
- response = clock_not_valid; // no current clock information and no previous clock info
+ response = clock_not_valid; // no current clock information and no previous clock info
}
}
} else {
actual_clock_id);
break;
case clock_not_ready:
- debug(2, "Connection %d: NQPTP master clock %" PRIx64 " is available but not ready.", conn->connection_number, actual_clock_id);
+ debug(2, "Connection %d: NQPTP master clock %" PRIx64 " is available but not ready.",
+ conn->connection_number, actual_clock_id);
break;
case clock_service_unavailable:
debug(1, "Connection %d: NQPTP clock is not available.", conn->connection_number);
debug(1, "Connection %d: Can not access NQPTP clock information.", conn->connection_number);
break;
case clock_no_master:
- debug(1, "Connection %d: No NQPTP master clock.", conn->connection_number);
+ debug(2, "Connection %d: No NQPTP master clock.", conn->connection_number);
break;
case clock_no_anchor_info:
debug(1, "Connection %d: No Clock Anchor.", conn->connection_number);
debug(2, "Buffered Audio Receiver Cleanup Done.");
}
+// not used right now, but potentially useful for understanding flush requests
+void display_flush_requests(int activeOnly, uint32_t currentSeq, uint32_t currentTS,
+ rtsp_conn_info *conn) {
+ if (conn->flush_requests == NULL) {
+ if (activeOnly == 0)
+ debug(1, "No flush requests.");
+ } else {
+ flush_request_t *t = conn->flush_requests;
+ do {
+ if (t->flushNow) {
+ debug(1, "immediate flush to untilSeq: %u, untilTS: %u.", t->flushUntilSeq,
+ t->flushUntilTS);
+ } else {
+ if (activeOnly == 0)
+ debug(1, "fromSeq: %u, fromTS: %u, to untilSeq: %u, untilTS: %u.", t->flushFromSeq,
+ t->flushFromTS, t->flushUntilSeq, t->flushUntilTS);
+ else if ((activeOnly == 1) &&
+ (currentSeq >=
+ (t->flushFromSeq -
+ 1))) // the -1 is because you might have to trim the end of the previous block
+ debug(1,
+ "fromSeq: %u, fromTS: %u, to untilSeq: %u, untilTS: %u, with currentSeq: %u, "
+ "currentTS: %u.",
+ t->flushFromSeq, t->flushFromTS, t->flushUntilSeq, t->flushUntilTS, currentSeq,
+ currentTS);
+ }
+ t = t->next;
+ } while (t != NULL);
+ }
+}
+
void *rtp_buffered_audio_processor(void *arg) {
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
pthread_cleanup_push(rtp_buffered_audio_cleanup_handler, arg);
int pcm_buffer_occupancy = 0;
int pcm_buffer_read_point = 0; // offset to where the next buffer should come from
uint32_t pcm_buffer_read_point_rtptime = 0;
- uint32_t expected_rtptime;
+ // uint32_t expected_rtptime;
uint64_t blocks_read = 0;
int flush_requested = 0;
int streaming_has_started = 0;
int play_enabled = 0;
uint32_t flush_from_timestamp;
- int dump_block_info = 0;
double requested_lead_time = 0.100; // normal lead time minimum
+ reset_buffer(conn); // in case there is any garbage in the player
do {
int flush_is_delayed = 0;
int flush_newly_requested = 0;
int flush_request_active = 0;
if (conn->ap2_flush_requested) {
- if (conn->ap2_flush_from_valid == 0) {// i.e. a flush from right now
+ if (conn->ap2_flush_from_valid == 0) { // i.e. a flush from right now
flush_request_active = 1;
flush_is_delayed = 0;
} else {
flush_is_delayed = 1;
- dump_block_info = 1;
flush_from_timestamp = conn->ap2_flush_from_rtp_timestamp;
int32_t blocks_to_start_of_flush = conn->ap2_flush_from_sequence_number - seq_no;
if (blocks_to_start_of_flush <= 0) {
if (flush_requested == 0) {
// here, a flush has been newly requested
- debug(1,"Flush requested:");
+ debug(2, "Flush requested.");
if (conn->ap2_flush_from_valid) {
- debug(1," fromTS: %u", conn->ap2_flush_from_rtp_timestamp);
- debug(1," fromSeq: %u", conn->ap2_flush_from_sequence_number);
- debug(1,"--");
+ debug(2, " fromTS: %u", conn->ap2_flush_from_rtp_timestamp);
+ debug(2, " fromSeq: %u", conn->ap2_flush_from_sequence_number);
+ debug(2, "--");
}
- debug(1," untilTS: %u", conn->ap2_flush_until_rtp_timestamp);
- debug(1," untilSeq: %u", conn->ap2_flush_until_sequence_number);
- debug(1,"--");
- debug(1," currentTS_Start: %u", pcm_buffer_read_point_rtptime);
- uint32_t fib = (pcm_buffer_occupancy - pcm_buffer_read_point) / 4;
- debug(1," framesInBuffer: %u", fib);
- uint32_t endTS = fib + pcm_buffer_read_point_rtptime;
- debug(1," currentTS_End: %u", endTS); // a frame occupies 4 bytes
- debug(1," currentSeq: %u", seq_no);
-
- flush_newly_requested = 1;
+ debug(2, " untilTS: %u", conn->ap2_flush_until_rtp_timestamp);
+ debug(2, " untilSeq: %u", conn->ap2_flush_until_sequence_number);
+ debug(2, "--");
+ debug(2, " currentTS_Start: %u", pcm_buffer_read_point_rtptime);
+ uint32_t fib = (pcm_buffer_occupancy - pcm_buffer_read_point) / 4;
+ debug(2, " framesInBuffer: %u", fib);
+ uint32_t endTS = fib + pcm_buffer_read_point_rtptime;
+ debug(2, " currentTS_End: %u", endTS); // a frame occupies 4 bytes
+ debug(2, " currentSeq: %u", seq_no);
+
+ flush_newly_requested = 1;
}
// blocks_read to ensure seq_no is valid
if ((blocks_read != 0) && (seq_no >= flushUntilSeq)) {
// we have reached or overshot the flushUntilSeq block
- // if (flushUntilSeq != seq_no)
- debug(1,"flushUntilSeq %u reached or overshot at %u.", flushUntilSeq, seq_no);
+ if (flushUntilSeq != seq_no)
+ debug(1, "flushUntilSeq %u reached or overshot at %u.", flushUntilSeq, seq_no);
conn->ap2_flush_requested = 0;
flush_request_active = 0;
flush_newly_requested = 0;
play_enabled = conn->ap2_play_enabled;
debug_mutex_unlock(&conn->flush_mutex, 3);
-
// do this outside the flush mutex
if (flush_newly_complete) {
- debug(1,"Flush Complete.");
+ debug(2, "Flush Complete.");
blocks_read = 0;
}
reset_buffer(conn);
if (flush_is_delayed == 0) {
- debug(1,"Immediate Buffered Audio Flush Started.");
- //player_full_flush(conn);
+ debug(2, "Immediate Buffered Audio Flush Started.");
+ // player_full_flush(conn);
streaming_has_started = 0;
pcm_buffer_occupancy = 0;
pcm_buffer_read_point = 0;
- dump_block_info = 0;
} else {
- debug(1,"Delayed Buffered Audio Flush Started.");
+ debug(2, "Delayed Buffered Audio Flush Started.");
streaming_has_started = 0;
pcm_buffer_occupancy = 0;
pcm_buffer_read_point = 0;
0) {
int64_t lead_time = buffer_should_be_time - get_absolute_time_in_ns();
// debug(1,"lead time in buffered_audio is %f milliseconds.", lead_time * 0.000001);
- if (blocks_read > 1) {
- if ((lead_time >= (int64_t)(requested_lead_time * 1000000000)) ||
- (streaming_has_started != 0)) {
- if (streaming_has_started == 0)
- debug(1, "Connection %d: buffered audio starting frame: %u, lead time: %f seconds.", conn->connection_number, pcm_buffer_read_point_rtptime,
+ if (blocks_read > 2) {
+ if ((lead_time >= (int64_t)(requested_lead_time * 1000000000)) ||
+ (streaming_has_started != 0)) {
+ if (streaming_has_started == 0)
+ debug(
+ 2,
+ "Connection %d: buffered audio starting frame: %u, lead time: %f seconds.",
+ conn->connection_number, pcm_buffer_read_point_rtptime,
0.000000001 * lead_time);
- //else {
- //if (expected_rtptime != pcm_buffer_read_point_rtptime)
- // debug(1,"actual rtptime is %u, expected rtptime is %u.", pcm_buffer_read_point_rtptime, expected_rtptime);
- //}
- expected_rtptime = pcm_buffer_read_point_rtptime + 352;
- player_put_packet(0, 0, pcm_buffer_read_point_rtptime,
- pcm_buffer + pcm_buffer_read_point, 352, conn);
- streaming_has_started++;
- usleep(2000);
- }
+ // else {
+ // if (expected_rtptime != pcm_buffer_read_point_rtptime)
+ // debug(1,"actual rtptime is %u, expected rtptime is %u.",
+ // pcm_buffer_read_point_rtptime, expected_rtptime);
+ //}
+ // expected_rtptime = pcm_buffer_read_point_rtptime + 352;
+ player_put_packet(0, 0, pcm_buffer_read_point_rtptime,
+ pcm_buffer + pcm_buffer_read_point, 352, conn);
+ streaming_has_started++;
+ usleep(2000);
+ }
}
pcm_buffer_read_point_rtptime += 352;
// uint8_t marker = 0;
// uint8_t payload_type = 0;
-/*
- if (dump_block_info != 0) {
- if ((flush_requested) && (seq_no < flushUntilSeq))
- debug(1, "block %u, rtptime %u, should be skipped.", seq_no, timestamp);
- else
- debug(1, "block %u, rtptime %u, should be decoded and used.", seq_no, timestamp);
-
- if (((flush_requested != 0) && (seq_no == flushUntilSeq)) || ((flush_requested == 0) && (new_buffer_needed))) {
- debug(1, "block %u, rtptime %u, will be decoded and used.", seq_no, timestamp);
- }
- }
-*/
-
-/*
- if ((blocks_read != 0) && (seq_no != previous_seq_no + 1)) {
- if (previous_seq_no != 0)
- debug(1, "block discontinuity: from sequence number %u to sequence number %u.",
- previous_seq_no, seq_no);
- if (pcm_buffer_occupancy != 0) {
- debug(1,
- "rtptime discontinuity! Existing pcm buffer contents with timestamp "
- "%u, seq_no %u to new block with timestamp %u, seq_no %u",
- pcm_buffer_read_point_rtptime, previous_seq_no, timestamp, seq_no);
- pcm_buffer_occupancy = 0;
- }
- // if still playing, this is a problem -- need to reset the player
- if (play_enabled)
- player_full_flush(conn);
- }
-*/
-
-
// previous_seq_no = seq_no;
// at this point, we can check if we can to flush this packet -- we won't have
if ((flush_requested) && (seq_no >= flushUntilSeq)) {
uint64_t should_be_time;
- if ((frame_to_local_time(timestamp, &should_be_time, conn) ==
- 0) && (play_enabled)) {
- // play enabled will be off when this is a full flush and the anchor information is not valid
+ if ((frame_to_local_time(timestamp, &should_be_time, conn) == 0) && (play_enabled)) {
+ // play enabled will be off when this is a full flush and the anchor information is not
+ // valid
int64_t lead_time = should_be_time - get_absolute_time_in_ns();
- debug(1,"flush completed to seq: %u with rtptime: %u, lead time: 0x%" PRIx64 " nanoseconds, i.e. %f sec.", seq_no, timestamp, lead_time, lead_time * 0.000000001);
+ debug(2,
+ "flush completed to seq: %u with rtptime: %u, lead time: 0x%" PRIx64
+ " nanoseconds, i.e. %f sec.",
+ seq_no, timestamp, lead_time, lead_time * 0.000000001);
} else {
- debug(1,"flush completed to seq: %u with rtptime: %u.", seq_no, timestamp);
+ debug(2, "flush completed to seq: %u with rtptime: %u.", seq_no, timestamp);
}
}
- if (((flush_requested != 0) && (seq_no == flushUntilSeq)) || ((flush_requested == 0) && (new_buffer_needed))) {
-
+ if (((flush_requested != 0) && (seq_no == flushUntilSeq)) ||
+ ((flush_requested == 0) && (new_buffer_needed))) {
// if we are here because of a flush request, it must be the case that
// flushing the pcm buffer wasn't enough, as the request would have been turned off by now
if (pcm_buffer_occupancy == 0) {
// they should match and the read point should be zero
- // if ((blocks_read != 0) && (pcm_buffer_read_point_rtptime != timestamp)) {
+ // if ((blocks_read != 0) && (pcm_buffer_read_point_rtptime != timestamp)) {
// debug(2, "set pcm_buffer_read_point_rtptime from %u to %u.",
// pcm_buffer_read_point_rtptime, timestamp);
- pcm_buffer_read_point_rtptime = timestamp;
- pcm_buffer_read_point = 0;
+ pcm_buffer_read_point_rtptime = timestamp;
+ pcm_buffer_read_point = 0;
//}
}
// copy the PCM audio into the PCM buffer.
// make sure it's big enough first
- // also, check it if needs to be truncated but to an impending delayed flush_is_delayed
+ // also, check it if needs to be truncated but to an impending delayed
+ // flush_is_delayed
if (flush_is_delayed) {
// see if the flush_from_timestamp is in the buffer
- int32_t samples_remaining = (flush_from_timestamp - pcm_buffer_read_point_rtptime);
- if ((samples_remaining > 0) && ((samples_remaining * 4) < dst_bufsize)) {
- debug(1,"samples remaining before flush: %d, number of samples %d. flushFromTS: %u, pcm_buffer_read_point_rtptime: %u.", samples_remaining, dst_bufsize/4, flush_from_timestamp, pcm_buffer_read_point_rtptime);
+ int32_t samples_remaining =
+ (flush_from_timestamp - pcm_buffer_read_point_rtptime);
+ if ((samples_remaining > 0) &&
+ ((samples_remaining * 4) < dst_bufsize)) {
+ debug(2,
+ "samples remaining before flush: %d, number of samples %d. "
+ "flushFromTS: %u, pcm_buffer_read_point_rtptime: %u.",
+ samples_remaining, dst_bufsize / 4, flush_from_timestamp,
+ pcm_buffer_read_point_rtptime);
dst_bufsize = samples_remaining * 4;
}
}
}
}
+void add_flush_request(int flushNow, uint32_t flushFromSeq, uint32_t flushFromTS,
+ uint32_t flushUntilSeq, uint32_t flushUntilTS, rtsp_conn_info *conn) {
+ // immediate flush requests are added sequentially. Don't know how more than one could arise, TBH
+ flush_request_t **t = &conn->flush_requests;
+ int done = 0;
+ do {
+ flush_request_t *u = *t;
+ if ((u == NULL) || ((u->flushNow == 0) && (flushNow != 0)) ||
+ (flushFromSeq < u->flushFromSeq) ||
+ ((flushFromSeq == u->flushFromSeq) && (flushFromTS < u->flushFromTS))) {
+ flush_request_t *n = (flush_request_t *)calloc(sizeof(flush_request_t), 1);
+ n->flushNow = flushNow;
+ n->flushFromSeq = flushFromSeq;
+ n->flushFromTS = flushFromTS;
+ n->flushUntilSeq = flushUntilSeq;
+ n->flushUntilTS = flushUntilTS;
+ n->next = u;
+ *t = n;
+ done = 1;
+ } else {
+ t = &u->next;
+ }
+ } while (done == 0);
+}
+
+void display_all_flush_requests(rtsp_conn_info *conn) {
+ if (conn->flush_requests == NULL) {
+ debug(1, "No flush requests.");
+ } else {
+ flush_request_t *t = conn->flush_requests;
+ do {
+ if (t->flushNow) {
+ debug(1, "immediate flush to untilSeq: %u, untilTS: %u.", t->flushUntilSeq,
+ t->flushUntilTS);
+ } else {
+ debug(1, "fromSeq: %u, fromTS: %u, to untilSeq: %u, untilTS: %u.", t->flushFromSeq,
+ t->flushFromTS, t->flushUntilSeq, t->flushUntilTS);
+ }
+ t = t->next;
+ } while (t != NULL);
+ }
+}
+
// park a null at the line ending, and return the next line pointer
// accept \r, \n, or \r\n
static char *nextline(char *in, int inbuf) {
} else {
plist_get_uint_val(item, &flushFromTS);
if (flushFromValid == 0)
- debug(1,"flushFromTS without flushFromSeq!");
+ debug(1, "flushFromTS without flushFromSeq!");
debug(2, "flushFromTS is %" PRId64 ".", flushFromTS);
}
}
debug_mutex_lock(&conn->flush_mutex, 1000, 1);
- conn->ap2_flush_from_valid = flushFromValid;
- // a flush with from... components will not be followed by a setanchoe (i.e. a play)
+ // a flush with from... components will not be followed by a setanchor (i.e. a play)
// if it's a flush that will be followed by a setanchor (i.e. a play) then stop play now.
if (flushFromValid == 0)
conn->ap2_play_enabled = 0;
- conn->ap2_flush_from_sequence_number = flushFromSeq;
- conn->ap2_flush_from_rtp_timestamp = flushFromTS;
+ // add the exact request as made to the linked list (not used for anything but diagnostics now)
+ // int flushNow = 0;
+ // if (flushFromValid == 0)
+ // flushNow = 1;
+ // add_flush_request(flushNow, flushFromSeq, flushFromTS, flushUntilSeq, flushUntilTS, conn);
+
+ // now, if it's an immediate flush, replace the existing request, if any
+ // but it if's a deferred flush and there is an existing deferred request,
+ // only update the flushUntil stuff -- that seems to preserve
+ // the intended semantics
+
+ // so, always replace these
conn->ap2_flush_until_sequence_number = flushUntilSeq;
conn->ap2_flush_until_rtp_timestamp = flushUntilTS;
+
+ if ((conn->ap2_flush_requested != 0) && (conn->ap2_flush_from_valid != 0) &&
+ (flushFromValid != 0)) {
+ // if there is a request already, and it's a deferred request, and the current request is also
+ // deferred... do nothing! -- leave the starting point in place. Yeah, yeah, we know de Morgan's
+ // Law, but this seems clearer
+ } else {
+ conn->ap2_flush_from_sequence_number = flushFromSeq;
+ conn->ap2_flush_from_rtp_timestamp = flushFromTS;
+ }
+
+ conn->ap2_flush_from_valid = flushFromValid;
conn->ap2_flush_requested = 1;
+
+ // reflect the possibly updated flush request
+ // add_flush_request(flushNow, conn->ap2_flush_from_sequence_number,
+ // conn->ap2_flush_from_rtp_timestamp, conn->ap2_flush_until_sequence_number,
+ // conn->ap2_flush_until_rtp_timestamp, conn);
+
debug_mutex_unlock(&conn->flush_mutex, 3);
if (flushFromValid)
- debug(1,"Deferred Flush Requested");
+ debug(2, "Deferred Flush Requested");
else
- debug(1,"Immediate Flush Requested");
+ debug(2, "Immediate Flush Requested");
+
+ // display_all_flush_requests(conn);
resp->respcode = 200;
}
if (!strncmp(cp, "volume: ", strlen("volume: "))) {
float volume = atof(cp + strlen("volume: "));
- debug(1, "Connection %d: request to set AirPlay Volume to: %f.", conn->connection_number, volume);
+ debug(2, "Connection %d: request to set AirPlay Volume to: %f.", conn->connection_number,
+ volume);
// if we are playing, go ahead and change the volume
if (try_to_hold_play_lock(conn) == 0) {
player_volume(volume, conn);
} else
#endif
{
- debug(1, "Connection %d, unrecognised parameter: \"%s\" (%d)\n", conn->connection_number, cp, strlen(cp));
+ debug(1, "Connection %d, unrecognised parameter: \"%s\" (%d)\n", conn->connection_number, cp,
+ strlen(cp));
}
cp = next;
}
if ((req->content) && (req->contentlength == strlen("volume\r\n")) &&
strstr(req->content, "volume") == req->content) {
- debug(2,"Connection %d: Current volume (%.6f) requested", conn->connection_number, config.airplay_volume);
+ 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) {
resp->content = p;
// debug(2, "received parameters in SET_PARAMETER request.");
handle_set_parameter_parameter(conn, req, resp); // this could be volume or progress
} else {
- debug(1, "Connection %d: received unknown Content-Type \"%s\" in SET_PARAMETER request.", conn->connection_number, ct);
- debug_print_msg_headers(1,req);
+ debug(1, "Connection %d: received unknown Content-Type \"%s\" in SET_PARAMETER request.",
+ conn->connection_number, ct);
+ debug_print_msg_headers(1, req);
}
} else {
- debug(1, "Connection %d: missing Content-Type header in SET_PARAMETER request.", conn->connection_number);
+ debug(1, "Connection %d: missing Content-Type header in SET_PARAMETER request.",
+ conn->connection_number);
}
resp->respcode = 200;
}
char *hdr = msg_get_header(req, "Apple-Challenge");
if (!hdr)
return;
- debug(1,"Apple Challenge");
+ debug(1, "Apple Challenge");
SOCKADDR fdsa;
socklen_t sa_len = sizeof(fdsa);
getsockname(fd, (struct sockaddr *)&fdsa, &sa_len);
maxfd = sockfd[i];
}
- char **t1; // ap1 test records
- char **t2; // two text records
+ char **t1; // ap1 test records
+ char **t2; // two text records
t1 = NULL;
t2 = NULL;
char **p = txt_records;
t1 = p; // first set of text records
-// make up a firmware version
+ // make up a firmware version
char firmware_version[64];
#ifdef CONFIG_USE_GIT_VERSION_STRING
- if (git_version_string[0] != '\0')
- snprintf(firmware_version, sizeof(firmware_version), "fv=%s",git_version_string);
- else
+ if (git_version_string[0] != '\0')
+ snprintf(firmware_version, sizeof(firmware_version), "fv=%s", git_version_string);
+ else
#endif
- snprintf(firmware_version, sizeof(firmware_version), "fv=%s",PACKAGE_VERSION);
+ snprintf(firmware_version, sizeof(firmware_version), "fv=%s", PACKAGE_VERSION);
#ifdef CONFIG_AIRPLAY_2
char ap1_featuresString[64];
pkString_make(pkString + strlen("pk="), sizeof(pkString) - strlen("pk="),
config.airplay_device_id);
-// the ap1 text record is different if it is set up for ap2
+ // the ap1 text record is different if it is set up for ap2
*p++ = "cn=0,1";
*p++ = "da=true";
*p++ = "et=0,4";
*p++ = NULL;
#endif
-
#ifdef CONFIG_AIRPLAY_2
// make up a secondary set of text records
char *secondary_txt_records[64];
*p++ = NULL;
#endif
-
- mdns_register(t1,t2);
+ mdns_register(t1, t2);
pthread_setcancelstate(oldState, NULL);
int acceptfd;
free(config.regtype2);
#endif
-
#ifdef CONFIG_LIBDAEMON
if (this_is_the_daemon_process) {
daemon_retval_send(0);
pthread_create(&soxr_time_check_thread, NULL, &soxr_time_check, NULL);
#endif
-/*
- uint8_t ap_md5[16];
-
-#ifdef CONFIG_OPENSSL
- MD5_CTX ctx;
- MD5_Init(&ctx);
- MD5_Update(&ctx, config.service_name, strlen(config.service_name));
- MD5_Final(ap_md5, &ctx);
-#endif
-
-#ifdef CONFIG_MBEDTLS
-#if MBEDTLS_VERSION_MINOR >= 7
- mbedtls_md5_context tctx;
- mbedtls_md5_starts_ret(&tctx);
- mbedtls_md5_update_ret(&tctx, (unsigned char *)config.service_name, strlen(config.service_name));
- mbedtls_md5_finish_ret(&tctx, ap_md5);
-#else
- mbedtls_md5_context tctx;
- mbedtls_md5_starts(&tctx);
- mbedtls_md5_update(&tctx, (unsigned char *)config.service_name, strlen(config.service_name));
- mbedtls_md5_finish(&tctx, ap_md5);
-#endif
-#endif
-
-#ifdef CONFIG_POLARSSL
- md5_context tctx;
- md5_starts(&tctx);
- md5_update(&tctx, (unsigned char *)config.service_name, strlen(config.service_name));
- md5_finish(&tctx, ap_md5);
-#endif
-
- memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr));
-*/
+ /*
+ uint8_t ap_md5[16];
+
+ #ifdef CONFIG_OPENSSL
+ MD5_CTX ctx;
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, config.service_name, strlen(config.service_name));
+ MD5_Final(ap_md5, &ctx);
+ #endif
+
+ #ifdef CONFIG_MBEDTLS
+ #if MBEDTLS_VERSION_MINOR >= 7
+ mbedtls_md5_context tctx;
+ mbedtls_md5_starts_ret(&tctx);
+ mbedtls_md5_update_ret(&tctx, (unsigned char *)config.service_name,
+ strlen(config.service_name)); mbedtls_md5_finish_ret(&tctx, ap_md5); #else mbedtls_md5_context
+ tctx; mbedtls_md5_starts(&tctx); mbedtls_md5_update(&tctx, (unsigned char *)config.service_name,
+ strlen(config.service_name)); mbedtls_md5_finish(&tctx, ap_md5); #endif #endif
+
+ #ifdef CONFIG_POLARSSL
+ md5_context tctx;
+ md5_starts(&tctx);
+ md5_update(&tctx, (unsigned char *)config.service_name, strlen(config.service_name));
+ md5_finish(&tctx, ap_md5);
+ #endif
+
+ memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr));
+ */
#ifdef CONFIG_METADATA
metadata_init(); // create the metadata pipe if necessary