Codebase list squeezelite / upstream/1.6 decode.c
upstream/1.6

Tree @upstream/1.6 (Download .tar.gz)

decode.c @upstream/1.6

c88fab0
fd65b52
c88fab0
80b66f6
c88fab0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a66990
c88fab0
 
 
5a66990
c88fab0
 
fc4f1fb
 
 
 
 
 
 
5a66990
 
 
 
 
 
 
 
 
 
c88fab0
 
 
5a66990
fd65b52
fc4f1fb
c88fab0
 
fd65b52
 
c88fab0
 
fd65b52
c88fab0
 
fc4f1fb
 
8b50589
c88fab0
 
5a66990
 
 
 
 
 
 
c88fab0
5a66990
c88fab0
fc4f1fb
c88fab0
5a66990
 
 
 
 
 
 
 
 
 
fc4f1fb
c88fab0
fc4f1fb
9c2cff3
 
9dbad2a
9c2cff3
 
 
 
c88fab0
fc4f1fb
c88fab0
fc4f1fb
 
 
c88fab0
fc4f1fb
c88fab0
 
 
 
 
 
 
fd65b52
c88fab0
 
fd65b52
 
c88fab0
 
 
 
 
11c3a1f
fd65b52
11c3a1f
 
 
74b08e7
 
 
 
c773a8a
 
 
 
3ebd004
 
c773a8a
 
c88fab0
fc4f1fb
 
3ad55e4
c88fab0
 
c0aef15
c88fab0
 
fd65b52
 
 
 
c88fab0
 
 
5a66990
 
 
 
 
c88fab0
 
 
 
fc4f1fb
c88fab0
 
fc4f1fb
c88fab0
 
fc4f1fb
3ad55e4
0f9850d
 
fc4f1fb
c88fab0
 
5a66990
 
 
 
 
 
 
 
 
 
11c3a1f
5a66990
 
 
11c3a1f
5a66990
 
 
 
 
 
c88fab0
fd65b52
 
c88fab0
 
fc4f1fb
 
c88fab0
 
 
5a66990
 
 
 
c88fab0
 
 
 
 
 
8245897
c88fab0
 
 
 
 
 
fc4f1fb
 
c88fab0
 
 
 
fc4f1fb
 
c88fab0
 
5a66990
/* 
 *  Squeezelite - lightweight headless squeezebox emulator
 *
 *  (c) Adrian Smith 2012-2014, triode1@btinternet.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)
#endif

static 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 thread_type thread;

void decode_init(log_level level, const char *opt) {
	int i;

	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 (!opt || strstr(opt, "dsd"))  codecs[i++] = register_dsd();
#endif
#if FFMPEG
	if (!opt || strstr(opt, "alac"))  codecs[i++] = register_ff("alc");
	if (!opt || strstr(opt, "wma"))  codecs[i++] = register_ff("wma");
#endif
	if (!opt || strstr(opt, "aac"))  codecs[i++] = register_faad();
	if (!opt || strstr(opt, "ogg"))  codecs[i++] = register_vorbis();
	if (!opt || strstr(opt, "flac")) codecs[i++] = register_flac();
	if (!opt || strstr(opt, "pcm"))  codecs[i++] = register_pcm();

	// try mad then mpg for mp3 unless command line option passed
	if ( !opt || strstr(opt, "mp3") || strstr(opt, "mad"))                codecs[i] = register_mad();
	if ((!opt || strstr(opt, "mp3") || strstr(opt, "mpg")) && !codecs[i]) codecs[i] = register_mpg();

	mutex_create(decode.mutex);

#if LINUX || OSX || FREEBSD
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + DECODE_THREAD_STACK_SIZE);
	pthread_create(&thread, &attr, decode_thread, NULL);
	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
	pthread_join(thread, NULL);
#endif
	mutex_destroy(decode.mutex);
}

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[]) {

	MAY_PROCESS(
		if (decode.process) {
			return process_newstream(&decode.direct, sample_rate, supported_rates);
		}
	);

	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);

			UNLOCK_D;
			return;
		}
	}

	UNLOCK_D;

	LOG_ERROR("codec not found");
}