Flush seems to be working properly and reliably. Some audio glitches removed too by dropping the first two AAC blocks of any passage. Crude. Flush diagnostic linked list stuff left in place.

This commit is contained in:
Mike Brady
2021-05-30 11:17:06 +01:00
parent 2ea28c0661
commit a109b587ec
12 changed files with 363 additions and 264 deletions
+6 -8
View File
@@ -93,8 +93,6 @@
#include <syslog.h>
#endif
#ifdef CONFIG_ALSA
void set_alsa_out_dev(char *);
#endif
@@ -1611,13 +1609,13 @@ char *get_version_string() {
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");
@@ -1945,7 +1943,7 @@ int get_device_id(uint8_t *id, int int_length) {
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;
}
+4 -5
View File
@@ -210,10 +210,10 @@ typedef struct {
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
@@ -498,7 +498,6 @@ int get_device_id(uint8_t *id, int int_length);
extern char git_version_string[];
#endif
#endif // _COMMON_H
#ifdef __cplusplus
+4 -2
View File
@@ -81,7 +81,8 @@ void mdns_register(char **txt_records, char **secondary_txt_records) {
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;
}
@@ -93,7 +94,8 @@ void mdns_register(char **txt_records, char **secondary_txt_records) {
} 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;
+2 -1
View File
@@ -17,7 +17,8 @@ void mdns_ls_backends(void);
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 *);
+16 -15
View File
@@ -243,24 +243,24 @@ static void register_service(AvahiClient *c) {
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");
}
}
}
@@ -318,7 +318,8 @@ static void client_callback(AvahiClient *c, AvahiClientState state,
}
}
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)
@@ -327,7 +328,8 @@ static int avahi_register(char *ap1name, char *ap2name, int srvport, char **txt_
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;
@@ -390,7 +392,6 @@ static void avahi_unregister(void) {
} 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);
+71 -64
View File
@@ -925,80 +925,83 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
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
@@ -1075,7 +1078,7 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
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;
@@ -1980,7 +1983,7 @@ void *player_thread_func(void *arg) {
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);
@@ -2243,7 +2246,7 @@ void *player_thread_func(void *arg) {
// 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,
@@ -2309,8 +2312,11 @@ void *player_thread_func(void *arg) {
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);
@@ -2318,7 +2324,8 @@ void *player_thread_func(void *arg) {
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;
+12 -10
View File
@@ -122,16 +122,17 @@ typedef struct {
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 {
@@ -294,6 +295,7 @@ 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;
+2 -1
View File
@@ -60,7 +60,8 @@ int get_nqptp_data(struct shm_structure *nqptp_data) {
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)
+2 -1
View File
@@ -29,7 +29,8 @@
#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);
+120 -102
View File
@@ -290,8 +290,8 @@ void *rtp_control_receiver(void *arg) {
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
@@ -302,19 +302,19 @@ void *rtp_control_receiver(void *arg) {
// 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 "
@@ -1213,7 +1213,8 @@ void set_ptp_anchor_info(rtsp_conn_info *conn, uint64_t clock_id, uint32_t rtpti
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;
@@ -1247,7 +1248,8 @@ int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
// 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;
@@ -1255,7 +1257,8 @@ int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
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 {
@@ -1266,13 +1269,17 @@ int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
// 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
@@ -1280,7 +1287,7 @@ int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
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 {
@@ -1296,7 +1303,8 @@ int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
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);
@@ -1310,7 +1318,7 @@ int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
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);
@@ -1922,6 +1930,37 @@ void rtp_buffered_audio_cleanup_handler(__attribute__((unused)) void *arg) {
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);
@@ -2046,7 +2085,7 @@ void *rtp_buffered_audio_processor(void *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;
@@ -2054,8 +2093,8 @@ void *rtp_buffered_audio_processor(void *arg) {
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;
@@ -2068,12 +2107,11 @@ void *rtp_buffered_audio_processor(void *arg) {
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) {
@@ -2086,29 +2124,29 @@ void *rtp_buffered_audio_processor(void *arg) {
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);
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;
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;
@@ -2123,10 +2161,9 @@ void *rtp_buffered_audio_processor(void *arg) {
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;
}
@@ -2137,14 +2174,13 @@ void *rtp_buffered_audio_processor(void *arg) {
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;
@@ -2182,22 +2218,26 @@ void *rtp_buffered_audio_processor(void *arg) {
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;
@@ -2273,38 +2313,6 @@ void *rtp_buffered_audio_processor(void *arg) {
// 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
@@ -2313,18 +2321,21 @@ void *rtp_buffered_audio_processor(void *arg) {
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
@@ -2337,11 +2348,11 @@ void *rtp_buffered_audio_processor(void *arg) {
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;
//}
}
@@ -2425,12 +2436,19 @@ void *rtp_buffered_audio_processor(void *arg) {
// 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;
}
}
+100 -25
View File
@@ -513,6 +513,49 @@ void cleanup_threads(void) {
}
}
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) {
@@ -1324,7 +1367,7 @@ void handle_flushbuffered(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
} 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);
}
@@ -1345,23 +1388,52 @@ void handle_flushbuffered(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
}
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;
}
@@ -2316,7 +2388,8 @@ void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *req,
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);
@@ -2337,7 +2410,8 @@ void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *req,
} 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;
}
@@ -3058,7 +3132,8 @@ 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(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;
@@ -3153,11 +3228,13 @@ static void handle_set_parameter(rtsp_conn_info *conn, rtsp_message *req, rtsp_m
// 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;
}
@@ -3449,7 +3526,7 @@ static void apple_challenge(int fd, rtsp_message *req, rtsp_message *resp) {
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);
@@ -4119,8 +4196,8 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
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;
@@ -4130,14 +4207,14 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
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];
@@ -4152,7 +4229,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
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";
@@ -4198,7 +4275,6 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
*p++ = NULL;
#endif
#ifdef CONFIG_AIRPLAY_2
// make up a secondary set of text records
char *secondary_txt_records[64];
@@ -4233,8 +4309,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) {
*p++ = NULL;
#endif
mdns_register(t1,t2);
mdns_register(t1, t2);
pthread_setcancelstate(oldState, NULL);
int acceptfd;
+24 -30
View File
@@ -1421,7 +1421,6 @@ void exit_function() {
free(config.regtype2);
#endif
#ifdef CONFIG_LIBDAEMON
if (this_is_the_daemon_process) {
daemon_retval_send(0);
@@ -2038,39 +2037,34 @@ int main(int argc, char **argv) {
pthread_create(&soxr_time_check_thread, NULL, &soxr_time_check, NULL);
#endif
/*
uint8_t ap_md5[16];
/*
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_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_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
#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));
*/
memcpy(config.hw_addr, ap_md5, sizeof(config.hw_addr));
*/
#ifdef CONFIG_METADATA
metadata_init(); // create the metadata pipe if necessary