Merge branch 'development' of git.aillwee.com:base/shairport-sync into development
Just checking.
This commit is contained in:
+3
-1
@@ -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])])
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user