diff --git a/Makefile b/Makefile index 93e8dcf..cedf8fb 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,27 @@ # Cross compile support - create a Makefile which defines these three variables and then includes this Makefile... CFLAGS ?= -Wall -fPIC -O2 $(OPTS) -LDFLAGS ?= -lasound -lpthread -lm -ldl -lrt +LDFLAGS ?= -lasound -lpthread -lm -lrt EXECUTABLE ?= squeezelite SOURCES = main.c slimproto.c utils.c output.c buffer.c stream.c decode.c process.c resample.c flac.c pcm.c mad.c vorbis.c faad.c mpg.c ffmpeg.c DEPS = squeezelite.h slimproto.h + +UNAME = $(shell uname -s) + +ifneq (,$(findstring -DLINKALL, $(CFLAGS))) + LDFLAGS += -lFLAC -lmad -lvorbisfile -lfaad -lmpg123 +ifneq (,$(findstring -DFFMPEG, $(CFLAGS))) + LDFLAGS += -lavcodec -lavformat -lavutil +endif +ifneq (,$(findstring -DRESAMPLE, $(CFLAGS))) + LDFLAGS += -lsoxr +endif +else +# if not LINKALL and linux add -ldl +ifeq ($(UNAME), Linux) + LDFLAGS += -ldl +endif +endif OBJECTS = $(SOURCES:.c=.o) diff --git a/faad.c b/faad.c index 639f668..c0ed8ff 100644 --- a/faad.c +++ b/faad.c @@ -43,6 +43,7 @@ bool empty; struct chunk_table *chunkinfo; // faad symbols to be dynamically loaded +#if !LINKALL NeAACDecConfigurationPtr (* NeAACDecGetCurrentConfiguration)(NeAACDecHandle); unsigned char (* NeAACDecSetConfiguration)(NeAACDecHandle, NeAACDecConfigurationPtr); NeAACDecHandle (* NeAACDecOpen)(void); @@ -51,6 +52,7 @@ char (* NeAACDecInit2)(NeAACDecHandle, unsigned char *pBuffer, unsigned long, unsigned long *, unsigned char *); void *(* NeAACDecDecode)(NeAACDecHandle, NeAACDecFrameInfo *, unsigned char *, unsigned long); char *(* NeAACDecGetErrorMessage)(unsigned char); +#endif }; static struct faad *a; @@ -80,6 +82,12 @@ #define IF_PROCESS(x) #endif +#if LINKALL +#define NEAAC(h, fn, ...) (NeAACDec ## fn)(__VA_ARGS__) +#else +#define NEAAC(h, fn, ...) (h)->NeAACDec##fn(__VA_ARGS__) +#endif + // minimal code for mp4 file parsing to extract audio config and find media data // adapted from faad2/common/mp4ff @@ -138,7 +146,7 @@ return -1; } config_len = mp4_desc_length(&ptr); - if (a->NeAACDecInit2(a->hAac, ptr, config_len, samplerate_p, channels_p) == 0) { + if (NEAAC(a, Init2, a->hAac, ptr, config_len, samplerate_p, channels_p) == 0) { LOG_DEBUG("playable aac track: %u", trak); play = trak; } @@ -344,7 +352,7 @@ } if (bytes_wrap >= 2) { - long n = a->NeAACDecInit(a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels); + long n = NEAAC(a, Init, a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels); if (n < 0) { found = -1; } else { @@ -394,15 +402,15 @@ memcpy(buf, streambuf->readp, bytes_wrap); memcpy(buf + bytes_wrap, streambuf->buf, WRAPBUF_LEN - bytes_wrap); - iptr = a->NeAACDecDecode(a->hAac, &info, buf, WRAPBUF_LEN); + iptr = NEAAC(a, Decode, a->hAac, &info, buf, WRAPBUF_LEN); } else { - iptr = a->NeAACDecDecode(a->hAac, &info, streambuf->readp, bytes_wrap); + iptr = NEAAC(a, Decode, a->hAac, &info, streambuf->readp, bytes_wrap); } if (info.error) { - LOG_WARN("error: %u %s", info.error, a->NeAACDecGetErrorMessage(info.error)); + LOG_WARN("error: %u %s", info.error, NEAAC(a, GetErrorMessage, info.error)); } endstream = false; @@ -547,22 +555,22 @@ a->empty = false; if (a->hAac) { - a->NeAACDecClose(a->hAac); - } - a->hAac = a->NeAACDecOpen(); - - conf = a->NeAACDecGetCurrentConfiguration(a->hAac); + NEAAC(a, Close, a->hAac); + } + a->hAac = NEAAC(a, Open); + + conf = NEAAC(a, GetCurrentConfiguration, a->hAac); conf->outputFormat = FAAD_FMT_24BIT; conf->downMatrix = 1; - if (!a->NeAACDecSetConfiguration(a->hAac, conf)) { + if (!NEAAC(a, SetConfiguration, a->hAac, conf)) { LOG_WARN("error setting config"); }; } static void faad_close(void) { - a->NeAACDecClose(a->hAac); + NEAAC(a, Close, a->hAac); a->hAac = NULL; if (a->chunkinfo) { free(a->chunkinfo); @@ -575,6 +583,7 @@ } static bool load_faad() { +#if !LINKALL void *handle = dlopen(LIBFAAD, RTLD_NOW); char *err; @@ -583,11 +592,6 @@ return false; } - a = malloc(sizeof(struct faad)); - - a->hAac = NULL; - a->chunkinfo = NULL; - a->stsc = NULL; a->NeAACDecGetCurrentConfiguration = dlsym(handle, "NeAACDecGetCurrentConfiguration"); a->NeAACDecSetConfiguration = dlsym(handle, "NeAACDecSetConfiguration"); a->NeAACDecOpen = dlsym(handle, "NeAACDecOpen"); @@ -603,6 +607,8 @@ } LOG_INFO("loaded "LIBFAAD""); +#endif + return true; } @@ -617,6 +623,15 @@ faad_decode, // decode }; + a = malloc(sizeof(struct faad)); + if (!a) { + return NULL; + } + + a->hAac = NULL; + a->chunkinfo = NULL; + a->stsc = NULL; + if (!load_faad()) { return NULL; } diff --git a/ffmpeg.c b/ffmpeg.c index be49ff9..588b54c 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -47,14 +47,13 @@ unsigned mmsh_bytes_left; unsigned mmsh_bytes_pad; unsigned mmsh_packet_len; - // library versions - unsigned avcodec_v, avformat_v, avutil_v; +#if !LINKALL // ffmpeg symbols to be dynamically loaded from libavcodec unsigned (* avcodec_version)(void); AVCodec * (* avcodec_find_decoder)(int); int attribute_align_arg (* avcodec_open2)(AVCodecContext *, const AVCodec *, AVDictionary **); AVFrame * (* avcodec_alloc_frame)(void); - void (* avcodec_free_frame)(AVFrame *); + void (* avcodec_free_frame)(AVFrame **); int attribute_align_arg (* avcodec_decode_audio4)(AVCodecContext *, AVFrame *, int *, const AVPacket *); // ffmpeg symbols to be dynamically loaded from libavformat unsigned (* avformat_version)(void); @@ -75,7 +74,8 @@ void (* av_log_set_level)(int); int (* av_strerror)(int, char *, size_t); void * (* av_malloc)(size_t); - void (* av_free)(void *); + void (* av_freep)(void *); +#endif }; static struct ff_s *ff; @@ -105,10 +105,23 @@ #define IF_PROCESS(x) #endif +#if LINKALL +#define AV(h, fn, ...) (av_ ## fn)(__VA_ARGS__) +#define AVIO(h, fn, ...) (avio_ ## fn)(__VA_ARGS__) +#define AVCODEC(h, fn, ...) (avcodec_ ## fn)(__VA_ARGS__) +#define AVFORMAT(h, fn, ...) (avformat_ ## fn)(__VA_ARGS__) +#else +#define AV(h, fn, ...) (h)->av_##fn(__VA_ARGS__) +#define AVIO(h, fn, ...) (h)->avio_##fn(__VA_ARGS__) +#define AVCODEC(h, fn, ...) (h)->avcodec_##fn(__VA_ARGS__) +#define AVFORMAT(h, fn, ...) (h)->avformat_##fn(__VA_ARGS__) +#endif + + // our own version of useful error function not included in earlier ffmpeg versions static char *av__err2str(errnum) { static char buf[64]; - ff->av_strerror(errnum, buf, 64); + AV(ff, strerror, errnum, buf, 64); return buf; } @@ -251,13 +264,13 @@ ff->mmsh_bytes_left = ff->mmsh_bytes_pad = ff->mmsh_packet_len = 0; if (!ff->readbuf) { - ff->readbuf = ff->av_malloc(READ_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); - } - - avio = ff->avio_alloc_context(ff->readbuf, READ_SIZE, 0, NULL, _read_data, NULL, NULL); + ff->readbuf = AV(ff, malloc, READ_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + } + + avio = AVIO(ff, alloc_context, ff->readbuf, READ_SIZE, 0, NULL, _read_data, NULL, NULL); avio->seekable = 0; - ff->formatC = ff->avformat_alloc_context(); + ff->formatC = AVFORMAT(ff, alloc_context); if (ff->formatC == NULL) { LOG_ERROR("null context"); return DECODE_ERROR; @@ -266,7 +279,7 @@ ff->formatC->pb = avio; ff->formatC->flags |= AVFMT_FLAG_CUSTOM_IO | AVFMT_FLAG_NOPARSE; - o = ff->avformat_open_input(&ff->formatC, "", ff->input_format, NULL); + o = AVFORMAT(ff, open_input, &ff->formatC, "", ff->input_format, NULL); if (o < 0) { LOG_WARN("avformat_open_input: %d %s", o, av__err2str(o)); return DECODE_ERROR; @@ -274,7 +287,7 @@ LOG_INFO("format: name:%s lname:%s", ff->formatC->iformat->name, ff->formatC->iformat->long_name); - o = ff->avformat_find_stream_info(ff->formatC, NULL); + o = AVFORMAT(ff, find_stream_info, ff->formatC, NULL); if (o < 0) { LOG_WARN("avformat_find_stream_info: %d %s", o, av__err2str(o)); return DECODE_ERROR; @@ -307,19 +320,19 @@ ff->codecC = av_stream->codec; - codec = ff->avcodec_find_decoder(ff->codecC->codec_id); - - ff->avcodec_open2(ff->codecC, codec, NULL); - - ff->frame = ff->avcodec_alloc_frame(); - - ff->avpkt = ff->av_malloc(sizeof(AVPacket)); + codec = AVCODEC(ff, find_decoder, ff->codecC->codec_id); + + AVCODEC(ff, open2, ff->codecC, codec, NULL); + + ff->frame = AVCODEC(ff, alloc_frame); + + ff->avpkt = AV(ff, malloc, sizeof(AVPacket)); if (ff->avpkt == NULL) { LOG_ERROR("can't allocate avpkt"); return DECODE_ERROR; } - ff->av_init_packet(ff->avpkt); + AV(ff, init_packet, ff->avpkt); ff->avpkt->data = NULL; ff->avpkt->size = 0; @@ -334,7 +347,7 @@ got_frame = 0; - if ((r = ff->av_read_frame(ff->formatC, ff->avpkt)) < 0) { + if ((r = AV(ff, read_frame, ff->formatC, ff->avpkt)) < 0) { if (r == AVERROR_EOF) { if (ff->end_of_stream) { LOG_INFO("decode complete"); @@ -358,7 +371,7 @@ while (pkt_c.size > 0 || got_frame) { - len = ff->avcodec_decode_audio4(ff->codecC, ff->frame, &got_frame, &pkt_c); + len = AVCODEC(ff, decode_audio4, ff->codecC, ff->frame, &got_frame, &pkt_c); if (len < 0) { LOG_ERROR("avcodec_decode_audio4 error: %i %s", len, av__err2str(len)); return DECODE_RUNNING; @@ -491,27 +504,33 @@ } } - ff->av_free_packet(ff->avpkt); + AV(ff, free_packet, ff->avpkt); return DECODE_RUNNING; } static void _free_ff_data(void) { if (ff->formatC) { - if (ff->formatC->pb) ff->av_free(ff->formatC->pb); - ff->avformat_free_context(ff->formatC); + if (ff->formatC->pb) AV(ff, freep, &ff->formatC->pb); + AVFORMAT(ff, free_context, ff->formatC); ff->formatC = NULL; } if (ff->frame) { // ffmpeg version dependant free function - ff->avcodec_free_frame ? ff->avcodec_free_frame(ff->frame) : ff->av_free(ff->frame); +#if !LINKALL + ff->avcodec_free_frame ? AVCODEC(ff, free_frame, &ff->frame) : AV(ff, freep, &ff->frame); +#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,28,0) + AVCODEC(ff, free_frame, &ff->frame); +#else + AV(ff, freep, &ff->frame); +#endif ff->frame = NULL; } if (ff->avpkt) { - ff->av_free_packet(ff->avpkt); - ff->av_free(ff->avpkt); + AV(ff, free_packet, ff->avpkt); + AV(ff, freep, &ff->avpkt); ff->avpkt = NULL; } } @@ -519,7 +538,7 @@ static void ff_open_wma(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { _free_ff_data(); - ff->input_format = ff->av_find_input_format("asf"); + ff->input_format = AV(ff, find_input_format, "asf"); if (ff->input_format == NULL) { LOG_ERROR("asf format not supported by ffmpeg library"); } @@ -535,7 +554,7 @@ static void ff_open_alac(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { _free_ff_data(); - ff->input_format = ff->av_find_input_format("mp4"); + ff->input_format = AV(ff, find_input_format, "mp4"); if (ff->input_format == NULL) { LOG_ERROR("mp4 format not supported by ffmpeg library"); } @@ -550,12 +569,13 @@ _free_ff_data(); if (ff->readbuf) { - ff->av_free(ff->readbuf); + AV(ff, freep, &ff->readbuf); ff->readbuf = NULL; } } static bool load_ff() { +#if !LINKALL void *handle_codec = NULL, *handle_format = NULL, *handle_util = NULL; char name[30]; char *err; @@ -582,9 +602,6 @@ LOG_INFO("dlerror: %s", dlerror()); return false; } - - ff = malloc(sizeof(struct ff_s)); - memset(ff, 0, sizeof(struct ff_s)); ff->avcodec_version = dlsym(handle_codec, "avcodec_version"); ff->avcodec_find_decoder = dlsym(handle_codec, "avcodec_find_decoder"); @@ -600,8 +617,7 @@ return false; } - ff->avcodec_v = ff->avcodec_version(); - LOG_INFO("loaded "LIBAVCODEC" (%u.%u.%u)", LIBAVCODEC_VERSION_MAJOR, ff->avcodec_v >> 16, (ff->avcodec_v >> 8) & 0xff, ff->avcodec_v & 0xff); + LOG_INFO("loaded "LIBAVCODEC" (%u.%u.%u)", LIBAVCODEC_VERSION_MAJOR, ff->avcodec_version() >> 16, (ff->avcodec_version() >> 8) & 0xff, ff->avcodec_version() & 0xff); ff->avformat_version = dlsym(handle_format, "avformat_version"); ff->avformat_alloc_context = dlsym(handle_format, "avformat_alloc_context"); @@ -618,23 +634,23 @@ return false; } - ff->avformat_v = ff->avformat_version(); - LOG_INFO("loaded "LIBAVFORMAT" (%u.%u.%u)", LIBAVFORMAT_VERSION_MAJOR, ff->avformat_v >> 16, (ff->avformat_v >> 8) & 0xff, ff->avformat_v & 0xff); + LOG_INFO("loaded "LIBAVFORMAT" (%u.%u.%u)", LIBAVFORMAT_VERSION_MAJOR, ff->avformat_version() >> 16, (ff->avformat_version() >> 8) & 0xff, ff->avformat_version() & 0xff); ff->avutil_version = dlsym(handle_util, "avutil_version"); ff->av_log_set_callback = dlsym(handle_util, "av_log_set_callback"); ff->av_log_set_level = dlsym(handle_util, "av_log_set_level"); ff->av_strerror = dlsym(handle_util, "av_strerror"); ff->av_malloc = dlsym(handle_util, "av_malloc"); - ff->av_free = dlsym(handle_util, "av_free"); + ff->av_freep = dlsym(handle_util, "av_freep"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } - ff->avutil_v = ff->avutil_version(); - LOG_INFO("loaded "LIBAVUTIL" (%u.%u.%u)", LIBAVUTIL_VERSION_MAJOR, ff->avutil_v >> 16, (ff->avutil_v >> 8) & 0xff, ff->avutil_v & 0xff); + LOG_INFO("loaded "LIBAVUTIL" (%u.%u.%u)", LIBAVUTIL_VERSION_MAJOR, ff->avutil_version() >> 16, (ff->avutil_version() >> 8) & 0xff, ff->avutil_version() & 0xff); + +#endif return true; } @@ -652,6 +668,13 @@ struct codec *register_ff(const char *codec) { if (!registered) { + + ff = malloc(sizeof(struct ff_s)); + if (!ff) { + return NULL; + } + + memset(ff, 0, sizeof(struct ff_s)); if (!load_ff()) { return NULL; @@ -669,9 +692,9 @@ default: break; } - ff->av_log_set_callback(av_err_callback); - - ff->av_register_all(); + AV(ff, log_set_callback, av_err_callback); + + AV(ff, register_all); registered = true; } diff --git a/flac.c b/flac.c index 5d22bfe..84884cd 100644 --- a/flac.c +++ b/flac.c @@ -24,6 +24,7 @@ struct flac { FLAC__StreamDecoder *decoder; +#if !LINKALL // FLAC symbols to be dynamically loaded const char **FLAC__StreamDecoderErrorStatusString; const char **FLAC__StreamDecoderStateString; @@ -44,6 +45,7 @@ ); FLAC__bool (* FLAC__stream_decoder_process_single)(FLAC__StreamDecoder *decoder); FLAC__StreamDecoderState (* FLAC__stream_decoder_get_state)(const FLAC__StreamDecoder *decoder); +#endif }; static struct flac *f; @@ -73,6 +75,14 @@ #define IF_PROCESS(x) #endif +#if LINKALL +#define FLAC(h, fn, ...) (FLAC__ ## fn)(__VA_ARGS__) +#define FLAC_A(h, a) (FLAC__ ## a) +#else +#define FLAC(h, fn, ...) (h)->FLAC__##fn(__VA_ARGS__) +#define FLAC_A(h, a) (h)->FLAC__ ## a +#endif + static FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *want, void *client_data) { size_t bytes; bool end; @@ -172,29 +182,29 @@ } static void error_cb(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { - LOG_INFO("flac error: %s", f->FLAC__StreamDecoderErrorStatusString[status]); + LOG_INFO("flac error: %s", FLAC_A(f, StreamDecoderErrorStatusString)[status]); } static void flac_open(u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness) { if (f->decoder) { - f->FLAC__stream_decoder_reset(f->decoder); + FLAC(f, stream_decoder_reset, f->decoder); } else { - f->decoder = f->FLAC__stream_decoder_new(); - } - f->FLAC__stream_decoder_init_stream(f->decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL); + f->decoder = FLAC(f, stream_decoder_new); + } + FLAC(f, stream_decoder_init_stream, f->decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL); } static void flac_close(void) { - f->FLAC__stream_decoder_delete(f->decoder); + FLAC(f, stream_decoder_delete, f->decoder); f->decoder = NULL; } static decode_state flac_decode(void) { - bool ok = f->FLAC__stream_decoder_process_single(f->decoder); - FLAC__StreamDecoderState state = f->FLAC__stream_decoder_get_state(f->decoder); + bool ok = FLAC(f, stream_decoder_process_single, f->decoder); + FLAC__StreamDecoderState state = FLAC(f, stream_decoder_get_state, f->decoder); if (!ok && state != FLAC__STREAM_DECODER_END_OF_STREAM) { - LOG_INFO("flac error: %s", f->FLAC__StreamDecoderStateString[state]); + LOG_INFO("flac error: %s", FLAC_A(f, StreamDecoderStateString)[state]); }; if (state == FLAC__STREAM_DECODER_END_OF_STREAM) { @@ -207,6 +217,7 @@ } static bool load_flac() { +#if !LINKALL void *handle = dlopen(LIBFLAC, RTLD_NOW); char *err; @@ -215,9 +226,6 @@ return false; } - f = malloc(sizeof(struct flac)); - - f->decoder = NULL; f->FLAC__StreamDecoderErrorStatusString = dlsym(handle, "FLAC__StreamDecoderErrorStatusString"); f->FLAC__StreamDecoderStateString = dlsym(handle, "FLAC__StreamDecoderStateString"); f->FLAC__stream_decoder_new = dlsym(handle, "FLAC__stream_decoder_new"); @@ -233,6 +241,8 @@ } LOG_INFO("loaded "LIBFLAC); +#endif + return true; } @@ -247,6 +257,13 @@ flac_decode, // decode }; + f = malloc(sizeof(struct flac)); + if (!f) { + return NULL; + } + + f->decoder = NULL; + if (!load_flac()) { return NULL; } diff --git a/mad.c b/mad.c index aef625a..ba321fc 100644 --- a/mad.c +++ b/mad.c @@ -38,6 +38,7 @@ u32_t skip; u64_t samples; u32_t padding; +#if !LINKALL // mad symbols to be dynamically loaded void (* mad_stream_init)(struct mad_stream *); void (* mad_frame_init)(struct mad_frame *); @@ -48,6 +49,7 @@ int (* mad_frame_decode)(struct mad_frame *, struct mad_stream *); void (* mad_synth_frame)(struct mad_synth *, struct mad_frame const *); char const *(* mad_stream_errorstr)(struct mad_stream const *); +#endif }; static struct mad *m; @@ -77,6 +79,12 @@ #define IF_PROCESS(x) #endif +#if LINKALL +#define MAD(h, fn, ...) (mad_ ## fn)(__VA_ARGS__) +#else +#define MAD(h, fn, ...) (h)->mad_##fn(__VA_ARGS__) +#endif + // based on libmad minimad.c scale static inline u32_t scale(mad_fixed_t sample) { sample += (1L << (MAD_F_FRACBITS - 24)); @@ -168,7 +176,7 @@ UNLOCK_S; - m->mad_stream_buffer(&m->stream, m->readbuf, m->readbuf_len); + MAD(m, stream_buffer, &m->stream, m->readbuf, m->readbuf_len); while (true) { size_t frames; @@ -176,19 +184,19 @@ s32_t *iptrr; unsigned max_frames; - if (m->mad_frame_decode(&m->frame, &m->stream) == -1) { + if (MAD(m, frame_decode, &m->frame, &m->stream) == -1) { decode_state ret; if (!eos && m->stream.error == MAD_ERROR_BUFLEN) { ret = DECODE_RUNNING; } else if (eos && (m->stream.error == MAD_ERROR_BUFLEN || m->stream.error == MAD_ERROR_LOSTSYNC)) { ret = DECODE_COMPLETE; } else if (!MAD_RECOVERABLE(m->stream.error)) { - LOG_INFO("mad_frame_decode error: %s - stopping decoder", m->mad_stream_errorstr(&m->stream)); + LOG_INFO("mad_frame_decode error: %s - stopping decoder", MAD(m, stream_errorstr, &m->stream)); ret = DECODE_COMPLETE; } else { if (m->stream.error != m->last_error) { // suppress repeat error messages - LOG_DEBUG("mad_frame_decode error: %s", m->mad_stream_errorstr(&m->stream)); + LOG_DEBUG("mad_frame_decode error: %s", MAD(m, stream_errorstr, &m->stream)); } ret = DECODE_RUNNING; } @@ -196,7 +204,7 @@ return ret; }; - m->mad_synth_frame(&m->synth, &m->frame); + MAD(m, synth_frame, &m->synth, &m->frame); if (decode.new_stream) { LOCK_O; @@ -301,20 +309,21 @@ m->samples = 0; m->readbuf_len = 0; m->last_error = MAD_ERROR_NONE; - m->mad_stream_init(&m->stream); - m->mad_frame_init(&m->frame); - m->mad_synth_init(&m->synth); + MAD(m, stream_init, &m->stream); + MAD(m, frame_init, &m->frame); + MAD(m, synth_init, &m->synth); } static void mad_close(void) { - mad_synth_finish(&m->synth); - m->mad_frame_finish(&m->frame); - m->mad_stream_finish(&m->stream); + mad_synth_finish(&m->synth); // macro only in current version + MAD(m, frame_finish, &m->frame); + MAD(m, stream_finish, &m->stream); free(m->readbuf); m->readbuf = NULL; } static bool load_mad() { +#if !LINKALL void *handle = dlopen(LIBMAD, RTLD_NOW); char *err; @@ -323,10 +332,6 @@ return false; } - m = malloc(sizeof(struct mad)); - - m->readbuf = NULL; - m->readbuf_len = 0; m->mad_stream_init = dlsym(handle, "mad_stream_init"); m->mad_frame_init = dlsym(handle, "mad_frame_init"); m->mad_synth_init = dlsym(handle, "mad_synth_init"); @@ -343,6 +348,8 @@ } LOG_INFO("loaded "LIBMAD); +#endif + return true; } @@ -357,6 +364,14 @@ mad_decode, // decode }; + m = malloc(sizeof(struct mad)); + if (!m) { + return NULL; + } + + m->readbuf = NULL; + m->readbuf_len = 0; + if (!load_mad()) { return NULL; } diff --git a/mpg.c b/mpg.c index 4142282..4f3f6e6 100644 --- a/mpg.c +++ b/mpg.c @@ -28,6 +28,7 @@ struct mpg { mpg123_handle *h; bool use16bit; +#if !LINKALL // mpg symbols to be dynamically loaded int (* mpg123_init)(void); int (* mpg123_feature)(const enum mpg123_feature_set); @@ -40,6 +41,7 @@ int (* mpg123_decode)(mpg123_handle *, const unsigned char *, size_t, unsigned char *, size_t, size_t *); int (* mpg123_getformat)(mpg123_handle *, long *, int *, int *); const char* (* mpg123_plain_strerror)(int); +#endif }; static struct mpg *m; @@ -73,6 +75,12 @@ #define IF_PROCESS(x) #endif +#if LINKALL +#define MPG123(h, fn, ...) (mpg123_ ## fn)(__VA_ARGS__) +#else +#define MPG123(h, fn, ...) (h)->mpg123_##fn(__VA_ARGS__) +#endif + static decode_state mpg_decode(void) { size_t bytes, space, size; int ret; @@ -109,7 +117,7 @@ space = 0; } - ret = m->mpg123_decode(m->h, streambuf->readp, bytes, write_buf, space, &size); + ret = MPG123(m, decode, m->h, streambuf->readp, bytes, write_buf, space, &size); if (ret == MPG123_NEW_FORMAT) { @@ -117,7 +125,7 @@ long rate; int channels, enc; - m->mpg123_getformat(m->h, &rate, &channels, &enc); + MPG123(m, getformat, m->h, &rate, &channels, &enc); LOG_INFO("setting track_start"); LOCK_O_not_direct; @@ -179,35 +187,36 @@ size_t count, i; if (m->h) { - m->mpg123_delete(m->h); - } - - m->h = m->mpg123_new(NULL, &err); + MPG123(m, delete, m->h); + } + + m->h = MPG123(m, new, NULL, &err); if (m->h == NULL) { - LOG_WARN("new error: %s", m->mpg123_plain_strerror(err)); + LOG_WARN("new error: %s", MPG123(m, plain_strerror, err)); } // restrict output to 32bit or 16bit signed 2 channel based on library capability - m->mpg123_rates(&list, &count); - m->mpg123_format_none(m->h); + MPG123(m, rates, &list, &count); + MPG123(m, format_none, m->h); for (i = 0; i < count; i++) { - m->mpg123_format(m->h, list[i], 2, m->use16bit ? MPG123_ENC_SIGNED_16 : MPG123_ENC_SIGNED_32); - } - - err = m->mpg123_open_feed(m->h); + MPG123(m, format, m->h, list[i], 2, m->use16bit ? MPG123_ENC_SIGNED_16 : MPG123_ENC_SIGNED_32); + } + + err = MPG123(m, open_feed, m->h); if (err) { - LOG_WARN("open feed error: %s", m->mpg123_plain_strerror(err)); + LOG_WARN("open feed error: %s", MPG123(m, plain_strerror, err)); } } static void mpg_close(void) { - m->mpg123_delete(m->h); + MPG123(m, delete, m->h); m->h = NULL; } static bool load_mpg() { +#if !LINKALL void *handle = dlopen(LIBMPG, RTLD_NOW); char *err; @@ -216,9 +225,6 @@ return false; } - m = malloc(sizeof(struct mpg)); - - m->h = NULL; m->mpg123_init = dlsym(handle, "mpg123_init"); m->mpg123_feature = dlsym(handle, "mpg123_feature"); m->mpg123_rates = dlsym(handle, "mpg123_rates"); @@ -236,11 +242,9 @@ return false; } - m->mpg123_init(); - - m->use16bit = !m->mpg123_feature(MPG123_FEATURE_OUTPUT_32BIT); - LOG_INFO("loaded "LIBMPG); +#endif + return true; } @@ -255,9 +259,20 @@ mpg_decode, // decode }; + m = malloc(sizeof(struct mpg)); + if (!m) { + return NULL; + } + + m->h = NULL; + if (!load_mpg()) { return NULL; } + MPG123(m, init); + + m->use16bit = MPG123(m, feature, MPG123_FEATURE_OUTPUT_32BIT); + return &ret; } diff --git a/resample.c b/resample.c index 7f1c95f..fa1c498 100644 --- a/resample.c +++ b/resample.c @@ -27,16 +27,6 @@ #include #include -#if LINUX -#define LIBSOXR "libsoxr.so.0" -#endif -#if OSX -#define LIBSOXR "libsoxr.0.dylib" -#endif -#if WIN -#define LIBSOXR "libsoxr.dll" -#endif - extern log_level loglevel; struct soxr { @@ -49,6 +39,8 @@ double q_passband_end; /* 0dB pt. bandwidth to preserve; nyquist=1 0.913 */ double q_stopband_begin; /* Aliasing/imaging control; > passband_end 1 */ double scale; + bool max_rate; +#if !LINKALL // soxr symbols to be dynamically loaded soxr_io_spec_t (* soxr_io_spec)(soxr_datatype_t itype, soxr_datatype_t otype); soxr_quality_spec_t (* soxr_quality_spec)(unsigned long recipe, unsigned long flags); @@ -57,10 +49,17 @@ void (* soxr_delete)(soxr_t); soxr_error_t (* soxr_process)(soxr_t, soxr_in_t, size_t, size_t *, soxr_out_t, size_t olen, size_t *); size_t *(* soxr_num_clips)(soxr_t); - bool max_rate; + // soxr_strerror is a macro so not included here +#endif }; static struct soxr *r; + +#if LINKALL +#define SOXR(h, fn, ...) (soxr_ ## fn)(__VA_ARGS__) +#else +#define SOXR(h, fn, ...) (h)->soxr_##fn(__VA_ARGS__) +#endif void resample_samples(struct processstate *process) { @@ -68,7 +67,7 @@ size_t clip_cnt; soxr_error_t error = - r->soxr_process(r->resampler, process->inbuf, process->in_frames, &idone, process->outbuf, process->max_out_frames, &odone); + SOXR(r, process, r->resampler, process->inbuf, process->in_frames, &idone, process->outbuf, process->max_out_frames, &odone); if (error) { LOG_INFO("soxr_process error: %s", soxr_strerror(error)); } @@ -83,7 +82,7 @@ process->total_in += idone; process->total_out += odone; - clip_cnt = *(r->soxr_num_clips(r->resampler)); + clip_cnt = *(SOXR(r, num_clips, r->resampler)); if (clip_cnt - r->old_clips) { LOG_DEBUG("resampling clips: %u", (unsigned)(clip_cnt - r->old_clips)); r->old_clips = clip_cnt; @@ -94,7 +93,7 @@ size_t odone; size_t clip_cnt; - soxr_error_t error = r->soxr_process(r->resampler, NULL, 0, NULL, process->outbuf, process->max_out_frames, &odone); + soxr_error_t error = SOXR(r, process, r->resampler, NULL, 0, NULL, process->outbuf, process->max_out_frames, &odone); if (error) { LOG_INFO("soxr_process error: %s", soxr_strerror(error)); } @@ -102,7 +101,7 @@ process->out_frames = odone; process->total_out += odone; - clip_cnt = *(r->soxr_num_clips(r->resampler)); + clip_cnt = *(SOXR(r, num_clips, r->resampler)); if (clip_cnt - r->old_clips) { LOG_DEBUG("resampling clips: %u", (unsigned)(clip_cnt - r->old_clips)); r->old_clips = clip_cnt; @@ -112,7 +111,7 @@ LOG_INFO("resample track complete - total track clips: %u", r->old_clips); - r->soxr_delete(r->resampler); + SOXR(r, delete, r->resampler); r->resampler = NULL; return true; @@ -138,7 +137,7 @@ process->out_sample_rate = outrate; if (r->resampler) { - r->soxr_delete(r->resampler); + SOXR(r, delete, r->resampler); r->resampler = NULL; } @@ -150,10 +149,10 @@ LOG_INFO("resampling from %u -> %u", raw_sample_rate, outrate); - io_spec = r->soxr_io_spec(SOXR_INT32_I, SOXR_INT32_I); + io_spec = SOXR(r, io_spec, SOXR_INT32_I, SOXR_INT32_I); io_spec.scale = r->scale; - q_spec = r->soxr_quality_spec(r->q_recipe, r->q_flags); + q_spec = SOXR(r, quality_spec, r->q_recipe, r->q_flags); if (r->q_precision > 0) { q_spec.precision = r->q_precision; } @@ -171,7 +170,7 @@ "phase_response: %03.1f, flags: 0x%02x], soxr_io_spec_t[scale: %03.2f]", q_spec.precision, q_spec.passband_end, q_spec.stopband_begin, q_spec.phase_response, q_spec.flags, io_spec.scale); - r->resampler = r->soxr_create(raw_sample_rate, outrate, 2, &error, &io_spec, &q_spec, NULL); + r->resampler = SOXR(r, create, raw_sample_rate, outrate, 2, &error, &io_spec, &q_spec, NULL); if (error) { LOG_INFO("soxr_create error: %s", soxr_strerror(error)); return false; @@ -189,12 +188,13 @@ void resample_flush(void) { if (r->resampler) { - r->soxr_delete(r->resampler); + SOXR(r, delete, r->resampler); r->resampler = NULL; } } static bool load_soxr(void) { +#if !LINKALL void *handle = dlopen(LIBSOXR, RTLD_NOW); char *err; @@ -203,11 +203,6 @@ return false; } - r = malloc(sizeof(struct soxr)); - - r->resampler = NULL; - r->old_clips = 0; - r->max_rate = false; r->soxr_io_spec = dlsym(handle, "soxr_io_spec"); r->soxr_quality_spec = dlsym(handle, "soxr_quality_spec"); r->soxr_create = dlsym(handle, "soxr_create"); @@ -221,6 +216,8 @@ } LOG_INFO("loaded "LIBSOXR); +#endif + return true; } @@ -228,6 +225,16 @@ char *recipe = NULL, *flags = NULL; char *atten = NULL; char *precision = NULL, *passband_end = NULL, *stopband_begin = NULL, *phase_response = NULL; + + r = malloc(sizeof(struct soxr)); + if (!r) { + LOG_WARN("resampling disabled"); + return false; + } + + r->resampler = NULL; + r->old_clips = 0; + r->max_rate = false; if (!load_soxr()) { LOG_WARN("resampling disabled"); diff --git a/squeezelite.h b/squeezelite.h index 0c75714..13c6fea 100644 --- a/squeezelite.h +++ b/squeezelite.h @@ -20,7 +20,7 @@ // make may define: PORTAUDIO, SELFPIPE or RESAMPLE to influence build -#define VERSION "v1.3" +#define VERSION "v1.3.1" // build detection #if defined(linux) @@ -76,7 +76,6 @@ #undef FFMPEG #define FFMPEG 1 #else -#undef FFMPEG #define FFMPEG 0 #endif @@ -84,11 +83,20 @@ #undef VISEXPORT #define VISEXPORT 1 // visulizer export support uses linux shared memory #else -#undef VISEXPORT #define VISEXPORT 0 #endif -// dynamically loaded libraries +#if defined(LINKALL) +#undef LINKALL +#define LINKALL 1 // link all libraries at build time - requires all to be available at run time +#else +#define LINKALL 0 +#endif + + +#if !LINKALL + +// dynamically loaded libraries at run time #if LINUX #define LIBFLAC "libFLAC.so.8" #define LIBMAD "libmad.so.0" @@ -99,6 +107,7 @@ #define LIBAVUTIL "libavutil.so.%d" #define LIBAVCODEC "libavcodec.so.%d" #define LIBAVFORMAT "libavformat.so.%d" +#define LIBSOXR "libsoxr.so.0" #endif #if OSX @@ -111,6 +120,7 @@ #define LIBAVUTIL "libavutil.%d.dylib" #define LIBAVCODEC "libavcodec.%d.dylib" #define LIBAVFORMAT "libavformat.%d.dylib" +#define LIBSOXR "libsoxr.0.dylib" #endif #if WIN @@ -123,7 +133,10 @@ #define LIBAVUTIL "avutil-%d.dll" #define LIBAVCODEC "avcodec-%d.dll" #define LIBAVFORMAT "avformat-%d.dll" -#endif +#define LIBSOXR "libsoxr.dll" +#endif + +#endif // !LINKALL // config options #define STREAMBUF_SIZE (2 * 1024 * 1024) diff --git a/vorbis.c b/vorbis.c index 37c544c..c7f661a 100644 --- a/vorbis.c +++ b/vorbis.c @@ -31,12 +31,14 @@ struct vorbis { OggVorbis_File *vf; +#if !LINKALL // vorbis symbols to be dynamically loaded - from either vorbisfile or vorbisidec (tremor) version of library vorbis_info *(* ov_info)(OggVorbis_File *vf, int link); int (* ov_clear)(OggVorbis_File *vf); long (* ov_read)(OggVorbis_File *vf, char *buffer, int length, int bigendianp, int word, int sgned, int *bitstream); long (* ov_read_tremor)(OggVorbis_File *vf, char *buffer, int length, int *bitstream); int (* ov_open_callbacks)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks); +#endif }; static struct vorbis *v; @@ -70,6 +72,15 @@ #define IF_PROCESS(x) #endif +#if LINKALL +#define OV(h, fn, ...) (ov_ ## fn)(__VA_ARGS__) +#define TREMOR(h) 0 +extern int ov_read_tremor(); // needed to enable compilation, not linked +#else +#define OV(h, fn, ...) (h)->ov_##fn(__VA_ARGS__) +#define TREMOR(h) (h)->ov_read_tremor +#endif + // called with mutex locked within vorbis_decode to avoid locking O before S static size_t _read_cb(void *ptr, size_t size, size_t nmemb, void *datasource) { size_t bytes; @@ -119,20 +130,20 @@ cbs.read_func = _read_cb; - if (v->ov_read_tremor) { + if (TREMOR(v)) { cbs.seek_func = _seek_cb; cbs.close_func = _close_cb; cbs.tell_func = _tell_cb; } else { cbs.seek_func = NULL; cbs.close_func = NULL; cbs.tell_func = NULL; } - if ((err = v->ov_open_callbacks(streambuf, v->vf, NULL, 0, cbs)) < 0) { + if ((err = OV(v, open_callbacks, streambuf, v->vf, NULL, 0, cbs)) < 0) { LOG_WARN("open_callbacks error: %d", err); UNLOCK_O_direct; UNLOCK_S; return DECODE_COMPLETE; } - info = v->ov_info(v->vf, -1); + info = OV(v, info, v->vf, -1); LOG_INFO("setting track_start"); LOCK_O_not_direct; @@ -166,14 +177,14 @@ ); // write the decoded frames into outputbuf even though they are 16 bits per sample, then unpack them - if (v->ov_read) { + if (!TREMOR(v)) { #if SL_LITTLE_ENDIAN - n = v->ov_read(v->vf, (char *)write_buf, bytes, 0, 2, 1, &s); + n = OV(v, read, v->vf, (char *)write_buf, bytes, 0, 2, 1, &s); #else - n = v->ov_read(v->vf, (char *)write_buf, bytes, 1, 2, 1, &s); + n = OV(v, read, v->vf, (char *)write_buf, bytes, 1, 2, 1, &s); #endif } else { - n = v->ov_read_tremor(v->vf, (char *)write_buf, bytes, &s); + n = OV(v, read_tremor, v->vf, (char *)write_buf, bytes, &s); } if (n > 0) { @@ -234,17 +245,18 @@ if (!v->vf) { v->vf = malloc(sizeof(OggVorbis_File) + 128); // add some padding as struct size may be larger } else { - v->ov_clear(v->vf); + OV(v, clear, v->vf); } } static void vorbis_close(void) { - v->ov_clear(v->vf); + OV(v, clear, v->vf); free(v->vf); v->vf = NULL; } static bool load_vorbis() { +#if !LINKALL void *handle = dlopen(LIBVORBIS, RTLD_NOW); char *err; bool tremor = false; @@ -259,8 +271,6 @@ } } - v = malloc(sizeof(struct vorbis)); - v->vf = NULL; v->ov_read = tremor ? NULL : dlsym(handle, "ov_read"); v->ov_read_tremor = tremor ? dlsym(handle, "ov_read") : NULL; v->ov_info = dlsym(handle, "ov_info"); @@ -273,6 +283,8 @@ } LOG_INFO("loaded %s", tremor ? LIBTREMOR : LIBVORBIS); +#endif + return true; } @@ -287,6 +299,13 @@ vorbis_decode,// decode }; + v = malloc(sizeof(struct vorbis)); + if (!v) { + return NULL; + } + + v->vf = NULL; + if (!load_vorbis()) { return NULL; }