This squashed commit comprises a long series of improvements, bug fixes, optimizations, and new feature additions:

- 48,000 frames per second operation,
 - 48k lossless stereo, 5.1 and 7.1 surround sound
 - Multichannel and multi rate operation on ALSA, PipeWire, PulseAudio, FreeBSD, stdout and unix pipe output backends.

Automatic, flexible and controllable output format (rate, sample format and channel count) selection.

Full FFmpeg integration to support transcoding, resampling, and new audio formats.

Better operation on lower powered devices down to e.g. Raspberry Pi B.

Reduced Docker image sizes with a slimmed-down FFmpeg library.

Enhanced timestamp handling for better synchronization.

Improved the sync error calculation.

A new "vernier" resampling and interpolation method for low-power CPUs.

Bug fixes and minor enhancements.

Note -- there are many breaking changes from previous versions of Shairport Sync!
This commit is contained in:
Mike Brady
2025-02-23 13:31:17 +00:00
parent 588e6a5b7b
commit 60143518c2
110 changed files with 12416 additions and 7759 deletions
+201 -27
View File
@@ -16,18 +16,98 @@
#include <polarssl/havege.h>
#endif
#ifdef CONFIG_OPENSSL
#include <openssl/aes.h>
#endif
#ifdef CONFIG_AIRPLAY_2
#include "pair_ap/pair.h"
#include <plist/plist.h>
#endif
#ifdef CONFIG_FFMPEG
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libswresample/swresample.h>
#endif
#include "alac.h"
#include "audio.h"
// clang-format off
/*
__________________________________________________________________________________________________________________________________
* ALAC Specific Info (24 bytes) (mandatory)
__________________________________________________________________________________________________________________________________
The Apple Lossless codec stores specific information about the encoded stream in the ALACSpecificConfig. This
info is vended by the encoder and is used to setup the decoder for a given encoded bitstream.
When read from and written to a file, the fields of this struct must be in big-endian order.
When vended by the encoder (and received by the decoder) the struct values will be in big-endian order.
struct ALACSpecificConfig (defined in ALACAudioTypes.h)
abstract This struct is used to describe codec provided information about the encoded Apple Lossless bitstream.
It must accompany the encoded stream in the containing audio file and be provided to the decoder.
field frameLength uint32_t indicating the frames per packet when no explicit frames per packet setting is
present in the packet header. The encoder frames per packet can be explicitly set
but for maximum compatibility, the default encoder setting of 4096 should be used.
field compatibleVersion uint8_t indicating compatible version,
value must be set to 0
field bitDepth uint8_t describes the bit depth of the source PCM data (maximum value = 32)
field pb uint8_t currently unused tuning parameter.
value should be set to 40
field mb uint8_t currently unused tuning parameter.
value should be set to 10
field kb uint8_t currently unused tuning parameter.
value should be set to 14
field numChannels uint8_t describes the channel count (1 = mono, 2 = stereo, etc...)
when channel layout info is not provided in the 'magic cookie', a channel count > 2
describes a set of discreet channels with no specific ordering
field maxRun uint16_t currently unused.
value should be set to 255
field maxFrameBytes uint32_t the maximum size of an Apple Lossless packet within the encoded stream.
value of 0 indicates unknown
field avgBitRate uint32_t the average bit rate in bits per second of the Apple Lossless stream.
value of 0 indicates unknown
field sampleRate uint32_t sample rate of the encoded stream
*/
// clang-format on
typedef struct __attribute__((__packed__)) ALACSpecificConfig {
uint32_t frameLength;
uint8_t compatibleVersion;
uint8_t bitDepth;
uint8_t pb;
uint8_t mb;
uint8_t kb;
uint8_t numChannels;
uint16_t maxRun;
uint32_t maxFrameBytes;
uint32_t avgBitRate;
uint32_t sampleRate;
} ALACSpecificConfig;
// everything in here is big-endian, i.e. network byte order
typedef struct __attribute__((__packed__)) alac_ffmpeg_magic_cookie {
uint32_t cookie_size; // 36 bytes
uint32_t cookie_tag; // 'alac'
uint32_t cookie_version; // 0
ALACSpecificConfig alac_config;
} alac_ffmpeg_magic_cookie;
#define time_ping_history_power_of_two 7
// this must now be zero, otherwise bad things will happen
#define time_ping_history \
@@ -57,6 +137,19 @@ typedef enum {
typedef uint16_t seq_t;
// these are the values coming in on a buffered audio RTP packet's SSRC field
// the apparent significances are as indicated.
// Dolby Atmos seems to be 7P1
typedef enum {
SSRC_NONE = 0,
ALAC_44100_S16_2 = 0x0000FACE, // this is made up
ALAC_48000_S24_2 = 0x15000000,
AAC_44100_F24_2 = 0x16000000,
AAC_48000_F24_2 = 0x17000000,
AAC_48000_F24_5P1 = 0x27000000,
AAC_48000_F24_7P1 = 0x28000000,
} ssrc_t;
typedef struct audio_buffer_entry { // decoded audio packets
uint8_t ready;
uint8_t status; // flags
@@ -66,11 +159,19 @@ typedef struct audio_buffer_entry { // decoded audio packets
uint64_t initialisation_time; // the time the packet was added or the time it was noticed the
// packet was missing
uint64_t resend_time; // time of last resend request or zero
uint32_t given_timestamp; // for debugging and checking
int length; // the length of the decoded data
uint32_t timestamp; // for timing
int32_t timestamp_gap; // the difference between the timestamp and the expected timestamp.
size_t length; // the length of the decoded data (or silence requested) in input frames
#ifdef CONFIG_FFMPEG
ssrc_t ssrc; // this is the type of this specific frame.
AVFrame *avframe; // In AP2 and optionally in AP1, an AVFrame will be
// used to carry audio rather than just a malloced memory space.
#endif
} abuf_t;
typedef struct stats { // statistics for running averages
uint32_t timestamp; // timestamp (denominated in input frames)
size_t frames; // number of audio frames in the block (denominated in output frames)
int64_t sync_error, correction, drift;
} stats_t;
@@ -88,6 +189,9 @@ typedef struct stats { // statistics for running averages
#define BUFFER_FRAMES 1024
// maximum number of frames that can be added or removed from a packet_count
#define INTERPOLATION_LIMIT 20
typedef enum {
ast_unknown,
ast_uncompressed, // L16/44100/2
@@ -126,12 +230,16 @@ typedef struct {
sized_buffer encrypted_read_buffer;
sized_buffer plaintext_read_buffer;
int is_encrypted;
char *description;
} pair_cipher_bundle; // cipher context and buffers
typedef struct {
struct pair_setup_context *setup_ctx;
struct pair_verify_context *verify_ctx;
pair_cipher_bundle control_cipher_bundle;
pair_cipher_bundle event_cipher_bundle;
pair_cipher_bundle data_cipher_bundle;
char *data_cipher_salt;
} ap2_pairing;
// flush requests are stored in order of flushFromSeq
@@ -148,11 +256,15 @@ typedef struct flush_request_t {
#endif
typedef struct {
int connection_number; // for debug ID purposes, nothing else...
int resend_interval; // this is really just for debugging
int rtsp_link_is_idle; // if true, this indicates if the client asleep
char *UserAgent; // free this on teardown
int AirPlayVersion; // zero if not an AirPlay session. Used to help calculate latency
int connection_number; // for debug ID purposes, nothing else...
int is_playing; // set true by player_play, set false by player_stop
unsigned int sync_samples_index; // for estimating the gap between the highest and lowest timing
// error over the past n samples
unsigned int sync_samples_count; // the array of samples is defined locally
int at_least_one_frame_seen_this_session; // set when the first frame is output
int resend_interval; // this is really just for debugging
char *UserAgent; // free this on teardown
int AirPlayVersion; // zero if not an AirPlay session. Used to help calculate latency
int latency_warning_issued;
uint32_t latency; // the actual latency used for this play session
uint32_t minimum_latency; // set if an a=min-latency: line appears in the ANNOUNCE message; zero
@@ -175,12 +287,11 @@ typedef struct {
// buffers to delete on exit
int32_t *tbuf;
int32_t *sbuf;
char *outbuf;
// for generating running statistics...
stats_t *statistics;
// stats_t *statistics;
// for holding the output rate information until printed out at the end of a session
double raw_frame_rate;
@@ -201,9 +312,11 @@ typedef struct {
// other stuff...
pthread_t *player_thread;
abuf_t audio_buffer[BUFFER_FRAMES];
unsigned int max_frames_per_packet, input_num_channels, input_bit_depth, input_rate;
int input_bytes_per_frame, output_bytes_per_frame, output_sample_ratio;
int max_frame_size_change;
unsigned int frames_per_packet, input_num_channels, input_bit_depth, input_effective_bit_depth,
input_rate;
int input_bytes_per_frame;
unsigned int output_sample_ratio;
unsigned int output_bit_depth;
int64_t previous_random_number;
alac_file *decoder_info;
uint64_t packet_count;
@@ -215,7 +328,8 @@ typedef struct {
uint64_t missing_packets, late_packets, too_late_packets, resend_requests;
int decoder_in_use;
// debug variables
int32_t last_seqno_read;
int last_seqno_valid;
seq_t last_seqno_read;
// mutexes and condition variables
pthread_cond_t flowcontrol;
pthread_mutex_t ab_mutex, flush_mutex, volume_control_mutex, player_create_delete_mutex;
@@ -224,11 +338,8 @@ typedef struct {
double own_airplay_volume;
int own_airplay_volume_set;
uint32_t timestamp_epoch, last_timestamp,
maximum_timestamp_interval; // timestamp_epoch of zero means not initialised, could start at 2
// or 1.
int ab_buffering, ab_synced;
int64_t first_packet_timestamp;
uint32_t first_packet_timestamp;
int flush_requested;
int flush_output_flushed; // true if the output device has been flushed.
uint32_t flush_rtp_timestamp;
@@ -243,8 +354,6 @@ typedef struct {
aes_context dctx;
#endif
int amountStuffed;
int32_t framesProcessedInThisEpoch;
int32_t framesGeneratedInThisEpoch;
int32_t correctionsRequestedInThisEpoch;
@@ -333,6 +442,7 @@ typedef struct {
int ap2_play_enabled; // protect with flush mutex
ap2_pairing ap2_pairing_context;
struct pair_result *pair_setup_result; // need to keep the shared secret
int event_socket;
int data_socket;
@@ -351,10 +461,70 @@ typedef struct {
uint64_t audio_format;
uint64_t compression;
unsigned char *session_key; // needs to be free'd at the end
char *ap2_client_name; // needs to be free'd at teardown phase 2
uint64_t frames_packet;
uint64_t type;
uint64_t networkTimeTimelineID; // the clock ID used by the player
uint8_t groupContainsGroupLeader; // information coming from the SETUP
uint64_t compressionType;
#endif
#ifdef CONFIG_FFMPEG
ssrc_t incoming_ssrc; // The SSRC of incoming packets. In AirPlay 2, the RTP SSRC seems to encode
// something about the contents of the packet -- Atmos/etc. We use it also
// even in AP1 as a code
ssrc_t resampler_ssrc; // the SSRC of packets for which the software resampler has been set up.
// normally it's the same as that of incoming packets, but if the encoding of incoming packets
// changes dynamically and the decoding chain hasn't been reset, the resampler will have to deal
// with queued AVFrames encoded according to the previous SSRC.
const AVCodec *codec;
AVCodecContext *codec_context;
// the swr can't be used just after the incoming packet has been decoded as explained below
// The reasons that resampling can not occur when the packet initially arrives are twofold.
// Resampling requires input samples from before and after the resampling instant.
// So, at the end of a block, since the subsequent samples aren't in the block, resampling
// is deferred until the next block is loaded. From this, the two reasons follow:
// First, the "next" block to be provided in player_put_packet is not guaranteed to be
// the next block in sequence -- packets can arrive out of sequence in UDP transmission.
// Second, not all the frames that should be generated for a block will be generated
// by a call to swr_convert. The frames that can't be calculated will not be provided, and
// will be held back and provided to the subsequent call.
// Tht means that the first frame output by swr_convert will not in general,
// not correspond to the first frame provided to it, throwing
// timing calculations off.
// In summary, we have to wait until (1) we have all the blocks in order,
// and (2) we have to track the number of resampler output frames to
// keep the correspondence between them and the input frames.
// We can calculate the "deficit" between the number of frames that should be generated
// versus the number of frames actually generated.
// For example, converting 352 frames at 44,100 to 48,000 should result
// in 352 * 48000 / 44100, or 383.129252 frames.
// Say only 360 frames are actually produced, then the deficit is 23.129252.
// If those 360 frames are sent to the output device, then the timing of the next
// block of 352 frames will be ahead by 23.129252 frames at 48,000 fps -- about 0.48 ms.
// We need to add the delay corresponding to the frames that should have been sent to
// keep timing correct. I.e. when calculating the buffer delay at the start of the following
// block, those 23.129252 frames the were not actually sent should be added to it.
// The "deficit" can readily be kept up to date and can be always added to the
// DAC buffer delay to exactly compensate for the
SwrContext *swr; // this will do transcoding anf resampling, if necessary, just prior to output
int ffmpeg_decoding_chain_initialised;
int64_t resampler_output_channels;
int resampler_output_bytes_per_sample;
int64_t frames_retained_in_the_resampler; // swr will retain frames it hasn't finished processing
// they'll come out before the frames corresponding to the start of next block passed to
// swrconvert so we need to compensate for their absence in sync timing
unsigned int output_channel_to_resampler_channel_map[8];
unsigned int output_channel_map_size;
#endif
// used as the initials values for calculating the rate at which the source thinks it's sending
@@ -407,7 +577,6 @@ typedef struct {
void *dapo_private_storage; // this is used for compatibility, if dacp stuff isn't enabled.
int enable_dither; // needed for filling silences before play actually starts
uint64_t dac_buffer_queue_minimum_length;
} rtsp_conn_info;
extern int statistics_row; // will be reset to zero when debug level changes or statistics enabled
@@ -421,7 +590,6 @@ int32_t modulo_32_offset(uint32_t from, uint32_t to);
void ab_resync(rtsp_conn_info *conn);
int player_prepare_to_play(rtsp_conn_info *conn);
int player_play(rtsp_conn_info *conn);
int player_stop(rtsp_conn_info *conn);
@@ -429,8 +597,8 @@ void player_volume(double f, rtsp_conn_info *conn);
void player_volume_without_notification(double f, rtsp_conn_info *conn);
void player_flush(uint32_t timestamp, rtsp_conn_info *conn);
// void player_full_flush(rtsp_conn_info *conn);
void player_put_packet(int original_format, seq_t seqno, uint32_t actual_timestamp, uint8_t *data,
int len, rtsp_conn_info *conn);
uint32_t player_put_packet(uint32_t ssrc, seq_t seqno, uint32_t actual_timestamp, uint8_t *data,
size_t len, int mute, int32_t timestamp_gap, rtsp_conn_info *conn);
int64_t monotonic_timestamp(uint32_t timestamp,
rtsp_conn_info *conn); // add an epoch to the timestamp. The monotonic
// timestamp guaranteed to start between 2^32 2^33
@@ -441,4 +609,10 @@ int64_t monotonic_timestamp(uint32_t timestamp,
double suggested_volume(rtsp_conn_info *conn); // volume suggested for the connection
const char *get_ssrc_name(ssrc_t ssrc);
int ssrc_is_recognised(ssrc_t ssrc);
int ssrc_is_aac(ssrc_t ssrc); // used to decide if a mute might be needed (AAC only)
void prepare_decoding_chain(rtsp_conn_info *conn, ssrc_t ssrc); // also sets up timing stuff
void clear_decoding_chain(rtsp_conn_info *conn); // tear down the decoding chain
#endif //_PLAYER_H