Merge support of apple's ALAC decoder
This commit is contained in:
@@ -4,6 +4,11 @@ bin_PROGRAMS = shairport-sync
|
||||
shairport_sync_SOURCES = shairport.c rtsp.c mdns.c mdns_external.c common.c rtp.c player.c alac.c audio.c
|
||||
|
||||
AM_CFLAGS = -Wno-multichar -DSYSCONFDIR=\"$(sysconfdir)\"
|
||||
AM_CPPFLAGS = -Wno-multichar -DSYSCONFDIR=\"$(sysconfdir)\"
|
||||
|
||||
if USE_APPLE_ALAC
|
||||
shairport_sync_SOURCES += apple_alac.cpp
|
||||
endif
|
||||
|
||||
if USE_CUSTOMPIDDIR
|
||||
AM_CFLAGS+= \
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <string.h>
|
||||
|
||||
// these are headers for the ALAC decoder, utilities and endian utilities
|
||||
#include <alac/ALACDecoder.h>
|
||||
#include <alac/ALACBitUtilities.h>
|
||||
#include <alac/EndianPortable.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "apple_alac.h"
|
||||
|
||||
typedef struct magicCookie {
|
||||
ALACSpecificConfig config;
|
||||
ALACAudioChannelLayout channelLayoutInfo; // seems to be unused
|
||||
} magicCookie;
|
||||
|
||||
magicCookie cookie;
|
||||
ALACDecoder * theDecoder;
|
||||
|
||||
extern "C" int apple_alac_init(int frame_size,int sample_size,int sample_rate) {
|
||||
|
||||
memset(&cookie,0,sizeof(magicCookie));
|
||||
|
||||
#define CHANNELS_PER_FRAME 2
|
||||
|
||||
//create a magic cookie for the decoder
|
||||
|
||||
cookie.config.frameLength = Swap32NtoB(frame_size);
|
||||
cookie.config.compatibleVersion = 0;
|
||||
cookie.config.bitDepth = sample_size;
|
||||
cookie.config.pb = 40;
|
||||
cookie.config.mb = 10;
|
||||
cookie.config.kb = 14;
|
||||
cookie.config.numChannels = CHANNELS_PER_FRAME;
|
||||
cookie.config.maxRun = Swap16NtoB(255);
|
||||
cookie.config.maxFrameBytes = 0;
|
||||
cookie.config.avgBitRate = 0;
|
||||
cookie.config.sampleRate = Swap32NtoB(sample_rate);
|
||||
|
||||
theDecoder = new ALACDecoder;
|
||||
theDecoder->Init(&cookie, sizeof(magicCookie));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int apple_alac_decode_frame(unsigned char *sampleBuffer, uint32_t bufferLength, unsigned char *dest, int *outsize)
|
||||
{
|
||||
uint32_t numFrames = 0;
|
||||
BitBuffer theInputBuffer;
|
||||
BitBufferInit(&theInputBuffer, sampleBuffer, bufferLength);
|
||||
theDecoder->Decode(&theInputBuffer, dest, Swap32BtoN(cookie.config.frameLength), CHANNELS_PER_FRAME, &numFrames);
|
||||
*outsize = numFrames;
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int apple_alac_terminate() {
|
||||
delete(theDecoder);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef __APPLE_ALAC_H
|
||||
#define __APPLE_ALAC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERNC extern "C"
|
||||
#else
|
||||
#define EXTERNC
|
||||
#endif
|
||||
|
||||
|
||||
EXTERNC int apple_alac_init(int frame_size,int sample_size,int sample_rate);
|
||||
EXTERNC int apple_alac_terminate();
|
||||
EXTERNC int apple_alac_decode_frame(unsigned char *sampleBuffer, uint32_t bufferLength, unsigned char *dest, int *outsize);
|
||||
|
||||
#undef EXTERNC
|
||||
|
||||
#endif /* __APPLE_ALAC_H */
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include <libdaemon/dlog.h>
|
||||
|
||||
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
|
||||
|
||||
@@ -46,8 +46,15 @@ enum stuffing_type {
|
||||
enum playback_mode_type {
|
||||
ST_stereo = 0,
|
||||
ST_mono,
|
||||
ST_reverse_stereo,
|
||||
ST_left_only,
|
||||
ST_right_only,
|
||||
} playback_mode_type;
|
||||
|
||||
enum decoders_supported_type {
|
||||
decoder_hammerton = 0,
|
||||
decoder_apple_alac,
|
||||
} decoders_supported_type;
|
||||
|
||||
// the following enum must _exactly match the snd_pcm_format_t definition in the alsa pcm.h file.
|
||||
|
||||
@@ -112,6 +119,8 @@ typedef struct {
|
||||
int cmd_blocking;
|
||||
int tolerance; // allow this much drift before attempting to correct it
|
||||
enum stuffing_type packet_stuffing;
|
||||
int decoders_supported;
|
||||
int use_apple_decoder; // set to 1 if you want to use the apple decoder instead of the original by David Hammerton
|
||||
char *pidfile;
|
||||
// char *logfile;
|
||||
// char *errfile;
|
||||
|
||||
+18
-1
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.50])
|
||||
AC_INIT([shairport-sync], [3.0PB1], [mikebrady@eircom.net])
|
||||
AC_INIT([shairport-sync], [3.0PB2], [mikebrady@eircom.net])
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_CONFIG_SRCDIR([shairport.c])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -20,6 +20,7 @@ with_os_type=`echo ${with_os_type} | tr '[[:upper:]]' '[[:lower:]]' `
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_INSTALL
|
||||
|
||||
PKG_PROG_PKG_CONFIG([0.9.0])
|
||||
@@ -93,6 +94,22 @@ AC_ARG_WITH([configfiles],
|
||||
[ --with-configfiles = install configuration files during a make install ], ,[with_configfiles=yes])
|
||||
AM_CONDITIONAL([INSTALL_CONFIG_FILES], [test "x$with_configfiles" = "xyes"])
|
||||
|
||||
# Look for Apple ALAC flag
|
||||
AC_ARG_WITH(apple-alac, [ --with-apple-alac = include support for the Apple ALAC decoder],
|
||||
[AC_MSG_RESULT(>>Including the Apple ALAC Decoder)
|
||||
HAS_APPLE_ALAC=1
|
||||
AM_CONDITIONAL([USE_APPLE_ALAC], [test 0])
|
||||
AC_DEFINE([CONFIG_APPLE_ALAC], 1, [Needed by the compiler.])
|
||||
if test "x${with_pkg_config}" = xyes ; then
|
||||
PKG_CHECK_MODULES(
|
||||
[ALAC], [alac],
|
||||
[LIBS="${ALAC_LIBS} ${LIBS}"
|
||||
AC_DEFINE([HAVE_APPLE_ALAC],[1],[Define to 1 if you are using the Apple ALAC Decoder])])
|
||||
else
|
||||
AC_CHECK_LIB([alac], [BitBufferInit], , AC_MSG_ERROR(Apple ALAC Decoder support requires the alac library!))
|
||||
AC_DEFINE([HAVE_APPLE_ALAC],[1],[Define to 1 if you have the Apple ALAC library])
|
||||
fi ])
|
||||
AM_CONDITIONAL([USE_APPLE_ALAC], [test "x$HAS_APPLE_ALAC" = "x1"])
|
||||
|
||||
# Look for piddir flag
|
||||
AC_ARG_WITH(piddir, [ --with-piddir=<pathname> Specify a pathname to a directory in which to write the PID file.], [
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
|
||||
#include "alac.h"
|
||||
|
||||
#include "apple_alac.h"
|
||||
|
||||
// parameters from the source
|
||||
static unsigned char *aesiv;
|
||||
#ifdef HAVE_LIBSSL
|
||||
@@ -100,6 +102,7 @@ static alac_file *decoder_info;
|
||||
static int late_packet_message_sent;
|
||||
static uint64_t packet_count = 0;
|
||||
static int32_t last_seqno_read;
|
||||
static int decoder_in_use = 0;
|
||||
|
||||
// interthread variables
|
||||
static int fix_volume = 0x10000;
|
||||
@@ -240,7 +243,7 @@ static int alac_decode(short *dest, int *destlen, uint8_t *buf, int len) {
|
||||
}
|
||||
unsigned char packet[MAX_PACKET];
|
||||
unsigned char packetp[MAX_PACKET];
|
||||
// assert(len <= MAX_PACKET);
|
||||
assert(len <= MAX_PACKET);
|
||||
int reply = 0; //everything okay
|
||||
int outsize=bytes_per_audio_frame*(*destlen); // the size the output should be, in bytes
|
||||
int toutsize = outsize;
|
||||
@@ -256,15 +259,49 @@ static int alac_decode(short *dest, int *destlen, uint8_t *buf, int len) {
|
||||
AES_cbc_encrypt(buf, packet, aeslen, &aes, iv, AES_DECRYPT);
|
||||
#endif
|
||||
memcpy(packet + aeslen, buf + aeslen, len - aeslen);
|
||||
alac_decode_frame(decoder_info, packet, dest, &outsize);
|
||||
#ifdef HAVE_APPLE_ALAC
|
||||
if (config.use_apple_decoder) {
|
||||
if (decoder_in_use!=1<<decoder_apple_alac) {
|
||||
debug(1,"Apple ALAC Decoder used on encrypted audio.");
|
||||
decoder_in_use=1<<decoder_apple_alac;
|
||||
}
|
||||
apple_alac_decode_frame(packet, len, (unsigned char *) dest, &outsize);
|
||||
outsize=outsize*4; // bring the size to bytes
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (decoder_in_use!=1<<decoder_hammerton) {
|
||||
debug(1,"Hammerton Decoder used on encrypted audio.");
|
||||
decoder_in_use=1<<decoder_hammerton;
|
||||
}
|
||||
alac_decode_frame(decoder_info, packet, (unsigned char *) dest, &outsize);
|
||||
}
|
||||
} else {
|
||||
alac_decode_frame(decoder_info, buf, dest, &outsize);
|
||||
// not encrypted
|
||||
#ifdef HAVE_APPLE_ALAC
|
||||
if (config.use_apple_decoder) {
|
||||
if (decoder_in_use!=1<<decoder_apple_alac) {
|
||||
debug(1,"Apple ALAC Decoder used on unencrypted audio.");
|
||||
decoder_in_use=1<<decoder_apple_alac;
|
||||
}
|
||||
apple_alac_decode_frame(buf, len, (unsigned char *) dest, &outsize);
|
||||
outsize=outsize*4; // bring the size to bytes
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if (decoder_in_use!=1<<decoder_hammerton) {
|
||||
debug(1,"Hammerton Decoder used on unencrypted audio.");
|
||||
decoder_in_use=1<<decoder_hammerton;
|
||||
}
|
||||
alac_decode_frame(decoder_info, buf, dest, &outsize);
|
||||
}
|
||||
}
|
||||
|
||||
if(outsize>toutsize) {
|
||||
debug(2,"Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- truncated, but buffer overflow possible! Encrypted = %d.",outsize, toutsize, encrypted);
|
||||
reply = -1; // output packet is the wrong size
|
||||
}
|
||||
}
|
||||
|
||||
*destlen = outsize / bytes_per_audio_frame;
|
||||
if ((outsize % bytes_per_audio_frame)!=0)
|
||||
debug(1,"Number of audio frames (%d) does not correspond exactly to the number of bytes (%d) and the audio frame size (%d).",*destlen,outsize,bytes_per_audio_frame);
|
||||
@@ -298,10 +335,20 @@ static int init_decoder(int32_t fmtp[12]) {
|
||||
alac->setinfo_86 = fmtp[10];
|
||||
alac->setinfo_8a_rate = fmtp[11];
|
||||
alac_allocate_buffers(alac);
|
||||
|
||||
#ifdef HAVE_APPLE_ALAC
|
||||
apple_alac_init(frame_size,sample_size,sampling_rate);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_decoder(void) { alac_free(decoder_info); }
|
||||
static void terminate_decoders(void) {
|
||||
alac_free(decoder_info);
|
||||
#ifdef HAVE_APPLE_ALAC
|
||||
apple_alac_terminate();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void init_buffer(void) {
|
||||
int i;
|
||||
@@ -506,7 +553,6 @@ static abuf_t *buffer_get_frame(void) {
|
||||
shutdown_requested = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int rco = get_requested_connection_state_to_output();
|
||||
|
||||
if (connection_state_to_output != rco) {
|
||||
@@ -530,7 +576,6 @@ static abuf_t *buffer_get_frame(void) {
|
||||
flush_requested = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&flush_mutex);
|
||||
|
||||
uint32_t flush_limit = 0;
|
||||
if (ab_synced) {
|
||||
do {
|
||||
@@ -1000,7 +1045,7 @@ static void *player_thread_func(void *arg) {
|
||||
|
||||
session_corrections = 0;
|
||||
play_segment_reference_frame = 0; // zero signals that we are not in a play segment
|
||||
|
||||
|
||||
int output_sample_ratio = 1;
|
||||
if (config.output_rate!=0)
|
||||
output_sample_ratio = config.output_rate/44100;
|
||||
@@ -1112,20 +1157,21 @@ static void *player_thread_func(void *arg) {
|
||||
while (!please_stop) {
|
||||
abuf_t *inframe = buffer_get_frame();
|
||||
if (inframe) {
|
||||
if (inframe->data) {
|
||||
inbuf = inframe->data;
|
||||
if (inbuf) {
|
||||
play_number++;
|
||||
// if it's a supplied silent frame, let us know...
|
||||
if (inframe->timestamp == 0) {
|
||||
// debug(1,"Player has a supplied silent frame.");
|
||||
last_seqno_read =
|
||||
(SUCCESSOR(last_seqno_read) & 0xffff); // manage the packet out of sequence minder
|
||||
if (inframe->data==NULL)
|
||||
debug(1,"NULL inframe->data to play -- skipping it.");
|
||||
if (inbuf==NULL)
|
||||
debug(1,"NULL inbuf to play -- skipping it.");
|
||||
else {
|
||||
if (inframe->length==0)
|
||||
debug(1,"empty frame to play -- skipping it.");
|
||||
debug(1,"empty frame to play -- skipping it (1).");
|
||||
else
|
||||
config.output->play(inframe->data, inframe->length);
|
||||
config.output->play(inbuf, inframe->length);
|
||||
}
|
||||
} else {
|
||||
// We have a frame of data. We need to see if we want to add or remove a frame from it to
|
||||
@@ -1279,8 +1325,8 @@ static void *player_thread_func(void *arg) {
|
||||
config.output->play(inframe->data, inframe->length);
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef HAVE_LIBSOXR
|
||||
switch (config.packet_stuffing) {
|
||||
case ST_basic:
|
||||
@@ -1766,7 +1812,7 @@ void player_stop(pthread_t *player_thread) {
|
||||
#endif
|
||||
command_stop();
|
||||
free_buffer();
|
||||
free_decoder();
|
||||
terminate_decoders();
|
||||
int rc = pthread_cond_destroy(&flowcontrol);
|
||||
if (rc)
|
||||
debug(1, "Error destroying condition variable.");
|
||||
|
||||
@@ -1882,7 +1882,7 @@ void rtsp_listen_loop(void) {
|
||||
freeaddrinfo(info);
|
||||
|
||||
if (!nsock)
|
||||
die("Could not establish a service on port %d -- program terminating. Is another Shairport Sync running on this device?",config.port);
|
||||
die("Could not establish a service on port %d -- program terminating. Is another instance of Shairport Sync running on this device?",config.port);
|
||||
|
||||
int maxfd = -1;
|
||||
fd_set fds;
|
||||
|
||||
@@ -62,6 +62,7 @@ alsa =
|
||||
// audio_backend_buffer_desired_length = 6615; // If set too small, buffer underflow occurs on low-powered machines. Too long and the response times with software mixer become annoying.
|
||||
// disable_synchronization = "no"; // Set to "yes" to disable synchronization. Default is "no".
|
||||
// period_size = <number>; // Use this optional advanced setting to set the alsa period size near to this value
|
||||
// buffer_size = <number>; // Use this optional advanced setting to set the alsa buffer size near to this value
|
||||
// use_mmap_if_available = "yes"; // Use this optional advanced setting to control whether MMAP-based output is used to communicate with the DAC. Default is "yes"
|
||||
};
|
||||
|
||||
|
||||
+19
@@ -441,6 +441,19 @@ int parse_options(int argc, char **argv) {
|
||||
config.volume_range_db = value;
|
||||
}
|
||||
|
||||
/* Get the use_apple_decoder setting. */
|
||||
if (config_lookup_string(config.cfg, "general.use_apple_decoder", &str)) {
|
||||
if (strcasecmp(str, "no") == 0)
|
||||
config.use_apple_decoder = 0;
|
||||
else if (strcasecmp(str, "yes") == 0) {
|
||||
if ((config.decoders_supported & 1<<decoder_apple_alac)!=0)
|
||||
config.use_apple_decoder = 1;
|
||||
else
|
||||
inform("Support for the Apple ALAC decoder has not been comiled into this version of Shairport Sync. The default decoder will be used.");
|
||||
} else
|
||||
die("Invalid use_apple_decoder option choice \"%s\". It should be \"yes\" or \"no\"");
|
||||
}
|
||||
|
||||
/* Get the default latency. Deprecated! */
|
||||
if (config_lookup_int(config.cfg, "latencies.default", &value))
|
||||
config.userSuppliedLatency = value;
|
||||
@@ -760,6 +773,10 @@ int main(int argc, char **argv) {
|
||||
config.audio_backend_buffer_desired_length = 6615; // 0.15 seconds.
|
||||
config.udp_port_base = 6001;
|
||||
config.udp_port_range = 100;
|
||||
config.decoders_supported = 1<<decoder_hammerton; // David Hammerton's decoder supported by default
|
||||
#ifdef HAVE_APPLE_ALAC
|
||||
config.decoders_supported += 1<<decoder_apple_alac;
|
||||
#endif
|
||||
|
||||
/* Check if we are called with -V or --version parameter */
|
||||
if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) {
|
||||
@@ -1023,6 +1040,8 @@ int main(int argc, char **argv) {
|
||||
debug(1, "audio backend latency offset is %d.", config.audio_backend_latency_offset);
|
||||
debug(1, "volume range in dB (zero means use the range specified by the mixer): %u.", config.volume_range_db);
|
||||
debug(1, "zeroconf regtype is \"%s\".", config.regtype);
|
||||
debug(1, "decoders_supported field is %d.", config.decoders_supported);
|
||||
debug(1, "use_apple_decoder is %d.", config.use_apple_decoder);
|
||||
|
||||
char *realConfigPath = realpath(config.configfile,NULL);
|
||||
if (realConfigPath) {
|
||||
|
||||
Reference in New Issue
Block a user