123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- /*
- * 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)
- #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 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(bool close) {
- LOG_INFO("decode flush");
- LOCK_D;
- decode.state = DECODE_STOPPED;
- IF_PROCESS(
- process_flush();
- );
- if (close && codec) {
- codec->close();
- codec = NULL;
- }
- 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");
- }
|