Merge branch 'development' of git.aillwee.com:base/shairport-sync into development

Just checking.
This commit is contained in:
Mike Brady
2021-12-07 09:59:14 +00:00
9 changed files with 290 additions and 137 deletions
+3 -1
View File
@@ -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])])
+4
View File
@@ -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 {
+21 -23
View File
@@ -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,
+1
View File
@@ -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);
+3 -2
View File
@@ -60,7 +60,8 @@
<string>366.0</string>
<key>statusFlags</key>
<integer>4</integer>
<key>txtAirPlay</key>
<!-- key values for deviceID, features, pi, name and model are added programatically -->
<!-- key values for deviceID, features, pi, name, model and a txtAirPlay data field are added programatically -->
<!-- key values for firmwareBuildDate (string), build (string) and PTPInfo (string)
should be added programatically -->
</dict>
</plist>
+1 -1
View File
@@ -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)
+115 -78
View File
@@ -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);
}
}
}
+137 -28
View File
@@ -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);
+5 -4
View File
@@ -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);