diff --git a/configure.ac b/configure.ac
index e0dcb10f..7f31e167 100644
--- a/configure.ac
+++ b/configure.ac
@@ -413,7 +413,9 @@ if test "x$with_airplay_2" = "xyes" ; then
# PKG_CHECK_MODULES([libgcrypt], [libgcrypt],[CFLAGS="${libgcrypt_CFLAGS} ${CFLAGS}" LIBS="${libgcrypt_LIBS} ${LIBS}"],[AC_MSG_ERROR(AirPlay 2 support requires libgcrypt -- libgcrypt-dev suggested)])
# AC_CHECK_LIB([sodium], [sodium_init], ,[AC_MSG_ERROR([AirPlay 2 support requires libsodium -- libsodium-dev suggested])])
AC_CHECK_LIB([avutil],[av_malloc], [], [AC_MSG_ERROR([Airplay 2 support requires libavutil -- libavutil-dev suggested])])
- AC_CHECK_LIB([avcodec],[avcodec_find_encoder], [], [AC_MSG_ERROR([AirPlay 2 support requires libavcodec -- libavcodec-dev suggested])])
+ PKG_CHECK_MODULES([libavcodec], [libavcodec],[CFLAGS="${libavcodec_CFLAGS} ${CFLAGS}" LIBS="${libavcodec_LIBS} ${LIBS}"],[
+ AC_MSG_ERROR(AirPlay 2 support requires libavcodec -- libavcodec-dev suggested)])
+# AC_CHECK_LIB([avcodec],[avcodec_find_encoder], [], [AC_MSG_ERROR([AirPlay 2 support requires libavcodec -- libavcodec-dev suggested])])
AC_CHECK_LIB([avformat],[avformat_new_stream], [], [AC_MSG_ERROR([AirPlay 2 support requires libavformat -- libavformat-dev suggested])])
AC_CHECK_LIB([swresample],[swr_convert], [], [AC_MSG_ERROR([AirPlay 2 support requires libswresample -- libswresample-dev suggested])])
AC_CHECK_LIB([uuid],[uuid_generate], [], [AC_MSG_ERROR([AirPlay 2 support requires the uuid library -- uuid-dev suggested])])
diff --git a/dbus-service.c b/dbus-service.c
index bbd5dcd4..05dd9726 100644
--- a/dbus-service.c
+++ b/dbus-service.c
@@ -423,6 +423,8 @@ gboolean notify_statistics_callback(ShairportSyncDiagnostics *skeleton,
// debug(1, "\"notify_statistics_callback\" called.");
if (shairport_sync_diagnostics_get_statistics(skeleton)) {
debug(1, ">> start logging statistics");
+ if (config.statistics_requested == 0)
+ statistics_row = 0; // redraw the header line
config.statistics_requested = 1;
} else {
debug(1, ">> stop logging statistics");
@@ -437,6 +439,8 @@ gboolean notify_verbosity_callback(ShairportSyncDiagnostics *skeleton,
if ((th >= 0) && (th <= 3)) {
if (th == 0)
debug(1, ">> log verbosity set to %d.", th);
+ if (((debuglev == 0) && (th != 0)) || ((debuglev != 0) && (th == 0)))
+ statistics_row = 0; // if the debug level changes, redraw the header line
debuglev = th;
debug(1, ">> log verbosity set to %d.", th);
} else {
diff --git a/player.c b/player.c
index 9b0b86ca..6e1ee80d 100644
--- a/player.c
+++ b/player.c
@@ -383,6 +383,10 @@ void reset_buffer(rtsp_conn_info *conn) {
debug_mutex_lock(&conn->ab_mutex, 30000, 0);
ab_resync(conn);
debug_mutex_unlock(&conn->ab_mutex, 0);
+ if (config.output->flush) {
+ config.output->flush(); // no cancellation points
+ // debug(1, "reset_buffer: flush output device.");
+ }
}
void get_audio_buffer_size_and_occupancy(unsigned int *size, unsigned int *occupancy,
@@ -1168,9 +1172,9 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) {
if (config.cmd_unfixable) {
command_execute(config.cmd_unfixable, "output_device_stalled", 1);
} else {
- warn("an unrecoverable error, \"output_device_stalled\", has been "
- "detected.",
- conn->connection_number);
+ die("an unrecoverable error, \"output_device_stalled\", has been "
+ "detected.",
+ conn->connection_number);
}
}
} else {
@@ -1554,25 +1558,19 @@ int *statistics_print_profile;
// these arrays specify which of the statistics specified by the statistics_item calls will actually
// be printed -- 2 means print, 1 means print only in a debug mode, 0 means skip
-// don't display output fps -- not sure how accurate it is (change item 14 and 17 to 1 to restore)
-int ap1_synced_statistics_print_profile[] = {2, 2, 2, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1};
-int ap1_nosync_statistics_print_profile[] = {2, 0, 0, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 0, 0, 1, 0};
-int ap1_nodelay_statistics_print_profile[] = {0, 0, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 1, 0, 0, 1, 0};
+// clang-format off
+int ap1_synced_statistics_print_profile[] = {2, 2, 2, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1};
+int ap1_nosync_statistics_print_profile[] = {2, 0, 0, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 0, 0, 1, 0};
+int ap1_nodelay_statistics_print_profile[] = {0, 0, 0, 1, 2, 1, 1, 2, 0, 1, 1, 1, 1, 0, 0, 1, 0};
-int ap2_realtime_synced_stream_statistics_print_profile[] = {2, 2, 2, 1, 2, 1, 1, 2, 1,
- 1, 1, 1, 1, 2, 2, 0, 0};
-int ap2_realtime_nosync_stream_statistics_print_profile[] = {2, 0, 0, 1, 2, 1, 1, 2, 1,
- 1, 1, 1, 1, 0, 0, 0, 0};
-int ap2_realtime_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 1, 2, 1, 1, 2, 0,
- 1, 1, 1, 1, 0, 0, 0, 0};
+int ap2_realtime_synced_stream_statistics_print_profile[] = {2, 2, 2, 1, 2, 1, 1, 2, 1, 1, 1, 0, 1, 2, 2, 0, 0};
+int ap2_realtime_nosync_stream_statistics_print_profile[] = {2, 0, 0, 1, 2, 1, 1, 2, 1, 1, 1, 0, 1, 0, 0, 0, 0};
+int ap2_realtime_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 1, 2, 1, 1, 2, 0, 1, 1, 0, 1, 0, 0, 0, 0};
-// don't display output fps -- not sure how accurate it is (change item 14 1 to restore)
-int ap2_buffered_synced_stream_statistics_print_profile[] = {2, 2, 2, 1, 0, 0, 0, 0, 1,
- 1, 1, 0, 0, 2, 2, 0, 0};
-int ap2_buffered_nosync_stream_statistics_print_profile[] = {2, 0, 0, 1, 0, 0, 0, 0, 1,
- 1, 1, 0, 0, 0, 0, 0, 0};
-int ap2_buffered_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 1, 0, 0, 0, 0, 0,
- 1, 1, 0, 0, 0, 0, 0, 0};
+int ap2_buffered_synced_stream_statistics_print_profile[] = {2, 2, 2, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 2, 2, 0, 0};
+int ap2_buffered_nosync_stream_statistics_print_profile[] = {2, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0};
+int ap2_buffered_nodelay_stream_statistics_print_profile[] = {0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0};
+// clang-format on
void statistics_item(const char *heading, const char *format, ...) {
if (((statistics_print_profile[statistics_column] == 1) && (debuglev != 0)) ||
@@ -1693,7 +1691,7 @@ void player_thread_cleanup_handler(void *arg) {
debug(3, "Audio thread terminated.");
#ifdef CONFIG_AIRPLAY_2
}
- ptp_send_control_message_string("T"); // remove all timing peers to force the master to 0
+ // ptp_send_control_message_string("T"); // remove all timing peers to force the master to 0
#endif
if (conn->outbuf) {
@@ -1717,7 +1715,7 @@ void player_thread_cleanup_handler(void *arg) {
if (conn->stream.type == ast_apple_lossless)
terminate_decoders(conn);
- reset_anchor_info(conn);
+ // reset_anchor_info(conn);
release_play_lock(conn);
conn->rtp_running = 0;
pthread_setcancelstate(oldState, NULL);
@@ -1753,7 +1751,7 @@ void *player_thread_func(void *arg) {
conn->ap2_play_enabled = 0;
#endif
- reset_anchor_info(conn);
+ // reset_anchor_info(conn);
if (conn->stream.type == ast_apple_lossless)
init_alac_decoder((int32_t *)&conn->stream.fmtp,
diff --git a/player.h b/player.h
index d37f00c7..edf80837 100644
--- a/player.h
+++ b/player.h
@@ -410,6 +410,7 @@ typedef struct {
} rtsp_conn_info;
extern pthread_mutex_t playing_conn_lock;
+extern int statistics_row; // will be reset to zero when debug level changes or statistics enabled
void reset_buffer(rtsp_conn_info *conn);
diff --git a/plists/get_info_response.xml b/plists/get_info_response.xml
index 7a3b7a05..b45e3bad 100644
--- a/plists/get_info_response.xml
+++ b/plists/get_info_response.xml
@@ -60,7 +60,8 @@
366.0
statusFlags
4
- txtAirPlay
-
+
+
diff --git a/ptp-utilities.c b/ptp-utilities.c
index 7019187e..fa1c6407 100644
--- a/ptp-utilities.c
+++ b/ptp-utilities.c
@@ -68,7 +68,7 @@ int get_nqptp_data(struct shm_structure *nqptp_data) {
int ptp_get_clock_info(uint64_t *actual_clock_id, uint64_t *time_of_sample, uint64_t *raw_offset,
uint64_t *mastership_start_time) {
int response = clock_ok;
- pthread_cleanup_debug_mutex_lock(&ptp_access_mutex, 20000, 1);
+ pthread_cleanup_debug_mutex_lock(&ptp_access_mutex, 50000, 1);
if (actual_clock_id != NULL)
*actual_clock_id = 0;
if (raw_offset != NULL)
diff --git a/rtp.c b/rtp.c
index 5b79ff4b..f80e32ae 100644
--- a/rtp.c
+++ b/rtp.c
@@ -1310,7 +1310,7 @@ int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
// if the master clock and the anchor clock are the same
// wait at least this time before using the new master clock
// note that mastershipo may be backdated
- if (duration_of_mastership < 1500000000) {
+ if (duration_of_mastership < 100000000) {
debug(3, "master not old enough yet: %f ms", 0.000001 * duration_of_mastership);
response = clock_not_ready;
} else if ((duration_of_mastership > 5000000000) ||
@@ -1400,6 +1400,7 @@ int get_ptp_anchor_local_time_info(rtsp_conn_info *conn, uint32_t *anchorRTP,
}
}
} else {
+ // debug(1, "anchor_remote_info_is_valid not valid");
response = clock_no_anchor_info; // no anchor information
}
}
@@ -2253,8 +2254,15 @@ void *rtp_buffered_audio_processor(void *arg) {
int streaming_has_started = 0;
int play_enabled = 0;
uint32_t flush_from_timestamp;
- double requested_lead_time = 0.3; // normal lead time minimum
- reset_buffer(conn); // in case there is any garbage in the player
+ double requested_lead_time = 0.0; // normal lead time minimum -- maybe it should be about 0.1
+
+ // wait until our timing information is valid
+
+ // debug(1,"rtp_buffered_audio_processor ready.");
+ while (have_ptp_timing_information(conn) == 0)
+ usleep(1000);
+
+ reset_buffer(conn); // in case there is any garbage in the player
// int not_first_time_out = 0;
// quick check of parameters
@@ -2519,43 +2527,60 @@ void *rtp_buffered_audio_processor(void *arg) {
// to decipher it first
// debug(1,"seq_no %u, timestamp %u", seq_no, timestamp);
+ uint64_t local_should_be_time = 0;
+ int have_time_information = frame_to_local_time(timestamp, &local_should_be_time, conn);
+ int64_t local_lead_time = 0;
+ int64_t requested_lead_time_ns = (int64_t)(requested_lead_time * 1000000000);
+ requested_lead_time_ns = (int64_t)(-300000000);
+ int outdated = 0;
+ if (have_time_information == 0) {
+ local_lead_time = local_should_be_time - get_absolute_time_in_ns();
+ outdated = (local_lead_time < requested_lead_time_ns);
+ // if (outdated != 0)
+ // debug(1,"Frame is outdated %d if lead_time %" PRId64 " is less than requested lead time
+ // %" PRId64 " ns.", outdated, local_lead_time, requested_lead_time_ns);
+ } else {
+ debug(1, "Timing information not valid");
+ // outdated = 1;
+ }
+
if ((flush_requested) && (seq_no >= flushUntilSeq)) {
- uint64_t should_be_time;
- if ((frame_to_local_time(timestamp, &should_be_time, conn) == 0) && (play_enabled)) {
+ if ((have_time_information == 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,
+ debug(2,
"flush completed to seq: %u, flushUntilTS; %u with rtptime: %u, lead time: "
"0x%" PRIx64 " nanoseconds, i.e. %f sec.",
- seq_no, flushUntilTS, timestamp, lead_time, lead_time * 0.000000001);
+ seq_no, flushUntilTS, timestamp, local_lead_time, local_lead_time * 0.000000001);
} else {
debug(2, "flush completed to seq: %u with rtptime: %u.", seq_no, timestamp);
}
}
+ // 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
+ // so we better indicate that the pcm buffer is empty and its contents invalid
+
+ // also, if the incomgin frame is outdated, set pcm_buffer_occupancy to 0;
+ if ((flush_requested) || (outdated)) {
+ pcm_buffer_occupancy = 0;
+ }
+
+ // decode the block and add it to or put it in the pcm buffer
+
+ 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)) {
+ // 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;
+ //}
+ }
+
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
- // so we better indicate that the pcm buffer is empty and its contents invalid
- if (flush_requested) {
- pcm_buffer_occupancy = 0;
- }
-
- // decode the block and add it to or put it in the pcm buffer
-
- 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)) {
- // 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;
- //}
- }
-
unsigned char nonce[12];
memset(nonce, 0, sizeof(nonce));
memcpy(nonce + 4, packet + nread - 8,
@@ -2596,6 +2621,7 @@ void *rtp_buffered_audio_processor(void *arg) {
data_remaining = aac_packet_length;
int ret = 0;
// there can be more than one av packet (? terminology) in a block
+ int frame_within_block = 0;
while (data_remaining > 0) {
if (decoded_frame == NULL) {
decoded_frame = av_frame_alloc();
@@ -2608,66 +2634,77 @@ void *rtp_buffered_audio_processor(void *arg) {
if (ret < 0) {
debug(1, "error while parsing deciphered audio packet.");
} else {
+ frame_within_block++;
data_to_process += ret;
data_remaining -= ret;
// debug(1, "frame found");
// now pass each packet to be decoded
if (pkt->size) {
- ret = avcodec_send_packet(codec_context, pkt);
- if (ret < 0) {
- debug(1, "error sending packet to decoder");
+ // if (0) {
+ if (pkt->size <= 7) { // no idea about this...
+ debug(1, "malformed AAC packet skipped.");
} else {
- while (ret >= 0) {
- ret = avcodec_receive_frame(codec_context, decoded_frame);
- if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
- break;
- else if (ret < 0) {
- debug(1, "error %d during decoding", ret);
- } else {
- av_samples_alloc(&pcm_audio, &dst_linesize, codec_context->channels,
- decoded_frame->nb_samples, av_format, 1);
- // remember to free pcm_audio
- ret = swr_convert(swr, &pcm_audio, decoded_frame->nb_samples,
- (const uint8_t **)decoded_frame->extended_data,
- decoded_frame->nb_samples);
- dst_bufsize = av_samples_get_buffer_size(
- &dst_linesize, codec_context->channels, ret, av_format, 1);
- // debug(1,"generated %d bytes of PCM", dst_bufsize);
- // copy the PCM audio into the PCM buffer.
- // make sure it's big enough first
+ ret = avcodec_send_packet(codec_context, pkt);
- // 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 * conn->input_bytes_per_frame) < 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 / conn->input_bytes_per_frame,
- flush_from_timestamp, pcm_buffer_read_point_rtptime);
- dst_bufsize = samples_remaining * conn->input_bytes_per_frame;
- }
- }
- if ((pcm_buffer_size - pcm_buffer_occupancy) < dst_bufsize) {
- debug(1,
- "pcm_buffer_read_point (frames): %u, pcm_buffer_occupancy "
- "(frames): %u",
- pcm_buffer_read_point / conn->input_bytes_per_frame,
- pcm_buffer_occupancy / conn->input_bytes_per_frame);
- pcm_buffer_size = dst_bufsize + pcm_buffer_occupancy;
- debug(1, "fatal error! pcm buffer too small at %d bytes.",
- pcm_buffer_size);
+ if (ret < 0) {
+ debug(1,
+ "error sending frame %d of size %d to decoder, blocks_read: %u, "
+ "blocks_read_since_flush: %u.",
+ frame_within_block, pkt->size, blocks_read, blocks_read_since_flush);
+ } else {
+ while (ret >= 0) {
+ ret = avcodec_receive_frame(codec_context, decoded_frame);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+ break;
+ else if (ret < 0) {
+ debug(1, "error %d during decoding", ret);
} else {
- memcpy(pcm_buffer + pcm_buffer_occupancy, pcm_audio, dst_bufsize);
- pcm_buffer_occupancy += dst_bufsize;
+ av_samples_alloc(&pcm_audio, &dst_linesize, codec_context->channels,
+ decoded_frame->nb_samples, av_format, 1);
+ // remember to free pcm_audio
+ ret = swr_convert(swr, &pcm_audio, decoded_frame->nb_samples,
+ (const uint8_t **)decoded_frame->extended_data,
+ decoded_frame->nb_samples);
+ dst_bufsize = av_samples_get_buffer_size(
+ &dst_linesize, codec_context->channels, ret, av_format, 1);
+ // debug(1,"generated %d bytes of PCM", dst_bufsize);
+ // 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
+ 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 * conn->input_bytes_per_frame) <
+ 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 / conn->input_bytes_per_frame,
+ flush_from_timestamp, pcm_buffer_read_point_rtptime);
+ dst_bufsize = samples_remaining * conn->input_bytes_per_frame;
+ }
+ }
+ if ((pcm_buffer_size - pcm_buffer_occupancy) < dst_bufsize) {
+ debug(1,
+ "pcm_buffer_read_point (frames): %u, pcm_buffer_occupancy "
+ "(frames): %u",
+ pcm_buffer_read_point / conn->input_bytes_per_frame,
+ pcm_buffer_occupancy / conn->input_bytes_per_frame);
+ pcm_buffer_size = dst_bufsize + pcm_buffer_occupancy;
+ debug(1, "fatal error! pcm buffer too small at %d bytes.",
+ pcm_buffer_size);
+ } else {
+ memcpy(pcm_buffer + pcm_buffer_occupancy, pcm_audio, dst_bufsize);
+ pcm_buffer_occupancy += dst_bufsize;
+ }
+ // debug(1,"decoded %d samples", decoded_frame->nb_samples);
+ // memcpy(sampleBuffer,outputBuffer16,dst_bufsize);
+ av_freep(&pcm_audio);
}
- // debug(1,"decoded %d samples", decoded_frame->nb_samples);
- // memcpy(sampleBuffer,outputBuffer16,dst_bufsize);
- av_freep(&pcm_audio);
}
}
}
diff --git a/rtsp.c b/rtsp.c
index 079ad14d..177a4b81 100644
--- a/rtsp.c
+++ b/rtsp.c
@@ -185,6 +185,39 @@ typedef struct {
#ifdef CONFIG_AIRPLAY_2
+int add_pstring_to_malloc(const char *s, void **allocation, size_t *size) {
+ int response = 0;
+ void *p = *allocation;
+ if (p == NULL) {
+ p = malloc(strlen(s) + 1);
+ if (p == NULL) {
+ debug(1, "error allocating memory");
+ } else {
+ *allocation = p;
+ *size = *size + strlen(s) + 1;
+ uint8_t *b = (uint8_t *)p;
+ *b = strlen(s);
+ p = p + 1;
+ memcpy(p, s, strlen(s));
+ response = 1;
+ }
+ } else {
+ p = realloc(p, *size + strlen(s) + 1);
+ if (p == NULL) { // assuming we never allocate a zero byte space
+ debug(1, "error reallocating memory");
+ } else {
+ *allocation = p;
+ uint8_t *b = (uint8_t *)p + *size;
+ *b = strlen(s);
+ p = p + *size + 1;
+ memcpy(p, s, strlen(s));
+ *size = *size + strlen(s) + 1;
+ response = 1;
+ }
+ }
+ return response;
+}
+
static void pkString_make(char *str, size_t str_size, const char *device_id) {
uint8_t public_key[32];
if (str_size < 2 * sizeof(public_key) + 1) {
@@ -611,8 +644,8 @@ void *player_watchdog_thread_code(void *arg) {
conn->unfixable_error_reported = 1;
command_execute(config.cmd_unfixable, "unable_to_cancel_play_session", 1);
} else {
- warn("an unrecoverable error, \"unable_to_cancel_play_session\", has been detected.",
- conn->connection_number);
+ die("an unrecoverable error, \"unable_to_cancel_play_session\", has been detected.",
+ conn->connection_number);
}
}
}
@@ -1139,7 +1172,9 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
enum rtsp_read_request_response reply = rtsp_read_request_response_ok;
ssize_t buflen = 4096;
- int release_buffer = 0; // on exit, don't deallocate the buffer if everything was okay
+ if ((config.metadata_enabled != 0) && (config.get_coverart != 0))
+ buflen = 1024 * 256; // big enough for typical picture data, which will be base64 encoded
+ int release_buffer = 0; // on exit, don't deallocate the buffer if everything was okay
char *buf = malloc(buflen + 1); // add a NUL at the end
if (!buf) {
warn("Connection %d: rtsp_read_request: can't get a buffer.", conn->connection_number);
@@ -1228,7 +1263,7 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
get_absolute_time_in_ns() + ((uint64_t)15000000000); // i.e. fifteen seconds from now
int warning_message_sent = 0;
- const size_t max_read_chunk = 1024 * 1024 / 16;
+ // const size_t max_read_chunk = 1024 * 1024 / 16;
while (inbuf < msg_size) {
// we are going to read the stream in chunks and time how long it takes to
@@ -1255,9 +1290,9 @@ enum rtsp_read_request_response rtsp_read_request(rtsp_conn_info *conn, rtsp_mes
goto shutdown;
}
size_t read_chunk = msg_size - inbuf;
- if (read_chunk > max_read_chunk)
- read_chunk = max_read_chunk;
- usleep(80000); // wait about 80 milliseconds between reads of up to max_read_chunk
+ // if (read_chunk > max_read_chunk)
+ // read_chunk = max_read_chunk;
+ // usleep(80000); // wait about 80 milliseconds between reads of up to max_read_chunk
nread = read_from_rtsp_connection(conn, buf + inbuf, read_chunk);
if (!nread) {
reply = rtsp_read_request_response_error;
@@ -1476,7 +1511,7 @@ void handle_record(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *req,
rtsp_message *resp) {
-
+ debug_log_rtsp_message(2, "GET /info:", req);
if (rtsp_message_contains_plist(req)) { // it's stage one
// get version of AirPlay -- it might be too old. Not using it yet.
char *hdr = msg_get_header(req, "User-Agent");
@@ -1519,6 +1554,58 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
if (response_plist == NULL) {
debug(1, "GET /info Stage 1: response plist not created from XML!");
} else {
+ void *qualifier_response_data = NULL;
+ size_t qualifier_response_data_length = 0;
+ if (add_pstring_to_malloc("acl=0", &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc(deviceIdString, &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc(featuresString, &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc("rsf=0x0", &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc("flags=0x4", &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc("model=Shairport Sync", &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc("manufacturer=", &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc("serialNumber=", &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc("protovers=1.1", &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc("srcvers=366.0", &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc(piString, &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc(gidString, &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ if (add_pstring_to_malloc("gcgl=0", &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ snprintf(pkString, sizeof(pkString), "pk=");
+ pkString_make(pkString + strlen("pk="), sizeof(pkString) - strlen("pk="),
+ config.airplay_device_id);
+ if (add_pstring_to_malloc(pkString, &qualifier_response_data,
+ &qualifier_response_data_length) == 0)
+ debug(1, "Problem");
+ // debug(1,"qualifier_response_data_length: %u.", qualifier_response_data_length);
+
+ plist_dict_set_item(response_plist, "txtAirPlay",
+ plist_new_data(qualifier_response_data, qualifier_response_data_length));
+
plist_dict_set_item(response_plist, "features", plist_new_uint(config.airplay_features));
plist_dict_set_item(response_plist, "statusFlags",
plist_new_uint(config.airplay_statusflags));
@@ -1526,15 +1613,16 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
plist_dict_set_item(response_plist, "pi", plist_new_string(config.airplay_pi));
plist_dict_set_item(response_plist, "name", plist_new_string(config.service_name));
char *vs = get_version_string();
- plist_dict_set_item(response_plist, "model", plist_new_string(vs));
+ // plist_dict_set_item(response_plist, "model", plist_new_string(vs));
+ plist_dict_set_item(response_plist, "model", plist_new_string("Shairport Sync"));
free(vs);
- char pkString[128];
- pkString_make(pkString, sizeof(pkString), config.airplay_device_id);
- plist_dict_set_item(response_plist, "pk", plist_new_string(pkString));
+ // pkString_make(pkString, sizeof(pkString), config.airplay_device_id);
+ // plist_dict_set_item(response_plist, "pk", plist_new_string(pkString));
plist_to_bin(response_plist, &resp->content, &resp->contentlength);
if (resp->contentlength == 0)
debug(1, "GET /info Stage 1: response bplist not created!");
plist_free(response_plist);
+ free(qualifier_response_data);
}
msg_add_header(resp, "Content-Type", "application/x-apple-binary-plist");
debug_log_rtsp_message(2, "GET /info Stage 1 Response:", resp);
@@ -1554,11 +1642,11 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r
plist_dict_set_item(response_plist, "pi", plist_new_string(config.airplay_pi));
plist_dict_set_item(response_plist, "name", plist_new_string(config.service_name));
char *vs = get_version_string();
- plist_dict_set_item(response_plist, "model", plist_new_string(vs));
+ // plist_dict_set_item(response_plist, "model", plist_new_string(vs));
+ plist_dict_set_item(response_plist, "model", plist_new_string("Shairport Sync"));
free(vs);
- char pkString[128];
- pkString_make(pkString, sizeof(pkString), config.airplay_device_id);
- plist_dict_set_item(response_plist, "pk", plist_new_string(pkString));
+ // pkString_make(pkString, sizeof(pkString), config.airplay_device_id);
+ // plist_dict_set_item(response_plist, "pk", plist_new_string(pkString));
plist_to_bin(response_plist, &resp->content, &resp->contentlength);
plist_free(response_plist);
msg_add_header(resp, "Content-Type", "application/x-apple-binary-plist");
@@ -1702,12 +1790,12 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
}
uint64_t networkTimeSecs;
plist_get_uint_val(item, &networkTimeSecs);
- debug(2, "anchor networkTimeSecs is 0x%" PRIx64 ".", networkTimeSecs);
+ debug(2, "anchor networkTimeSecs is %" PRIu64 ".", networkTimeSecs);
item = plist_dict_get_item(messagePlist, "networkTimeFrac");
uint64_t networkTimeFrac;
plist_get_uint_val(item, &networkTimeFrac);
- debug(2, "anchor networkTimeFrac is 0x%" PRIx64 ".", networkTimeFrac);
+ debug(2, "anchor networkTimeFrac is 0%" PRIu64 ".", networkTimeFrac);
// it looks like the networkTimeFrac is a fraction where the msb is work 1/2, the
// next 1/4 and so on
// now, convert the network time and fraction into nanoseconds
@@ -1718,7 +1806,7 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
networkTimeSecs = networkTimeSecs * 1000000000; // turn the whole seconds into ns
uint64_t anchorTimeNanoseconds = networkTimeSecs + networkTimeFrac;
- debug(2, "anchorTimeNanoseconds looks like 0x%" PRIx64 ".", anchorTimeNanoseconds);
+ debug(2, "anchorTimeNanoseconds looks like %" PRIu64 ".", anchorTimeNanoseconds);
item = plist_dict_get_item(messagePlist, "rtpTime");
uint64_t rtpTime;
@@ -1756,7 +1844,7 @@ void handle_setrateanchori(rtsp_conn_info *conn, rtsp_message *req, rtsp_message
if ((rate & 1) != 0) {
// keep this outside the flush_mutex lock
if (conn->ap2_timing_peer_list_message) {
- ptp_send_control_message_string(conn->ap2_timing_peer_list_message);
+ // ptp_send_control_message_string(conn->ap2_timing_peer_list_message);
} else {
debug(1, "Connection %d: No timing peer list!", conn->connection_number);
}
@@ -2158,7 +2246,27 @@ void handle_configure(rtsp_conn_info *conn __attribute__((unused)),
void handle_feedback(__attribute__((unused)) rtsp_conn_info *conn,
__attribute__((unused)) rtsp_message *req,
- __attribute__((unused)) rtsp_message *resp) {}
+ __attribute__((unused)) rtsp_message *resp) {
+ /* not finished yet
+ plist_t payload_plist = plist_new_dict();
+ plist_dict_set_item(payload_plist, "type", plist_new_uint(103));
+ plist_dict_set_item(payload_plist, "sr", plist_new_real(44100.0));
+
+ plist_t array_plist = plist_new_array();
+ plist_array_append_item(array_plist, payload_plist);
+
+ plist_t response_plist = plist_new_dict();
+ plist_dict_set_item(response_plist, "streams",array_plist);
+
+ plist_to_bin(response_plist, &resp->content, &resp->contentlength);
+ plist_free(response_plist);
+ // plist_free(array_plist);
+ // plist_free(payload_plist);
+
+ msg_add_header(resp, "Content-Type", "application/x-apple-binary-plist");
+ debug_log_rtsp_message(2, "FEEDBACK response:", resp);
+ */
+}
void handle_command(__attribute__((unused)) rtsp_conn_info *conn, rtsp_message *req,
__attribute__((unused)) rtsp_message *resp) {
@@ -2290,10 +2398,10 @@ void handle_setpeers(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp
free(conn->ap2_timing_peer_list_message);
conn->ap2_timing_peer_list_message = strdup(timing_list_message);
// if this rtsp thread is playing...
- if (try_to_hold_play_lock(conn) == 0) {
- ptp_send_control_message_string(conn->ap2_timing_peer_list_message);
- release_hold_on_play_lock(conn);
- }
+ // if (try_to_hold_play_lock(conn) == 0) {
+ ptp_send_control_message_string(conn->ap2_timing_peer_list_message);
+ // release_hold_on_play_lock(conn);
+ // }
}
plist_free(addresses_array);
resp->respcode = 200;
@@ -2876,10 +2984,11 @@ void handle_setup_2(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp)
conn->input_bit_depth = conn->stream.fmtp[3];
conn->input_bytes_per_frame = conn->input_num_channels * ((conn->input_bit_depth + 7) / 8);
debug(2, "Realtime Stream Play");
- if (conn->ap2_timing_peer_list_message)
- ptp_send_control_message_string(conn->ap2_timing_peer_list_message);
- else
+ if (conn->ap2_timing_peer_list_message) {
+ // ptp_send_control_message_string(conn->ap2_timing_peer_list_message);
+ } else {
debug(1, "No timing peer list!");
+ }
activity_monitor_signify_activity(1);
player_prepare_to_play(conn);
player_play(conn);
diff --git a/shairport.c b/shairport.c
index 9b7648e3..b1faf5c9 100644
--- a/shairport.c
+++ b/shairport.c
@@ -1807,11 +1807,12 @@ int main(int argc, char **argv) {
#ifdef CONFIG_METADATA
// If we are asking for metadata, turn on the relevant bits
- if (config.metadata_enabled != 0)
+ if (config.metadata_enabled != 0) {
config.airplay_features |= (1 << 17) | (1 << 16); // 16 is progress, 17 is text
- // If we are asking for artwork, turn on the relevant bit
- if (config.get_coverart)
- config.airplay_features |= (1 << 15); // 15 is artwork
+ // If we are asking for artwork, turn on the relevant bit
+ if (config.get_coverart)
+ config.airplay_features |= (1 << 15); // 15 is artwork
+ }
#endif
debug(1, "Features: 0x%" PRIx64 ".", config.airplay_features);