Enhancements
Enable the AirPlay 2 build to operate an optional classic-AirPlay-only service or to gracefully degrade to classic AirPlay, as follows:
1. Add a new command-line option '-—service-type=<type>' and an equivalent configuration entry 'service_type = "<type>"' in the 'general' section of the configuration file,
where <type> can be "auto", "classic" or "airplay2":
1. "auto" (default) means that the service will be AirPlay 2 if NQPTP is running. If NQPTP is not running, classic AirPlay service will be provided instead, and in that case,
"(Classic)" will be appended to the default AirPlay service name visible to AirPlay clients like Apple Music, for example "RaspberryPi3B (Classic)".
2. "classic" means the service will be classic AirPlay (aka AirPlay 1).
3. "airplay2" means the service will be the modern AirPlay 2. In this case, as distinct from "auto", if NQPTP is not running, Shairport Sync will log an error and terminate.
2. In the systemd service file, NQPTP is now a "Want" rather than a "Require". If it's present, then it will be launched before Shairport Sync. If it's absent, Shairport Sync will launch anyway.
3. Improve the delivery of input format changes and emit 'sdsc' metadata when changes occur.
4. Emit new format information in the log if statistics is enabled.
Docker Changes
1. Support for linux/arm/v6 has been dropped, as Docker is no longer supported.
2. NQPTP is not started in the AirPlay 2 Docker image if '--service-type=classic' or '--service-type=airplay1' is in the command line options at the end of the docker run command.
The purpose is to ensure that ports 319 and 320 are left alone when the AirPlay 2 image is set to provide Classic service only.
Note that setting the configuration file 'service_type' to 'classic' will not prevent NQPTP from starting up -- you must use the command line option.
3. A new "dev" target has been added. It is a large image containing the custom-built FFmpeg library, NQPTP, Avahi and D-Bus along with the Shairport Sync source and
all necessary development tools. When started, Avahi, D-Bus and NQPTP are all installed and running. The bash shell has also been added and is entered.
Stability Improvements
Reorganise session preemption to fully terminate the existing session before starting a new one.
Don't delay closing the event port to wait for it to be closed at the client end.
Add a safe_socket_close() function to ensure sockets are fully closed. Use -1 to designate closed rather than 0, to prevent attempts to reclose sockets, causing mayhem.
Re-order FFmpeg decommissioning during teardown.
This commit is contained in:
@@ -657,17 +657,17 @@ int setup_software_resampler(rtsp_conn_info *conn, ssrc_t ssrc) {
|
||||
// or by using the setting that has been given.
|
||||
|
||||
// Upmixing will not be done if the setting is "auto".
|
||||
|
||||
|
||||
// Similarly, on the "auto" setting, downmixing will be done
|
||||
// only if the number of output channels available
|
||||
// is strictly less than the number of input channels.
|
||||
|
||||
|
||||
// If the number of output channels equals the number of input channels,
|
||||
// no mixing is done on the "auto" setting.
|
||||
|
||||
// To do an upmix or a custom downmix, specify
|
||||
// the target format, e.g. "7.1" in the mixdown setting.
|
||||
|
||||
|
||||
// NOTE: upmixing, by default, simply copies the input channels to their
|
||||
// equivalents in the output channels. All other channels are
|
||||
// left silent.
|
||||
@@ -683,9 +683,9 @@ int setup_software_resampler(rtsp_conn_info *conn, ssrc_t ssrc) {
|
||||
AVChannelLayout output_channel_layout;
|
||||
if (config.mixdown_enable != 0) {
|
||||
if (config.mixdown_channel_layout == 0) {
|
||||
if (CHANNELS_FROM_ENCODED_FORMAT(output_configuration) < (unsigned) input_channel_count) {
|
||||
if (CHANNELS_FROM_ENCODED_FORMAT(output_configuration) < (unsigned)input_channel_count) {
|
||||
av_channel_layout_default(&output_channel_layout,
|
||||
CHANNELS_FROM_ENCODED_FORMAT(output_configuration));
|
||||
CHANNELS_FROM_ENCODED_FORMAT(output_configuration));
|
||||
} else {
|
||||
av_channel_layout_from_mask(&output_channel_layout, input_layout);
|
||||
}
|
||||
@@ -705,7 +705,8 @@ int setup_software_resampler(rtsp_conn_info *conn, ssrc_t ssrc) {
|
||||
av_opt_set_int(swr, "in_channel_layout", input_layout, 0);
|
||||
if (config.mixdown_enable != 0) {
|
||||
if (config.mixdown_channel_layout == 0) {
|
||||
if (CHANNELS_FROM_ENCODED_FORMAT(output_configuration) < av_get_channel_layout_nb_channels(input_layout)) {
|
||||
if ((signed)CHANNELS_FROM_ENCODED_FORMAT(output_configuration) <
|
||||
(av_get_channel_layout_nb_channels(input_layout)) {
|
||||
output_layout =
|
||||
av_get_default_channel_layout(CHANNELS_FROM_ENCODED_FORMAT(output_configuration));
|
||||
} // else leave output_layout as it was: the sames as the input_layout.
|
||||
@@ -778,7 +779,7 @@ int setup_software_resampler(rtsp_conn_info *conn, ssrc_t ssrc) {
|
||||
// get information about the output from the resampler
|
||||
int64_t resampler_output_format = 0;
|
||||
int resampler_channels_found = 0;
|
||||
|
||||
|
||||
int oldState;
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
|
||||
|
||||
@@ -891,7 +892,7 @@ int setup_software_resampler(rtsp_conn_info *conn, ssrc_t ssrc) {
|
||||
free(device_channels);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
|
||||
if (output_channel_map_faulty != 0) {
|
||||
@@ -1035,17 +1036,26 @@ int setup_software_resampler(rtsp_conn_info *conn, ssrc_t ssrc) {
|
||||
return response; // 0 if everything is okay
|
||||
}
|
||||
void prepare_decoding_chain(rtsp_conn_info *conn, ssrc_t ssrc) {
|
||||
if ((ssrc != SSRC_NONE) && (ssrc != conn->incoming_ssrc) && (ssrc_is_recognised(ssrc) != 0)) {
|
||||
// conn->incoming_ssrc will be SSRC_NONE only before the first valid encoding is found
|
||||
if ((config.statistics_requested) && (conn->incoming_ssrc != SSRC_NONE))
|
||||
inform("Connection %d: Incoming Audio Encoding is switching to: \"%s\".",
|
||||
conn->connection_number, get_ssrc_name(ssrc));
|
||||
// conn->incoming_ssrc = payload_ssrc;
|
||||
#ifdef CONFIG_METADATA
|
||||
send_ssnc_metadata('sdsc', get_ssrc_name(ssrc), strlen(get_ssrc_name(ssrc)), 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((ssrc_is_recognised(ssrc)) && (ssrc != conn->incoming_ssrc)) {
|
||||
|
||||
if ((config.statistics_requested != 0) && (ssrc != SSRC_NONE) &&
|
||||
(conn->incoming_ssrc != SSRC_NONE)) {
|
||||
debug(2, "Connection %d: incoming audio switching to \"%s\".", conn->connection_number,
|
||||
get_ssrc_name(ssrc));
|
||||
#ifdef CONFIG_METADATA
|
||||
send_ssnc_metadata('sdsc', get_ssrc_name(ssrc), strlen(get_ssrc_name(ssrc)), 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
if ((config.statistics_requested != 0) && (ssrc != SSRC_NONE) &&
|
||||
(conn->incoming_ssrc != SSRC_NONE)) {
|
||||
debug(2, "Connection %d: incoming audio switching to \"%s\".", conn->connection_number,
|
||||
get_ssrc_name(ssrc));
|
||||
}
|
||||
*/
|
||||
// the ssrc of the incoming packet is different to the ssrc of the decoding chain
|
||||
// so the decoding chain must be rebuilt
|
||||
|
||||
@@ -1071,7 +1081,8 @@ void prepare_decoding_chain(rtsp_conn_info *conn, ssrc_t ssrc) {
|
||||
conn->codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
|
||||
break;
|
||||
default:
|
||||
die("Connection %d: can't find a suitable codec for SSRC: %s", conn->connection_number, get_ssrc_name(ssrc));
|
||||
die("Connection %d: can't find a suitable codec for SSRC: %s", conn->connection_number,
|
||||
get_ssrc_name(ssrc));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1080,17 +1091,18 @@ void prepare_decoding_chain(rtsp_conn_info *conn, ssrc_t ssrc) {
|
||||
|
||||
// Get a decoder-dependent codec context
|
||||
conn->codec_context = avcodec_alloc_context3(conn->codec);
|
||||
if (conn->codec_context != NULL) {
|
||||
if (conn->codec_context != NULL) {
|
||||
// push a deallocator -- av_free(codec_context)
|
||||
// pthread_cleanup_push(avcodec_alloc_context3_cleanup_handler, &conn->codec_context);
|
||||
|
||||
|
||||
// prepare to open the codec context with that codec
|
||||
// but first, if it's the ALAC decoder, prepare a magic cookie
|
||||
if ((ssrc == ALAC_48000_S24_2) || (ssrc == ALAC_44100_S16_2)) {
|
||||
alac_ffmpeg_magic_cookie *extradata =
|
||||
malloc(sizeof(alac_ffmpeg_magic_cookie)); // might not use it
|
||||
if (extradata == NULL)
|
||||
die("connection %d: could not allocate memory for a magic cookie.", conn->connection_number);
|
||||
die("connection %d: could not allocate memory for a magic cookie.",
|
||||
conn->connection_number);
|
||||
// creata a magic cookie preceded by the 12-byte "atom" (?) expected by FFMPEG (?)
|
||||
memset(extradata, 0, sizeof(alac_ffmpeg_magic_cookie));
|
||||
extradata->cookie_size = htonl(sizeof(alac_ffmpeg_magic_cookie));
|
||||
@@ -1115,18 +1127,19 @@ void prepare_decoding_chain(rtsp_conn_info *conn, ssrc_t ssrc) {
|
||||
}
|
||||
// pthread_cleanup_push(malloc_cleanup, &conn->codec_context->extradata);
|
||||
// avcodec_free_context() will free extradata
|
||||
|
||||
|
||||
if (avcodec_open2(conn->codec_context, conn->codec, NULL) < 0) {
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
die("connection %d: could not initialise the codec context", conn->connection_number);
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
die("connection %d: could not initialise the codec context", conn->connection_number);
|
||||
}
|
||||
} else {
|
||||
die("connection %d: could not allocate a codec context!", conn->connection_number);
|
||||
}
|
||||
}
|
||||
pthread_setcancelstate(oldState, NULL);
|
||||
|
||||
conn->input_rate = get_ssrc_rate(ssrc);
|
||||
debug(2, "Connection %d: set conn->input_rate: %u.", conn->connection_number, conn->input_rate);
|
||||
debug(2, "Connection %d: set conn->input_rate: %u.", conn->connection_number,
|
||||
conn->input_rate);
|
||||
if ((ssrc == ALAC_48000_S24_2) || (ssrc == ALAC_44100_S16_2)) {
|
||||
conn->frames_per_packet = 352;
|
||||
} else {
|
||||
@@ -1583,28 +1596,26 @@ uint32_t player_put_packet(uint32_t ssrc, seq_t seqno, uint32_t actual_timestamp
|
||||
} else if (config.decoder_in_use == 1 << decoder_ffmpeg_alac) {
|
||||
#ifdef CONFIG_FFMPEG
|
||||
prepare_decoding_chain(conn, ALAC_44100_S16_2);
|
||||
// if (len > 8) {
|
||||
abuf->avframe = block_to_avframe(conn, data_to_use, len);
|
||||
abuf->ssrc = ALAC_44100_S16_2;
|
||||
if (abuf->avframe) {
|
||||
input_packets_used = abuf->avframe->nb_samples;
|
||||
}
|
||||
if (mute) {
|
||||
// it's important to have already run it through the decoder before dropping it
|
||||
// especially if it an AAC decoder
|
||||
debug(2, "ap1 muting frame %u.", actual_timestamp);
|
||||
abuf->length = abuf->avframe->nb_samples;
|
||||
av_frame_free(&abuf->avframe);
|
||||
abuf->avframe = NULL;
|
||||
}
|
||||
// } else {
|
||||
if (len <= 8) {
|
||||
debug(1,
|
||||
"Using the FFMPEG ALAC_44100_S16_2 decoder, a short audio packet %u, rtptime %u, of length %zu has been decoded but not discarded. Contents follow:", seqno,
|
||||
actual_timestamp, len);
|
||||
debug_print_buffer(1, data, len);
|
||||
// abuf->length = conn->frames_per_packet;
|
||||
// abuf->avframe = NULL;
|
||||
abuf->avframe = block_to_avframe(conn, data_to_use, len);
|
||||
abuf->ssrc = ALAC_44100_S16_2;
|
||||
if (abuf->avframe) {
|
||||
input_packets_used = abuf->avframe->nb_samples;
|
||||
}
|
||||
if (mute) {
|
||||
// it's important to have already run it through the decoder before dropping it
|
||||
// especially if it an AAC decoder
|
||||
debug(2, "ap1 muting frame %u.", actual_timestamp);
|
||||
abuf->length = abuf->avframe->nb_samples;
|
||||
av_frame_free(&abuf->avframe);
|
||||
abuf->avframe = NULL;
|
||||
}
|
||||
if (len <= 8) {
|
||||
debug(2,
|
||||
"Connection %d, using FFMPEG on an ALAC_44100_S16_2 stream, a short audio packet "
|
||||
"%u, rtptime %u, of length %zu has been decoded but not discarded. Contents "
|
||||
"follow:",
|
||||
conn->connection_number, seqno, actual_timestamp, len);
|
||||
debug_print_buffer(2, data, len);
|
||||
}
|
||||
#else
|
||||
debug(1, "FFMPEG support has not been built into this version Shairport Sync!");
|
||||
@@ -1636,27 +1647,25 @@ uint32_t player_put_packet(uint32_t ssrc, seq_t seqno, uint32_t actual_timestamp
|
||||
|
||||
prepare_decoding_chain(conn, ssrc); // dynamically set the decoding environment
|
||||
|
||||
// if (len > 8) {
|
||||
abuf->avframe = block_to_avframe(conn, data, len);
|
||||
abuf->ssrc = ssrc; // tag the avframe with its specific SSRC
|
||||
if (abuf->avframe) {
|
||||
input_packets_used = abuf->avframe->nb_samples;
|
||||
}
|
||||
if (mute) {
|
||||
// it's important to have already run it through the decoder before dropping it
|
||||
debug(2, "ap2 muting frame %u.", actual_timestamp);
|
||||
abuf->length = abuf->avframe->nb_samples;
|
||||
av_frame_free(&abuf->avframe);
|
||||
abuf->avframe = NULL;
|
||||
}
|
||||
//} else {
|
||||
abuf->avframe = block_to_avframe(conn, data, len);
|
||||
abuf->ssrc = ssrc; // tag the avframe with its specific SSRC
|
||||
if (abuf->avframe) {
|
||||
input_packets_used = abuf->avframe->nb_samples;
|
||||
}
|
||||
if (mute) {
|
||||
// it's important to have already run it through the decoder before dropping it
|
||||
debug(2, "ap2 muting frame %u.", actual_timestamp);
|
||||
abuf->length = abuf->avframe->nb_samples;
|
||||
av_frame_free(&abuf->avframe);
|
||||
abuf->avframe = NULL;
|
||||
}
|
||||
|
||||
if (len <= 8) {
|
||||
debug(1,
|
||||
"Using an FFMPEG decoder, a short audio packet %u, rtptime %u, of length %zu has been decoded but not discarded. Contents follow:", seqno,
|
||||
actual_timestamp, len);
|
||||
debug_print_buffer(1, data, len);
|
||||
// abuf->length = 0;
|
||||
// abuf->avframe = NULL;
|
||||
debug(2,
|
||||
"Connection %d: using FFMPEG on a %s stream, a short audio packet %u, rtptime %u, "
|
||||
"of length %zu has been decoded but not discarded. Contents follow:",
|
||||
conn->connection_number, get_ssrc_name(ssrc), seqno, actual_timestamp, len);
|
||||
debug_print_buffer(2, data, len);
|
||||
}
|
||||
abuf->ready = 1;
|
||||
abuf->status = 0; // signifying that it was received
|
||||
@@ -2497,12 +2506,12 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
|
||||
RATE_FROM_ENCODED_FORMAT(config.current_output_configuration)) /
|
||||
1000000000;
|
||||
int64_t exact_frame_gap = gross_frame_gap - dac_delay;
|
||||
|
||||
|
||||
debug(4,
|
||||
"Exact frame gap: %" PRId64
|
||||
". DAC delay: %ld. Total: %" PRId64 ". First packet timestamp: %u. ",
|
||||
exact_frame_gap, dac_delay, gross_frame_gap, conn->first_packet_timestamp);
|
||||
"Exact frame gap: %" PRId64 ". DAC delay: %ld. Total: %" PRId64
|
||||
". First packet timestamp: %u. ",
|
||||
exact_frame_gap, dac_delay, gross_frame_gap,
|
||||
conn->first_packet_timestamp);
|
||||
// int64_t frames_needed_to_maintain_desired_buffer =
|
||||
// (int64_t)(config.audio_backend_buffer_desired_length *
|
||||
// config.current_output_configuration->rate) -
|
||||
@@ -2720,9 +2729,11 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
|
||||
}
|
||||
if (wait) {
|
||||
uint64_t time_to_wait_for_wakeup_ns = 10000000; // default
|
||||
if (conn->input_format_is_valid != 0) {
|
||||
time_to_wait_for_wakeup_ns = 1000000000 / conn->input_rate; // this is time period of one frame
|
||||
time_to_wait_for_wakeup_ns *= 4 * conn->frames_per_packet; // about 4 * 7 mS for 352 frames per second
|
||||
if (conn->input_format_is_valid != 0) {
|
||||
time_to_wait_for_wakeup_ns =
|
||||
1000000000 / conn->input_rate; // this is time period of one frame
|
||||
time_to_wait_for_wakeup_ns *=
|
||||
4 * conn->frames_per_packet; // about 4 * 7 mS for 352 frames per second
|
||||
}
|
||||
|
||||
#ifdef COMPILE_FOR_LINUX_AND_FREEBSD_AND_CYGWIN_AND_OPENBSD
|
||||
@@ -2733,13 +2744,14 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
|
||||
struct timespec time_of_wakeup;
|
||||
time_of_wakeup.tv_sec = sec;
|
||||
time_of_wakeup.tv_nsec = nsec;
|
||||
// debug(1, "wait for up to %f mS or for the next packet...", time_to_wait_for_wakeup_ns * 1E-6);
|
||||
// debug(1, "wait for up to %f mS or for the next packet...", time_to_wait_for_wakeup_ns *
|
||||
// 1E-6);
|
||||
int rc = pthread_cond_timedwait(&conn->flowcontrol, &conn->ab_mutex,
|
||||
&time_of_wakeup); // this is a pthread cancellation point
|
||||
if ((rc != 0) && (rc != ETIMEDOUT))
|
||||
// if (rc)
|
||||
debug(3, "pthread_cond_timedwait returned error code %d.", rc);
|
||||
// debug(1, "waited");
|
||||
// debug(1, "waited");
|
||||
#endif
|
||||
#ifdef COMPILE_FOR_OSX
|
||||
uint64_t sec = time_to_wait_for_wakeup_ns / 1000000000;
|
||||
@@ -2789,8 +2801,8 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn, int resync_requested) {
|
||||
debug(2, "setting up software resampler for %s for the first time.",
|
||||
get_ssrc_name(curframe->ssrc));
|
||||
} else {
|
||||
debug(1, "Connection %d: queued audio buffers switching to \"%s\".", conn->connection_number,
|
||||
get_ssrc_name(curframe->ssrc));
|
||||
debug(2, "Connection %d: queued audio buffers switching to \"%s\".",
|
||||
conn->connection_number, get_ssrc_name(curframe->ssrc));
|
||||
clear_software_resampler(conn);
|
||||
// ask the backend if it can give us its best choice for an ffmpeg configuration:
|
||||
}
|
||||
@@ -3029,8 +3041,8 @@ static int stuff_buffer_vernier(int32_t *inptr, int length, sps_format_t l_outpu
|
||||
debug(1,
|
||||
"Can't see how this could ever happen, but "
|
||||
"current_input_sample_floor_index %" PRId64
|
||||
" has just stepped outside the frame of %d samples, with stuff %d and current_input_sample_index_fp at %" PRId64 ".%05" PRId64
|
||||
".",
|
||||
" has just stepped outside the frame of %d samples, with stuff %d and "
|
||||
"current_input_sample_index_fp at %" PRId64 ".%05" PRId64 ".",
|
||||
current_input_sample_floor_index, length, stuff, current_input_sample_index_int,
|
||||
current_input_sample_index_low);
|
||||
current_input_sample_floor_index = length - 1; // hack
|
||||
@@ -3052,8 +3064,8 @@ static int stuff_buffer_vernier(int32_t *inptr, int length, sps_format_t l_outpu
|
||||
debug(1,
|
||||
"Can't see how this could ever happen, but "
|
||||
"current_input_sample_ceil_index %" PRId64
|
||||
" has just stepped outside the frame of %d samples, with stuff %d and current_input_sample_index_fp at %" PRId64
|
||||
".%05" PRId64 ".",
|
||||
" has just stepped outside the frame of %d samples, with stuff %d and "
|
||||
"current_input_sample_index_fp at %" PRId64 ".%05" PRId64 ".",
|
||||
current_input_sample_floor_index, length, stuff, current_input_sample_index_int,
|
||||
current_input_sample_index_low);
|
||||
}
|
||||
@@ -3314,17 +3326,10 @@ void player_thread_cleanup_handler(void *arg) {
|
||||
rtsp_conn_info *conn = (rtsp_conn_info *)arg;
|
||||
// debug(1, "Connection %d: player_thread_cleanup_handler start.", conn->connection_number);
|
||||
|
||||
#ifdef CONFIG_FFMPEG
|
||||
// debug(1, "FFmpeg clearup");
|
||||
clear_software_resampler(conn);
|
||||
clear_decoding_chain(conn);
|
||||
// debug(1, "FFmpeg clearup done");
|
||||
#endif
|
||||
|
||||
if (config.output->stop) {
|
||||
#ifdef CONFIG_FFMPEG
|
||||
if (avflush(conn) > 1)
|
||||
debug(3, "ffmpeg flush at stop!");
|
||||
if ((config.decoder_in_use == 1 << decoder_ffmpeg_alac) && (avflush(conn) > 1))
|
||||
debug(1, "ffmpeg flush at stop!");
|
||||
#endif
|
||||
debug(2, "Connection %d: player: stop the output backend.", conn->connection_number);
|
||||
config.output->stop();
|
||||
@@ -3418,6 +3423,15 @@ void player_thread_cleanup_handler(void *arg) {
|
||||
ptp_send_control_message_string("E");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FFMPEG
|
||||
if (config.decoder_in_use == 1 << decoder_ffmpeg_alac) {
|
||||
// debug(1, "FFmpeg clearup");
|
||||
clear_software_resampler(conn);
|
||||
clear_decoding_chain(conn);
|
||||
// debug(1, "FFmpeg clearup done");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (conn->outbuf) {
|
||||
free(conn->outbuf);
|
||||
conn->outbuf = NULL;
|
||||
@@ -3512,11 +3526,17 @@ void *player_thread_func(void *arg) {
|
||||
init_alac_decoder((int32_t *)&conn->stream.fmtp,
|
||||
conn); // this sets up incoming rate, bit depth, channels.
|
||||
// No pthread cancellation point in here
|
||||
#ifdef CONFIG_METADATA
|
||||
send_ssnc_metadata('sdsc', "ALAC/44100/S16_LE/2", strlen("ALAC/44100/S16_LE/2"), 1);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_APPLE_ALAC
|
||||
if (config.decoder_in_use == 1 << decoder_apple_alac) {
|
||||
apple_alac_init(conn->stream.fmtp); // no pthread cancellation point in here
|
||||
#ifdef CONFIG_METADATA
|
||||
send_ssnc_metadata('sdsc', "ALAC/44100/S16_LE/2", strlen("ALAC/44100/S16_LE/2"), 1);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -4145,7 +4165,7 @@ void *player_thread_func(void *arg) {
|
||||
send_ssnc_stream_description("Classic", get_ssrc_name(conn->incoming_ssrc));
|
||||
#endif
|
||||
if (config.statistics_requested)
|
||||
inform("Connection %d: Classic AirPlay (\"AirPlay 1\") Compatible playback. "
|
||||
inform("Connection %d: Classic AirPlay (\"AirPlay 1\") playback. "
|
||||
"Input format: %s. Output format: %s.",
|
||||
conn->connection_number, get_ssrc_name(conn->incoming_ssrc),
|
||||
short_description);
|
||||
@@ -4369,10 +4389,14 @@ void *player_thread_func(void *arg) {
|
||||
if (inframe->timestamp_gap < 0) {
|
||||
gap_to_fix = -inframe->timestamp_gap; // this is frames at the input rate
|
||||
int64_t gap_to_fix_ns = (gap_to_fix * 1000000000) / conn->input_rate;
|
||||
gap_to_fix = (gap_to_fix_ns *
|
||||
RATE_FROM_ENCODED_FORMAT(config.current_output_configuration) + 1000000000/2) /
|
||||
gap_to_fix = (gap_to_fix_ns * RATE_FROM_ENCODED_FORMAT(
|
||||
config.current_output_configuration) +
|
||||
1000000000 / 2) /
|
||||
1000000000; // this is frames at the output rate
|
||||
debug(4, "gap_to_fix: %u frames at input rate, %" PRId64 " frames at output rate.", -inframe->timestamp_gap, gap_to_fix);
|
||||
debug(4,
|
||||
"gap_to_fix: %u frames at input rate, %" PRId64
|
||||
" frames at output rate.",
|
||||
-inframe->timestamp_gap, gap_to_fix);
|
||||
// debug(3, "due to timstamp gap of %d frames, skip %" PRId64 " output
|
||||
// frames.", inframe->timestamp_gap, gap_to_fix);
|
||||
}
|
||||
@@ -4580,7 +4604,7 @@ void *player_thread_func(void *arg) {
|
||||
#endif
|
||||
) {
|
||||
|
||||
float(*fbufs)[1024] = malloc(conn->input_num_channels * sizeof(*fbufs));
|
||||
float (*fbufs)[1024] = malloc(conn->input_num_channels * sizeof(*fbufs));
|
||||
// debug(1, "size of array allocated is %d bytes.", conn->input_num_channels *
|
||||
// sizeof(*fbufs));
|
||||
int32_t *tbuf32 = conn->tbuf;
|
||||
@@ -4794,15 +4818,14 @@ void *player_thread_func(void *arg) {
|
||||
frames_to_skip -= play_samples;
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
size_t bytes_to_skip = frames_to_skip *
|
||||
size_t bytes_to_skip =
|
||||
frames_to_skip *
|
||||
CHANNELS_FROM_ENCODED_FORMAT(config.current_output_configuration) *
|
||||
sps_format_sample_size(
|
||||
FORMAT_FROM_ENCODED_FORMAT(config.current_output_configuration));
|
||||
|
||||
char *play_starting_point = conn->outbuf + bytes_to_skip;
|
||||
|
||||
|
||||
config.output->play(play_starting_point, play_samples - frames_to_skip,
|
||||
play_samples_are_timed, inframe->timestamp,
|
||||
should_be_time);
|
||||
|
||||
Reference in New Issue
Block a user