| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 | /*  *  Squeezelite - lightweight headless squeezebox emulator * *  (c) Adrian Smith 2012-2015, triode1@btinternet.com *      Ralph Irving 2015-2017, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. *  * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program.  If not, see <http://www.gnu.org/licenses/>. * */// decode thread#include "squeezelite.h"log_level loglevel;extern struct buffer *streambuf;extern struct buffer *outputbuf;extern struct streamstate stream;extern struct outputstate output;extern struct processstate process;struct decodestate decode;struct codec *codecs[MAX_CODECS];struct codec *codec;static bool running = true;#define LOCK_S   mutex_lock(streambuf->mutex)#define UNLOCK_S mutex_unlock(streambuf->mutex)#define LOCK_O   mutex_lock(outputbuf->mutex)#define UNLOCK_O mutex_unlock(outputbuf->mutex)#define LOCK_D   mutex_lock(decode.mutex);#define UNLOCK_D mutex_unlock(decode.mutex);#if PROCESS#define IF_DIRECT(x)    if (decode.direct) { x }#define IF_PROCESS(x)   if (!decode.direct) { x }#define MAY_PROCESS(x)  { x }#else#define IF_DIRECT(x)    { x }#define IF_PROCESS(x)#define MAY_PROCESS(x)#endifstatic void *decode_thread() {		while (running) {		size_t bytes, space, min_space;		bool toend;		bool ran = false;				LOCK_S;		bytes = _buf_used(streambuf);		toend = (stream.state <= DISCONNECT);		UNLOCK_S;		LOCK_O;		space = _buf_space(outputbuf);		UNLOCK_O;		LOCK_D;				if (decode.state == DECODE_RUNNING && codec) {					LOG_SDEBUG("streambuf bytes: %u outputbuf space: %u", bytes, space);			IF_DIRECT(				min_space = codec->min_space;			);			IF_PROCESS(				min_space = process.max_out_frames * BYTES_PER_FRAME;			);			if (space > min_space && (bytes > codec->min_read_bytes || toend)) {								decode.state = codec->decode();				IF_PROCESS(					if (process.in_frames) {						process_samples();					}					if (decode.state == DECODE_COMPLETE) {						process_drain();					}				);				if (decode.state != DECODE_RUNNING) {					LOG_INFO("decode %s", decode.state == DECODE_COMPLETE ? "complete" : "error");					LOCK_O;					if (output.fade_mode) _checkfade(false);					UNLOCK_O;					wake_controller();				}				ran = true;			}		}				UNLOCK_D;		if (!ran) {			usleep(100000);		}	}		return 0;}static void sort_codecs(int pry, struct codec* ptr) {	static int priority[MAX_CODECS];	int i, tpry;	struct codec* tptr;	for (i = 0; i < MAX_CODECS; i++) {		if (!codecs[i]) {			codecs[i] = ptr;			priority[i] = pry;			return;		}		if (pry < priority[i]) {			tptr = codecs[i];			codecs[i] = ptr;			ptr = tptr;			tpry = priority[i];			priority[i] = pry;			pry = tpry;		}	}}static thread_type thread;void decode_init(log_level level, const char *include_codecs, const char *exclude_codecs) {	int i;	char* order_codecs = NULL;	loglevel = level;	LOG_INFO("init decode");	// register codecs	// dsf,dff,alc,wma,wmap,wmal,aac,spt,ogg,ogf,flc,aif,pcm,mp3	i = 0;#if DSD	if (!strstr(exclude_codecs, "dsd")	&& (!include_codecs || (order_codecs = strstr(include_codecs, "dsd"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_dsd());#endif#if FFMPEG	if (!strstr(exclude_codecs, "alac") && (!include_codecs || (order_codecs = strstr(include_codecs, "alac"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_ff("alc"));	if (!strstr(exclude_codecs, "wma")	&& (!include_codecs || (order_codecs = strstr(include_codecs, "wma"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_ff("wma"));#else		if (!strstr(exclude_codecs, "alac") && (!include_codecs || (order_codecs = strstr(include_codecs, "alac"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_alac());#endif	#ifndef NO_FAAD	if (!strstr(exclude_codecs, "aac")	&& (!include_codecs || (order_codecs = strstr(include_codecs, "aac"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_faad());#endif	if (!strstr(exclude_codecs, "aac")	&& (!include_codecs || (order_codecs = strstr(include_codecs, "aac"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_helixaac());	if (!strstr(exclude_codecs, "ogg")	&& (!include_codecs || (order_codecs = strstr(include_codecs, "ogg"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_vorbis());		if (!strstr(exclude_codecs, "ops")	&& (!include_codecs || (order_codecs = strstr(include_codecs, "ops"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_opus());	if (!strstr(exclude_codecs, "flac") && (!include_codecs || (order_codecs = strstr(include_codecs, "flac"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_flac());	if (!strstr(exclude_codecs, "pcm")	&& (!include_codecs || (order_codecs = strstr(include_codecs, "pcm"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_pcm());	// try mad then mpg for mp3 unless command line option passed	if (!(strstr(exclude_codecs, "mp3") || strstr(exclude_codecs, "mad")) &&		(!include_codecs || (order_codecs = strstr(include_codecs, "mp3")) || (order_codecs = strstr(include_codecs, "mad"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_mad());	else if (!(strstr(exclude_codecs, "mp3") || strstr(exclude_codecs, "mpg")) &&		(!include_codecs || (order_codecs = strstr(include_codecs, "mp3")) || (order_codecs = strstr(include_codecs, "mpg"))))		sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_mpg());		#if EMBEDDED	register_external();#endif 		LOG_DEBUG("include codecs: %s exclude codecs: %s", include_codecs ? include_codecs : "", exclude_codecs);	mutex_create(decode.mutex);#if LINUX || OSX || FREEBSD || EMBEDDED	pthread_attr_t attr;	pthread_attr_init(&attr);#ifdef PTHREAD_STACK_MIN	pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + DECODE_THREAD_STACK_SIZE);#endif	pthread_create_name(&thread, &attr, decode_thread, NULL, "decode");	pthread_attr_destroy(&attr);#endif#if WIN	thread = CreateThread(NULL, DECODE_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&decode_thread, NULL, 0, NULL);#endif	decode.new_stream = true;	decode.state = DECODE_STOPPED;	MAY_PROCESS(		decode.direct = true;		decode.process = false;	);}void decode_close(void) {	LOG_INFO("close decode");	LOCK_D;	if (codec) {		codec->close();		codec = NULL;	}	running = false;	UNLOCK_D;#if LINUX || OSX || FREEBSD || EMBEDDED	pthread_join(thread, NULL);#endif	mutex_destroy(decode.mutex);#if EMBEDDED		deregister_external();#endif	}void decode_flush(void) {	LOG_INFO("decode flush");	LOCK_D;	decode.state = DECODE_STOPPED;	IF_PROCESS(		process_flush();	);	UNLOCK_D;}unsigned decode_newstream(unsigned sample_rate, unsigned supported_rates[]) {	// called with O locked to get sample rate for potentially processed output stream	// release O mutex during process_newstream as it can take some time	MAY_PROCESS(		if (decode.process) {			UNLOCK_O;			sample_rate = process_newstream(&decode.direct, sample_rate, supported_rates);			LOCK_O;		}	);	return sample_rate;}void codec_open(u8_t format, u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness) {	int i;	LOG_INFO("codec open: '%c'", format);	LOCK_D;	decode.new_stream = true;	decode.state = DECODE_STOPPED;	MAY_PROCESS(		decode.direct = true; // potentially changed within codec when processing enabled	);	// find the required codec	for (i = 0; i < MAX_CODECS; ++i) {		if (codecs[i] && codecs[i]->id == format) {			if (codec && codec != codecs[i]) {				LOG_INFO("closing codec: '%c'", codec->id);				codec->close();			}						codec = codecs[i];						codec->open(sample_size, sample_rate, channels, endianness);			decode.state = DECODE_READY;			UNLOCK_D;			return;		}	}	UNLOCK_D;	LOG_ERROR("codec not found");}
 |