From: Mike Brady Date: Sun, 28 Aug 2016 20:38:24 +0000 (+0100) Subject: Merge support of apple's ALAC decoder X-Git-Tag: 3.0.d18~69 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=945483d9c34eaa39b5911fa1501a5741583dee11;p=thirdparty%2Fshairport-sync.git Merge support of apple's ALAC decoder --- diff --git a/Makefile.am b/Makefile.am index 3ad62609..ff677f80 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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+= \ diff --git a/apple_alac.cpp b/apple_alac.cpp new file mode 100644 index 00000000..5c633fb9 --- /dev/null +++ b/apple_alac.cpp @@ -0,0 +1,58 @@ +#include + +// these are headers for the ALAC decoder, utilities and endian utilities +#include +#include +#include + +#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); +} + diff --git a/apple_alac.h b/apple_alac.h new file mode 100644 index 00000000..61eb685c --- /dev/null +++ b/apple_alac.h @@ -0,0 +1,21 @@ +#ifndef __APPLE_ALAC_H +#define __APPLE_ALAC_H + +#include +#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 */ + diff --git a/common.c b/common.c index bf721273..39a936f5 100644 --- a/common.c +++ b/common.c @@ -67,7 +67,6 @@ #endif #endif -#include "common.h" #include // true if Shairport Sync is supposed to be sending output to the output device, false otherwise diff --git a/common.h b/common.h index d67100e7..e2ab47b9 100644 --- a/common.h +++ b/common.h @@ -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; diff --git a/configure.ac b/configure.ac index 5ba84ebc..51fa97d8 100644 --- a/configure.ac +++ b/configure.ac @@ -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= Specify a pathname to a directory in which to write the PID file.], [ diff --git a/player.c b/player.c index da1a312c..7572b899 100644 --- a/player.c +++ b/player.c @@ -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<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."); diff --git a/rtsp.c b/rtsp.c index d2c71497..3f6d9646 100644 --- a/rtsp.c +++ b/rtsp.c @@ -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; diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index bb737ee8..3d7918d0 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -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 = ; // Use this optional advanced setting to set the alsa period size near to this value +// buffer_size = ; // 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" }; diff --git a/shairport.c b/shairport.c index 1b97099b..2ccaa9fb 100644 --- a/shairport.c +++ b/shairport.c @@ -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<= 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) {