From e3faba141c24bdbec191c46010c7d394453a9969 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Sun, 28 Nov 2021 21:51:26 +0000 Subject: [PATCH 01/13] log statsistics headers whenever verbosity goes on or off or whenever statistics are requested --- dbus-service.c | 4 ++++ player.h | 1 + 2 files changed, 5 insertions(+) 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.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); From 7b8e293f8b4c8ea3a7d7aeb148ffed6b10d44288 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 1 Dec 2021 09:01:31 +0000 Subject: [PATCH 02/13] Change behaviour after an unrecoverable error -- if not handled, exit Shairport Sync. --- player.c | 2 +- rtsp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/player.c b/player.c index 9b0b86ca..e9fa556a 100644 --- a/player.c +++ b/player.c @@ -1168,7 +1168,7 @@ 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 " + die("an unrecoverable error, \"output_device_stalled\", has been " "detected.", conn->connection_number); } diff --git a/rtsp.c b/rtsp.c index 079ad14d..ae650a6a 100644 --- a/rtsp.c +++ b/rtsp.c @@ -611,7 +611,7 @@ 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.", + die("an unrecoverable error, \"unable_to_cancel_play_session\", has been detected.", conn->connection_number); } } From b3526c54726c627b9e083ed0eed9b770bca3f829 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Wed, 1 Dec 2021 17:58:22 +0000 Subject: [PATCH 03/13] Use PKG_CHECK_MODULES to find libavcodec, to make it work with Fedora 35 (?) --- configure.ac | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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])]) From 60f830bc7541e374dc87d72cc956452c924a7a75 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 2 Dec 2021 11:16:00 +0000 Subject: [PATCH 04/13] Tidy up the statistics printout table and stop outputting (non existent) nominal fps for AP2 Realtime Streams. --- player.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/player.c b/player.c index e9fa556a..257d2ee8 100644 --- a/player.c +++ b/player.c @@ -1554,25 +1554,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)) || From d32544d9047e3c3286819c93a56acd9a387c67bd Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 2 Dec 2021 11:17:59 +0000 Subject: [PATCH 05/13] Set the timing peer list as soon as a SETPEERS message is received -- don't wait until play is about to begin. Not sure if it works universally yet. --- rtsp.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/rtsp.c b/rtsp.c index ae650a6a..ba03359f 100644 --- a/rtsp.c +++ b/rtsp.c @@ -1756,7 +1756,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); } @@ -2290,10 +2290,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) { + // 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); - } + // release_hold_on_play_lock(conn); + // } } plist_free(addresses_array); resp->respcode = 200; @@ -2876,10 +2876,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); From 231d5242d782483a44176b8e1efddb9ca4b2be0c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 2 Dec 2021 11:19:52 +0000 Subject: [PATCH 06/13] clang format only --- player.c | 4 ++-- rtsp.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/player.c b/player.c index 257d2ee8..36c7aabf 100644 --- a/player.c +++ b/player.c @@ -1169,8 +1169,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { command_execute(config.cmd_unfixable, "output_device_stalled", 1); } else { die("an unrecoverable error, \"output_device_stalled\", has been " - "detected.", - conn->connection_number); + "detected.", + conn->connection_number); } } } else { diff --git a/rtsp.c b/rtsp.c index ba03359f..45efd8cf 100644 --- a/rtsp.c +++ b/rtsp.c @@ -612,7 +612,7 @@ void *player_watchdog_thread_code(void *arg) { command_execute(config.cmd_unfixable, "unable_to_cancel_play_session", 1); } else { die("an unrecoverable error, \"unable_to_cancel_play_session\", has been detected.", - conn->connection_number); + conn->connection_number); } } } @@ -2291,8 +2291,8 @@ void handle_setpeers(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp 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); + ptp_send_control_message_string(conn->ap2_timing_peer_list_message); + // release_hold_on_play_lock(conn); // } } plist_free(addresses_array); From 41f55ac1cb4b371ac8dec4155ffabcccc6facd51 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Dec 2021 18:03:42 +0000 Subject: [PATCH 07/13] Stop clearing the timing peer list and stop removing the anchor clock info. --- player.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/player.c b/player.c index 36c7aabf..da98c55b 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, @@ -1687,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) { @@ -1711,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); @@ -1747,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, From fa20d88c1c2b46391a6de88f187fd8c816bd7806 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Dec 2021 18:08:35 +0000 Subject: [PATCH 08/13] Remember to add some exta fields later... --- plists/get_info_response.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 - + + From be4807fff205b7022d7fed2bc27b29ce1f1d38fa Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Dec 2021 18:09:29 +0000 Subject: [PATCH 09/13] Wait for that mutex for longer. (Don't know why yet...) --- ptp-utilities.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 0b21373c0e5ba8e565bf420ed2ad8eb79f23d491 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Dec 2021 18:13:20 +0000 Subject: [PATCH 10/13] Skip some invalid AAC packets, don't decode outdated frames, wait for timing information to be valid. --- rtp.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/rtp.c b/rtp.c index 5b79ff4b..d3bf06c4 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,7 +2254,14 @@ 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 + 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; @@ -2519,28 +2527,42 @@ 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 (((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) { + + // also, if the incomgin frame is outdated, set pcm_buffer_occupancy to 0; + if ((flush_requested) || (outdated)){ pcm_buffer_occupancy = 0; } @@ -2556,6 +2578,11 @@ void *rtp_buffered_audio_processor(void *arg) { //} } + + if (((flush_requested != 0) && (seq_no == flushUntilSeq)) || + ((flush_requested == 0) && (new_buffer_needed))) { + + unsigned char nonce[12]; memset(nonce, 0, sizeof(nonce)); memcpy(nonce + 4, packet + nread - 8, @@ -2596,6 +2623,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,14 +2636,20 @@ 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) { + //if (0) { + if (pkt->size <= 7) { // no idea about this... + debug(1,"malformed AAC packet skipped."); + } else { ret = avcodec_send_packet(codec_context, pkt); + if (ret < 0) { - debug(1, "error sending packet to decoder"); + 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); @@ -2672,6 +2706,7 @@ void *rtp_buffered_audio_processor(void *arg) { } } } + } } if (decoded_frame == NULL) debug(1, "decoded_frame is NULL"); From 3b9f2e1c53ede54dcbfba551bc534d2efd43c557 Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Dec 2021 18:14:23 +0000 Subject: [PATCH 11/13] Don't ask for cover art if not asking for metadata too. Duh. --- shairport.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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); From 1e2136821337dd73f4ac563e0ff6f3ff5790a92c Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Dec 2021 18:16:57 +0000 Subject: [PATCH 12/13] Add txtAirPlay data to info response. Don't pause long RTSP message reception. --- rtsp.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 17 deletions(-) diff --git a/rtsp.c b/rtsp.c index 45efd8cf..6c0da18f 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) { @@ -1139,6 +1172,8 @@ 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; + 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) { @@ -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,43 @@ 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 +1598,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 +1627,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 +1775,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 +1791,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; @@ -2158,7 +2231,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) { From 70821f81278abbf204ec9353b2808f8cad8bffec Mon Sep 17 00:00:00 2001 From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Mon, 6 Dec 2021 18:45:25 +0000 Subject: [PATCH 13/13] Clang Format --- player.c | 4 +- rtp.c | 170 ++++++++++++++++++++++++++++--------------------------- rtsp.c | 75 ++++++++++++++---------- 3 files changed, 133 insertions(+), 116 deletions(-) diff --git a/player.c b/player.c index da98c55b..6e1ee80d 100644 --- a/player.c +++ b/player.c @@ -384,8 +384,8 @@ void reset_buffer(rtsp_conn_info *conn) { 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."); + config.output->flush(); // no cancellation points + // debug(1, "reset_buffer: flush output device."); } } diff --git a/rtp.c b/rtp.c index d3bf06c4..f80e32ae 100644 --- a/rtp.c +++ b/rtp.c @@ -2255,14 +2255,14 @@ void *rtp_buffered_audio_processor(void *arg) { int play_enabled = 0; uint32_t flush_from_timestamp; 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 + + reset_buffer(conn); // in case there is any garbage in the player // int not_first_time_out = 0; // quick check of parameters @@ -2532,17 +2532,17 @@ void *rtp_buffered_audio_processor(void *arg) { 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; + 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); + // 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"); + debug(1, "Timing information not valid"); // outdated = 1; } - if ((flush_requested) && (seq_no >= flushUntilSeq)) { if ((have_time_information == 0) && (play_enabled)) { @@ -2556,33 +2556,31 @@ void *rtp_buffered_audio_processor(void *arg) { 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 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 (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; - //} - } + // 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))) { - unsigned char nonce[12]; memset(nonce, 0, sizeof(nonce)); memcpy(nonce + 4, packet + nread - 8, @@ -2642,71 +2640,75 @@ void *rtp_buffered_audio_processor(void *arg) { // debug(1, "frame found"); // now pass each packet to be decoded if (pkt->size) { - //if (0) { + // if (0) { if (pkt->size <= 7) { // no idea about this... - debug(1,"malformed AAC packet skipped."); + debug(1, "malformed AAC packet skipped."); } else { - ret = avcodec_send_packet(codec_context, pkt); - - 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 { - 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); } } } } - } } if (decoded_frame == NULL) debug(1, "decoded_frame is NULL"); diff --git a/rtsp.c b/rtsp.c index 6c0da18f..177a4b81 100644 --- a/rtsp.c +++ b/rtsp.c @@ -197,8 +197,8 @@ int add_pstring_to_malloc(const char *s, void **allocation, size_t *size) { *size = *size + strlen(s) + 1; uint8_t *b = (uint8_t *)p; *b = strlen(s); - p = p+1; - memcpy(p,s,strlen(s)); + p = p + 1; + memcpy(p, s, strlen(s)); response = 1; } } else { @@ -210,7 +210,7 @@ int add_pstring_to_malloc(const char *s, void **allocation, size_t *size) { uint8_t *b = (uint8_t *)p + *size; *b = strlen(s); p = p + *size + 1; - memcpy(p,s,strlen(s)); + memcpy(p, s, strlen(s)); *size = *size + strlen(s) + 1; response = 1; } @@ -1173,8 +1173,8 @@ 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; 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 + 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); @@ -1290,7 +1290,7 @@ 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) + // 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); @@ -1556,41 +1556,56 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r } 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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, "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)); @@ -1598,11 +1613,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); - //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!"); @@ -2236,14 +2251,14 @@ void handle_feedback(__attribute__((unused)) rtsp_conn_info *conn, 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_to_bin(response_plist, &resp->content, &resp->contentlength); plist_free(response_plist); // plist_free(array_plist); // plist_free(payload_plist);