Beispiel #1
0
/*---------------------------------------------------------------------------*/
static void *decode_thread(struct thread_ctx_s *ctx) {
	while (ctx->decode_running) {
		size_t bytes, space, min_space;
		bool toend;
		bool ran = false;

		LOCK_S;
		bytes = _buf_used(ctx->streambuf);
		toend = (ctx->stream.state <= DISCONNECT);
		UNLOCK_S;
		LOCK_O;
		space = _buf_space(ctx->outputbuf);
		UNLOCK_O;

		LOCK_D;

		if (ctx->decode.state == DECODE_RUNNING && ctx->codec) {

			LOG_SDEBUG("streambuf bytes: %u outputbuf space: %u", bytes, space);

			IF_DIRECT(
				min_space = ctx->codec->min_space;
			);
			IF_PROCESS(
				min_space = ctx->process.max_out_frames * BYTES_PER_FRAME;
			);
Beispiel #2
0
static int _parse_packlen(void) {
	int bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
	u8_t *ptr = streambuf->readp;
	int remain = 1;

	while (bytes >= 24 && remain > 0) {
		u32_t len = *(ptr+16) | *(ptr+17) << 8 | *(ptr+18) << 16 | *(ptr+19) << 24; // assume msb 32 bits are 0
		if (!memcmp(ptr, header_guid, 16) && bytes >= 30) {
			ptr    += 30;
			bytes  -= 30;
			remain = len - 30;
			continue;
		}
		if (!memcmp(ptr, file_props_guid, 16) && len == 104) {
			u32_t packlen = *(ptr+92) | *(ptr+93) << 8 | *(ptr+94) << 16 | *(ptr+95) << 24;
			LOG_INFO("asf packet len: %u", packlen);
			return packlen;
		}
		ptr    += len;
		bytes  -= len;
		remain -= len;
	}

	LOG_WARN("could not parse packet length");
	return 0;
}
Beispiel #3
0
// 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;

	bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
	bytes = min(bytes, size * nmemb);

	memcpy(ptr, streambuf->readp, bytes);
	_buf_inc_readp(streambuf, bytes);

	return bytes / size;
}
Beispiel #4
0
static decode_state mpg_decode(void) {
    size_t bytes, space, size;
    int ret;
    u8_t *write_buf;

    LOCK_S;
    LOCK_O_direct;
    bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));

    IF_DIRECT(
        space = min(_buf_space(outputbuf), _buf_cont_write(outputbuf));
        write_buf = outputbuf->writep;
    );
Beispiel #5
0
static void *decode_thread() {

	while (running) {
		size_t bytes, 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 (space > codec->min_space && (bytes > codec->min_read_bytes || toend)) {
				
				decode.state = codec->decode();

				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;
}
Beispiel #6
0
static FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *want, void *client_data) {
	size_t bytes;
	bool end;

	LOCK_S;
	bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
	bytes = min(bytes, *want);
	end = (stream.state <= DISCONNECT && bytes == 0);

	memcpy(buffer, streambuf->readp, bytes);
	_buf_inc_readp(streambuf, bytes);
	UNLOCK_S;

	*want = bytes;

	return end ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
Beispiel #7
0
static decode_state _decode_dsf(void) {

	// samples in streambuf are interleaved on block basis
	// we transfer whole blocks for all channels in one call and so itterate the while loop below to handle wraps
	
	unsigned bytes = _buf_used(streambuf);
	unsigned block_left = d->block_size;
	
	unsigned bytes_per_frame = dop ? 2 : 1;
	
	if (bytes < d->block_size * d->channels) {
		LOG_INFO("stream too short"); // this can occur when scanning the track
		return DECODE_COMPLETE;
	}
	
	IF_PROCESS(
		process.in_frames = 0;
	);
Beispiel #8
0
void _check_header(void) {
	u8_t *ptr = streambuf->readp;
	unsigned bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
	header_format format = UNKNOWN;

	// simple parsing of wav and aiff headers and get to samples

	if (bytes > 12) {
		if (!memcmp(ptr, "RIFF", 4) && !memcmp(ptr+8, "WAVE", 4)) {
			LOG_INFO("WAVE");
			format = WAVE;
		} else if (!memcmp(ptr, "FORM", 4) && (!memcmp(ptr+8, "AIFF", 4) || !memcmp(ptr+8, "AIFC", 4))) {
			LOG_INFO("AIFF");
			format = AIFF;
		}
	}

	if (format != UNKNOWN) {
		ptr   += 12;
		bytes -= 12;

		while (bytes >= 8) {
			char id[5];
			unsigned len;
			memcpy(id, ptr, 4);
			id[4] = '\0';
			
			if (format == WAVE) {
				len = *(ptr+4) | *(ptr+5) << 8 | *(ptr+6) << 16| *(ptr+7) << 24;
			} else {
				len = *(ptr+4) << 24 | *(ptr+5) << 16 | *(ptr+6) << 8 | *(ptr+7);
			}
				
			LOG_INFO("header: %s len: %d", id, len);

			if (format == WAVE && !memcmp(ptr, "data", 4)) {
				ptr += 8;
				_buf_inc_readp(streambuf, ptr - streambuf->readp);
				audio_left = len;
				LOG_INFO("audio size: %u", audio_left);
				limit = true;
				return;
			}

			if (format == AIFF && !memcmp(ptr, "SSND", 4) && bytes >= 16) {
				unsigned offset = *(ptr+8) << 24 | *(ptr+9) << 16 | *(ptr+10) << 8 | *(ptr+11);
				// following 4 bytes is blocksize - ignored
				ptr += 8 + 8;
				_buf_inc_readp(streambuf, ptr + offset - streambuf->readp);
				audio_left = len - 8 - offset;
				LOG_INFO("audio size: %u", audio_left);
				limit = true;
				return;
			}

			if (format == WAVE && !memcmp(ptr, "fmt ", 4) && bytes >= 24) {
				// override the server parsed values with our own
				channels    = *(ptr+10) | *(ptr+11) << 8;
				sample_rate = *(ptr+12) | *(ptr+13) << 8 | *(ptr+14) << 16 | *(ptr+15) << 24;
				sample_size = (*(ptr+22) | *(ptr+23) << 8) / 8;
				bigendian   = 0;
				LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian);
			}

			if (format == AIFF && !memcmp(ptr, "COMM", 4) && bytes >= 26) {
				int exponent;
				// override the server parsed values with our own
				channels    = *(ptr+8) << 8 | *(ptr+9);
				sample_size = (*(ptr+14) << 8 | *(ptr+15)) / 8;
				bigendian   = 1;
				// sample rate is encoded as IEEE 80 bit extended format
				// make some assumptions to simplify processing - only use first 32 bits of mantissa
				exponent = ((*(ptr+16) & 0x7f) << 8 | *(ptr+17)) - 16383 - 31;
				sample_rate  = *(ptr+18) << 24 | *(ptr+19) << 16 | *(ptr+20) << 8 | *(ptr+21);
				while (exponent < 0) { sample_rate >>= 1; ++exponent; }
				while (exponent > 0) { sample_rate <<= 1; --exponent; }
				LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian);
			}

			if (bytes >= len + 8) {
				ptr   += len + 8;
				bytes -= (len + 8);
			} else {
				LOG_WARN("run out of data");
				return;
			}
		}
Beispiel #9
0
static decode_state faad_decode(void) {
	size_t bytes_total;
	size_t bytes_wrap;
	NeAACDecFrameInfo info;
	s32_t *iptr;
	bool endstream;
	frames_t frames;

	LOCK_S;
	bytes_total = _buf_used(streambuf);
	bytes_wrap  = min(bytes_total, _buf_cont_read(streambuf));

	if (stream.state <= DISCONNECT && !bytes_total) {
		UNLOCK_S;
		return DECODE_COMPLETE;
	}

	if (a->consume) {
		u32_t consume = min(a->consume, bytes_wrap);
		LOG_DEBUG("consume: %u of %u", consume, a->consume);
		_buf_inc_readp(streambuf, consume);
		a->pos += consume;
		a->consume -= consume;
		UNLOCK_S;
		return DECODE_RUNNING;
	}

	if (decode.new_stream) {
		int found = 0;
		static unsigned char channels;
		static unsigned long samplerate;

		if (a->type == '2') {

			// adts stream - seek for header
			while (bytes_wrap >= 2 && (*(streambuf->readp) != 0xFF || (*(streambuf->readp + 1) & 0xF6) != 0xF0)) {
				_buf_inc_readp(streambuf, 1);
				bytes_total--;
				bytes_wrap--;
			}
			
			if (bytes_wrap >= 2) {
				long n = NEAAC(a, Init, a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels);
				if (n < 0) {
					found = -1;
				} else {
					_buf_inc_readp(streambuf, n);
					found = 1;
				}
			}

		} else {

			// mp4 - read header
			found = read_mp4_header(&samplerate, &channels);
		}

		if (found == 1) {

			LOG_INFO("samplerate: %u channels: %u", samplerate, channels);
			bytes_total = _buf_used(streambuf);
			bytes_wrap  = min(bytes_total, _buf_cont_read(streambuf));

			LOCK_O;
			LOG_INFO("setting track_start");
			output.next_sample_rate = decode_newstream(samplerate, output.supported_rates);
			IF_DSD( output.next_dop = false; )
Beispiel #10
0
// read mp4 header to extract config data
static int read_mp4_header(unsigned long *samplerate_p, unsigned char *channels_p) {
	size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
	char type[5];
	u32_t len;

	while (bytes >= 8) {
		// count trak to find the first playable one
		static unsigned trak, play;
		u32_t consume;

		len = unpackN((u32_t *)streambuf->readp);
		memcpy(type, streambuf->readp + 4, 4);
		type[4] = '\0';

		if (!strcmp(type, "moov")) {
			trak = 0;
			play = 0;
		}
		if (!strcmp(type, "trak")) {
			trak++;
		}

		// extract audio config from within esds and pass to DecInit2
		if (!strcmp(type, "esds") && bytes > len) {
			unsigned config_len;
			u8_t *ptr = streambuf->readp + 12;
			if (*ptr++ == 0x03) {
				mp4_desc_length(&ptr);
				ptr += 4;
			} else {
				ptr += 3;
			}
			mp4_desc_length(&ptr);
			ptr += 13;
			if (*ptr++ != 0x05) {
				LOG_WARN("error parsing esds");
				return -1;
			}
			config_len = mp4_desc_length(&ptr);
			if (NEAAC(a, Init2, a->hAac, ptr, config_len, samplerate_p, channels_p) == 0) {
				LOG_DEBUG("playable aac track: %u", trak);
				play = trak;
			}
		}

		// extract the total number of samples from stts
		if (!strcmp(type, "stts") && bytes > len) {
			u32_t i;
			u8_t *ptr = streambuf->readp + 12;
			u32_t entries = unpackN((u32_t *)ptr);
			ptr += 4;
			for (i = 0; i < entries; ++i) {
				u32_t count = unpackN((u32_t *)ptr);
				u32_t size = unpackN((u32_t *)(ptr + 4));
				a->sttssamples += count * size;
				ptr += 8;
			}
			LOG_DEBUG("total number of samples contained in stts: " FMT_u64, a->sttssamples);
		}

		// stash sample to chunk info, assume it comes before stco
		if (!strcmp(type, "stsc") && bytes > len && !a->chunkinfo) {
			a->stsc = malloc(len - 12);
			if (a->stsc == NULL) {
				LOG_WARN("malloc fail");
				return -1;
			}
			memcpy(a->stsc, streambuf->readp + 12, len - 12);
		}

		// build offsets table from stco and stored stsc
		if (!strcmp(type, "stco") && bytes > len && play == trak) {
			u32_t i;
			// extract chunk offsets
			u8_t *ptr = streambuf->readp + 12;
			u32_t entries = unpackN((u32_t *)ptr);
			ptr += 4;
			a->chunkinfo = malloc(sizeof(struct chunk_table) * (entries + 1));
			if (a->chunkinfo == NULL) {
				LOG_WARN("malloc fail");
				return -1;
			}
			for (i = 0; i < entries; ++i) {
				a->chunkinfo[i].offset = unpackN((u32_t *)ptr);
				a->chunkinfo[i].sample = 0;
				ptr += 4;
			}
			a->chunkinfo[i].sample = 0;
			a->chunkinfo[i].offset = 0;
			// fill in first sample id for each chunk from stored stsc
			if (a->stsc) {
				u32_t stsc_entries = unpackN((u32_t *)a->stsc);
				u32_t sample = 0;
				u32_t last = 0, last_samples = 0;
				u8_t *ptr = (u8_t *)a->stsc + 4;
				while (stsc_entries--) {
					u32_t first = unpackN((u32_t *)ptr);
					u32_t samples = unpackN((u32_t *)(ptr + 4));
					if (last) {
						for (i = last - 1; i < first - 1; ++i) {
							a->chunkinfo[i].sample = sample;
							sample += last_samples;
						}
					}
					if (stsc_entries == 0) {
						for (i = first - 1; i < entries; ++i) {
							a->chunkinfo[i].sample = sample;
							sample += samples;
						}
					}
					last = first;
					last_samples = samples;
					ptr += 12;
				}
				free(a->stsc);
				a->stsc = NULL;
			}
		}

		// found media data, advance to start of first chunk and return
		if (!strcmp(type, "mdat")) {
			_buf_inc_readp(streambuf, 8);
			a->pos += 8;
			bytes  -= 8;
			if (play) {
				LOG_DEBUG("type: mdat len: %u pos: %u", len, a->pos);
				if (a->chunkinfo && a->chunkinfo[0].offset > a->pos) {
					u32_t skip = a->chunkinfo[0].offset - a->pos; 	
					LOG_DEBUG("skipping: %u", skip);
					if (skip <= bytes) {
						_buf_inc_readp(streambuf, skip);
						a->pos += skip;
					} else {
						a->consume = skip;
					}
				}
				a->sample = a->nextchunk = 1;
				return 1;
			} else {
				LOG_DEBUG("type: mdat len: %u, no playable track found", len);
				return -1;
			}
		}

		// parse key-value atoms within ilst ---- entries to get encoder padding within iTunSMPB entry for gapless
		if (!strcmp(type, "----") && bytes > len) {
			u8_t *ptr = streambuf->readp + 8;
			u32_t remain = len - 8, size;
			if (!memcmp(ptr + 4, "mean", 4) && (size = unpackN((u32_t *)ptr)) < remain) {
				ptr += size; remain -= size;
			}
			if (!memcmp(ptr + 4, "name", 4) && (size = unpackN((u32_t *)ptr)) < remain && !memcmp(ptr + 12, "iTunSMPB", 8)) {
				ptr += size; remain -= size;
			}
			if (!memcmp(ptr + 4, "data", 4) && remain > 16 + 48) {
				// data is stored as hex strings: 0 start end samples
				u32_t b, c; u64_t d;
				if (sscanf((const char *)(ptr + 16), "%x %x %x " FMT_x64, &b, &b, &c, &d) == 4) {
					LOG_DEBUG("iTunSMPB start: %u end: %u samples: " FMT_u64, b, c, d);
					if (a->sttssamples && a->sttssamples < b + c + d) {
						LOG_DEBUG("reducing samples as stts count is less");
						d = a->sttssamples - (b + c);
					}
					a->skip = b;
					a->samples = d;
				}
			}
		}

		// default to consuming entire box
		consume = len;

		// read into these boxes so reduce consume
		if (!strcmp(type, "moov") || !strcmp(type, "trak") || !strcmp(type, "mdia") || !strcmp(type, "minf") || !strcmp(type, "stbl") ||
			!strcmp(type, "udta") || !strcmp(type, "ilst")) {
			consume = 8;
		}
		// special cases which mix mix data in the enclosing box which we want to read into
		if (!strcmp(type, "stsd")) consume = 16;
		if (!strcmp(type, "mp4a")) consume = 36;
		if (!strcmp(type, "meta")) consume = 12;

		// consume rest of box if it has been parsed (all in the buffer) or is not one we want to parse
		if (bytes >= consume) {
			LOG_DEBUG("type: %s len: %u consume: %u", type, len, consume);
			_buf_inc_readp(streambuf, consume);
			a->pos += consume;
			bytes -= consume;
		} else if ( !(!strcmp(type, "esds") || !strcmp(type, "stts") || !strcmp(type, "stsc") || 
					 !strcmp(type, "stco") || !strcmp(type, "----")) ) {
			LOG_DEBUG("type: %s len: %u consume: %u - partial consume: %u", type, len, consume, bytes);
			_buf_inc_readp(streambuf, bytes);
			a->pos += bytes;
			a->consume = consume - bytes;
			break;
		} else {
			break;
		}
	}

	return 0;
}
Beispiel #11
0
/*---------------------------------------------------------------------------*/
unsigned _output_bytes(struct thread_ctx_s *ctx) {
	return ctx->output.thru ? _buf_used(ctx->outputbuf) : _buf_used(ctx->encodebuf);
}
Beispiel #12
0
static int _read_header(void) {
	unsigned bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
	s32_t consume;

	if (!d->type && bytes >= 4) {
		if (!memcmp(streambuf->readp, "FRM8", 4)) {
			d->type = DSDIFF;
		} else if (!memcmp(streambuf->readp, "DSD ", 4)) {
			d->type = DSF;
		} else {
			LOG_WARN("bad type");
			return -1;
		}
	}

	while (bytes >= 16) {
		char id[5];
		u64_t len = d->type == DSDIFF ? unpack64be(streambuf->readp + 4) : unpack64le(streambuf->readp + 4);
		memcpy(id, streambuf->readp, 4);
		id[4] = '\0';
		consume = 0;

		if (d->type == DSDIFF) {
			if (!strcmp(id, "FRM8")) {
				if (!memcmp(streambuf->readp + 12, "DSD ", 4)) {
					consume = 16; // read into
				} else {
					LOG_WARN("bad dsdiff FRM8");
					return -1;
				}
			}
			if (!strcmp(id, "PROP") && !memcmp(streambuf->readp + 12, "SND ", 4)) {
				consume = 16; // read into
			}
			if (!strcmp(id, "FVER")) {
				LOG_INFO("DSDIFF version: %u.%u.%u.%u", *(streambuf->readp + 12), *(streambuf->readp + 13),
					 *(streambuf->readp + 14), *(streambuf->readp + 15));
			}
			if (!strcmp(id, "FS  ")) {
				d->sample_rate = unpackN((void *)(streambuf->readp + 12));
				LOG_INFO("sample rate: %u", d->sample_rate);
			}
			if (!strcmp(id, "CHNL")) {
				d->channels = unpackn((void *)(streambuf->readp + 12));
				LOG_INFO("channels: %u", d->channels);
			}
			if (!strcmp(id, "DSD ")) {
				LOG_INFO("found dsd len: " FMT_u64, len);
				d->sample_bytes = len;
				_buf_inc_readp(streambuf, 12);
				bytes  -= 12;
				return 1; // got to the audio
			}
		}

		if (d->type == DSF) {
			if (!strcmp(id, "fmt ")) {
				if (bytes >= len && bytes >= 52) {
					u32_t version = unpack32le((void *)(streambuf->readp + 12));
					u32_t format  = unpack32le((void *)(streambuf->readp + 16));
					LOG_INFO("DSF version: %u format: %u", version, format);
					if (format != 0) {
						LOG_WARN("only support DSD raw format");
						return -1;
					}
					d->channels = unpack32le((void *)(streambuf->readp + 24));
					d->sample_rate = unpack32le((void *)(streambuf->readp + 28));
					d->lsb_first = (unpack32le((void *)(streambuf->readp + 32)) == 1);
					d->sample_bytes = unpack64le((void *)(streambuf->readp + 36)) / 8;
					d->block_size = unpack32le((void *)(streambuf->readp + 44));
					LOG_INFO("channels: %u", d->channels);
					LOG_INFO("sample rate: %u", d->sample_rate);
					LOG_INFO("lsb first: %u", d->lsb_first);
					LOG_INFO("sample bytes: " FMT_u64, d->sample_bytes);
					LOG_INFO("block size: %u", d->block_size);
				} else {
					consume = -1; // come back later
				}
			}
			if (!strcmp(id, "data")) {
				LOG_INFO("found dsd len: " FMT_u64, len);
				_buf_inc_readp(streambuf, 12);
				bytes  -= 12;
				return 1; // got to the audio
			}
		}

		// default to consuming whole chunk
		if (!consume) {
			consume = (s32_t)((d->type == DSDIFF) ? len + 12 : len);
		}

		if (bytes >= consume) {
			LOG_DEBUG("id: %s len: " FMT_u64 " consume: %d", id, len, consume);
			_buf_inc_readp(streambuf, consume);
			bytes  -= consume;
		} else if (consume > 0) {
			LOG_DEBUG("id: %s len: " FMT_u64 " consume: %d - partial consume: %u", id, len, consume, bytes);
			_buf_inc_readp(streambuf, bytes);
			d->consume = consume - bytes;
			break;
		} else {
			break;
		}
	}

	return 0;
}
Beispiel #13
0
frames_t _output_frames(frames_t avail) {

	frames_t frames, size;
	bool silence;
	
	s32_t cross_gain_in = 0, cross_gain_out = 0; s32_t *cross_ptr = NULL;
	
	s32_t gainL = output.current_replay_gain ? gain(output.gainL, output.current_replay_gain) : output.gainL;
	s32_t gainR = output.current_replay_gain ? gain(output.gainR, output.current_replay_gain) : output.gainR;

	if (output.invert) { gainL = -gainL; gainR = -gainR; }

	frames = _buf_used(outputbuf) / BYTES_PER_FRAME;
	silence = false;

	// start when threshold met
	if (output.state == OUTPUT_BUFFER && frames > output.threshold * output.next_sample_rate / 100 && frames > output.start_frames) {
		output.state = OUTPUT_RUNNING;
		LOG_INFO("start buffer frames: %u", frames);
		wake_controller();
	}
	
	// skip ahead - consume outputbuf but play nothing
	if (output.state == OUTPUT_SKIP_FRAMES) {
		if (frames > 0) {
			frames_t skip = min(frames, output.skip_frames);
			LOG_INFO("skip %u of %u frames", skip, output.skip_frames);
			frames -= skip;
			output.frames_played += skip;
			while (skip > 0) {
				frames_t cont_frames = min(skip, _buf_cont_read(outputbuf) / BYTES_PER_FRAME);
				skip -= cont_frames;
				_buf_inc_readp(outputbuf, cont_frames * BYTES_PER_FRAME);
			}
		}
		output.state = OUTPUT_RUNNING;
	}
	
	// pause frames - play silence for required frames
	if (output.state == OUTPUT_PAUSE_FRAMES) {
		LOG_INFO("pause %u frames", output.pause_frames);
		if (output.pause_frames == 0) {
			output.state = OUTPUT_RUNNING;
		} else {
			silence = true;
			frames = min(avail, output.pause_frames);
			frames = min(frames, MAX_SILENCE_FRAMES);
			output.pause_frames -= frames;
		}
	}
	
	// start at - play silence until jiffies reached
	if (output.state == OUTPUT_START_AT) {
		u32_t now = gettime_ms();
		if (now >= output.start_at || output.start_at > now + 10000) {
			output.state = OUTPUT_RUNNING;
		} else {
			u32_t delta_frames = (output.start_at - now) * output.current_sample_rate / 1000;
			silence = true;
			frames = min(avail, delta_frames);
			frames = min(frames, MAX_SILENCE_FRAMES);
		}
	}
	
	// play silence if buffering or no frames
	if (output.state <= OUTPUT_BUFFER || frames == 0) {
		silence = true;
		frames = min(avail, MAX_SILENCE_FRAMES);
	}

	LOG_SDEBUG("avail: %d frames: %d silence: %d", avail, frames, silence);
	frames = min(frames, avail);
	size = frames;
	
	while (size > 0) {
		frames_t out_frames;
		frames_t cont_frames = _buf_cont_read(outputbuf) / BYTES_PER_FRAME;
		int wrote;
		
		if (output.track_start && !silence) {
			if (output.track_start == outputbuf->readp) {
				unsigned delay = 0;
				if (output.current_sample_rate != output.next_sample_rate) {
					delay = output.rate_delay;
				}
				IF_DSD(
				   if (output.dop != output.next_dop) {
					   delay = output.dop_delay;
				   }
				)
				frames -= size;
				// add silence delay in two halves, before and after track start on rate or pcm-dop change
				if (delay) {
					output.state = OUTPUT_PAUSE_FRAMES;
					if (!output.delay_active) {
						output.pause_frames = output.current_sample_rate * delay / 2000;
						output.delay_active = true;  // first delay - don't process track start
						break;
					} else {
						output.pause_frames = output.next_sample_rate * delay / 2000;
						output.delay_active = false; // second delay - process track start
					}
				}
				LOG_INFO("track start sample rate: %u replay_gain: %u", output.next_sample_rate, output.next_replay_gain);
				output.frames_played = 0;
				output.track_started = true;
				output.track_start_time = gettime_ms();
				output.current_sample_rate = output.next_sample_rate;
				IF_DSD(
				   output.dop = output.next_dop;
				)
				if (output.fade != FADE_ACTIVE || output.fade_mode != FADE_CROSSFADE) {
					output.current_replay_gain = output.next_replay_gain;
				}
				output.track_start = NULL;
				break;
			} else if (output.track_start > outputbuf->readp) {
Beispiel #14
0
static int _read_data(void *opaque, u8_t *buffer, int buf_size) {
	size_t bytes;

	LOCK_S;

	bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
	ff->end_of_stream = (stream.state <= DISCONNECT && bytes == 0);
	bytes = min(bytes, buf_size);

	// for chunked wma extract asf header and data frames from framing structure
	// pad asf data frames to size of packet extracted from asf header
	if (ff->wma_mmsh) {
		unsigned chunk_type = 0, chunk_len = 0;
		
		if (ff->mmsh_bytes_left) {
			// bytes remaining from previous frame
			if (bytes >= ff->mmsh_bytes_left) {
				bytes = ff->mmsh_bytes_left;
				ff->mmsh_bytes_left = 0;
			} else {
				ff->mmsh_bytes_left -= bytes;
			}
		} else if (ff->mmsh_bytes_pad) {
			// add padding for previous frame
			bytes = min(ff->mmsh_bytes_pad, buf_size);
			memset(buffer, 0, bytes);
			ff->mmsh_bytes_pad -= bytes;
			UNLOCK_S;
			return bytes;
		} else if (bytes >= 12) {
			// new chunk header
			chunk_type = (*(streambuf->readp) & 0x7f) | *(streambuf->readp + 1) << 8;
			chunk_len = *(streambuf->readp + 2) | *(streambuf->readp + 3) << 8;
			_buf_inc_readp(streambuf, 12);
			bytes -= 12;
		} else if (_buf_used(streambuf) >= 12) {
			// new chunk header split over end of streambuf, read in two
			u8_t header[12];
			memcpy(header, streambuf->readp, bytes);
			_buf_inc_readp(streambuf, bytes);
			memcpy(header + bytes, streambuf->readp, 12 - bytes);
			_buf_inc_readp(streambuf, 12 - bytes);
			chunk_type = (header[0] & 0x7f) | header[1] << 8;
			chunk_len  = header[2] | header[3] << 8;
			bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
			bytes = min(bytes, buf_size);
		} else {
			// should not get here...
			LOG_ERROR("chunk parser stalled bytes: %u %u", bytes, _buf_used(streambuf));
			UNLOCK_S;
			return 0;
		}
		
		if (chunk_type && chunk_len) {
			if (chunk_type == 0x4824) {
				// asf header - parse packet length
				ff->mmsh_packet_len = _parse_packlen();
				ff->mmsh_bytes_pad = 0;
			} else if (chunk_type == 0x4424 && ff->mmsh_packet_len) {
				// asf data packet - add padding
				ff->mmsh_bytes_pad = ff->mmsh_packet_len - chunk_len + 8;
			} else {
				LOG_INFO("unknown chunk: %04x", chunk_type);
				// other packet - no padding
				ff->mmsh_bytes_pad = 0;
			}
	
			if (chunk_len - 8 <= bytes) {
				bytes = chunk_len - 8;
				ff->mmsh_bytes_left = 0;
			} else {
				ff->mmsh_bytes_left = chunk_len - 8 - bytes;
			}
		}

	}

	memcpy(buffer, streambuf->readp, bytes);

	_buf_inc_readp(streambuf, bytes);

	if (ff->mmsh_bytes_pad && bytes + ff->mmsh_bytes_pad < buf_size) {
		memset(buffer + bytes, 0, ff->mmsh_bytes_pad);
		bytes += ff->mmsh_bytes_pad;
		ff->mmsh_bytes_pad = 0;
	}

	UNLOCK_S;

	return bytes;
}
Beispiel #15
0
static void slimproto_run() {
	static u8_t buffer[MAXBUF];
	int  expect = 0;
	int  got    = 0;
	u32_t now;
	static u32_t last = 0;
	event_handle ehandles[2];
	int timeouts = 0;

	set_readwake_handles(ehandles, sock, wake_e);

	while (running && !new_server) {

		bool wake = false;
		event_type ev;

		if ((ev = wait_readwake(ehandles, 1000)) != EVENT_TIMEOUT) {
	
			if (ev == EVENT_READ) {

				if (expect > 0) {
					int n = recv(sock, buffer + got, expect, 0);
					if (n <= 0) {
						LOG_INFO("error reading from socket: %s", n ? strerror(last_error()) : "closed");
						return;
					}
					expect -= n;
					got += n;
					if (expect == 0) {
						process(buffer, got);
						got = 0;
					}
				} else if (expect == 0) {
					int n = recv(sock, buffer + got, 2 - got, 0);
					if (n <= 0) {
						LOG_INFO("error reading from socket: %s", n ? strerror(last_error()) : "closed");
						return;
					}
					got += n;
					if (got == 2) {
						expect = buffer[0] << 8 | buffer[1]; // length pack 'n'
						got = 0;
						if (expect > MAXBUF) {
							LOG_ERROR("FATAL: slimproto packet too big: %d > %d", expect, MAXBUF);
							return;
						}
					}
				} else {
					LOG_ERROR("FATAL: negative expect");
					return;
				}

			}

			if (ev == EVENT_WAKE) {
				wake = true;
			}

			timeouts = 0;

		} else if (++timeouts > 35) {

			// expect message from server every 5 seconds, but 30 seconds on mysb.com so timeout after 35 seconds
			LOG_INFO("No messages from server - connection dead");
			return;
		}

		// update playback state when woken or every 100ms
		now = gettime_ms();

		if (wake || now - last > 100 || last > now) {
			bool _sendSTMs = false;
			bool _sendDSCO = false;
			bool _sendRESP = false;
			bool _sendMETA = false;
			bool _sendSTMd = false;
			bool _sendSTMt = false;
			bool _sendSTMl = false;
			bool _sendSTMu = false;
			bool _sendSTMo = false;
			bool _sendSTMn = false;
			disconnect_code disconnect;
			static char header[MAX_HEADER];
			size_t header_len = 0;
			last = now;

			LOCK_S;
			status.stream_full = _buf_used(streambuf);
			status.stream_size = streambuf->size;
			status.stream_bytes = stream.bytes;
			status.stream_state = stream.state;
						
			if (stream.state == DISCONNECT) {
				disconnect = stream.disconnect;
				stream.state = STOPPED;
				_sendDSCO = true;
			}
			if (!stream.sent_headers && 
				(stream.state == STREAMING_HTTP || stream.state == STREAMING_WAIT || stream.state == STREAMING_BUFFERING)) {
				header_len = stream.header_len;
				memcpy(header, stream.header, header_len);
				_sendRESP = true;
				stream.sent_headers = true;
			}
			if (stream.meta_send) {
				header_len = stream.header_len;
				memcpy(header, stream.header, header_len);
				_sendMETA = true;
				stream.meta_send = false;
			}
			UNLOCK_S;
			
			LOCK_O;
			status.output_full = _buf_used(outputbuf);
			status.output_size = outputbuf->size;
			status.frames_played = output.frames_played;
			status.current_sample_rate = output.current_sample_rate;
			status.updated = output.updated;
			status.device_frames = output.device_frames;
			
			if (output.track_started) {
				_sendSTMs = true;
				output.track_started = false;
				status.stream_start = output.updated;
			}
#if PORTAUDIO
			if (output.pa_reopen) {
				_pa_open();
				output.pa_reopen = false;
			}
#endif
			if (output.state == OUTPUT_RUNNING && !sentSTMu && status.output_full == 0 && status.stream_state <= DISCONNECT) {
				_sendSTMu = true;
				sentSTMu = true;
			}
			if (output.state == OUTPUT_RUNNING && !sentSTMo && status.output_full == 0 && status.stream_state == STREAMING_HTTP) {
				_sendSTMo = true;
				sentSTMo = true;
			}
			UNLOCK_O;

			LOCK_D;
			if (decode.state == DECODE_RUNNING && now - status.last > 1000) {
				_sendSTMt = true;
				status.last = now;
			}
			if (decode.state == DECODE_COMPLETE) {
				_sendSTMd = true;
				decode.state = DECODE_STOPPED;
			}
			if (decode.state == DECODE_ERROR) {
				_sendSTMn = true;
				decode.state = DECODE_STOPPED;
			}
			if ((status.stream_state == STREAMING_HTTP || status.stream_state == STREAMING_FILE) && !sentSTMl 
				&& decode.state == DECODE_STOPPED) {
				if (autostart == 0) {
					_sendSTMl = true;
					sentSTMl = true;
				} else if (autostart == 1) {
					decode.state = DECODE_RUNNING;
					LOCK_O;
					if (output.state == OUTPUT_STOPPED) {
						output.state = OUTPUT_BUFFER;
					}
					UNLOCK_O;
				}
				// autostart 2 and 3 require cont to be received first
			}
			UNLOCK_D;
		
			// send packets once locks released as packet sending can block
			if (_sendDSCO) sendDSCO(disconnect);
			if (_sendSTMs) sendSTAT("STMs", 0);
			if (_sendSTMd) sendSTAT("STMd", 0);
			if (_sendSTMt) sendSTAT("STMt", 0);
			if (_sendSTMl) sendSTAT("STMl", 0);
			if (_sendSTMu) sendSTAT("STMu", 0);
			if (_sendSTMo) sendSTAT("STMo", 0);
			if (_sendSTMn) sendSTAT("STMn", 0);
			if (_sendRESP) sendRESP(header, header_len);
			if (_sendMETA) sendMETA(header, header_len);
		}
	}
}
Beispiel #16
0
static decode_state mpg_decode(void) {
	size_t bytes, space, size;
	int ret;

	LOCK_S;
	LOCK_O;
	bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
	space = min(_buf_space(outputbuf), _buf_cont_write(outputbuf));
	bytes = min(bytes, READ_SIZE);
	space = min(space, WRITE_SIZE);

	if (stream.state <= DISCONNECT && bytes == 0) {
		UNLOCK_O;
		UNLOCK_S;
		return DECODE_COMPLETE;
	}

	if (m->use16bit) {
		space = (space / BYTES_PER_FRAME) * 4;
	}

	ret = m->mpg123_decode(m->h, streambuf->readp, bytes, outputbuf->writep, space, &size);

	if (ret == MPG123_NEW_FORMAT) {

		if (decode.new_stream) {
			long rate;
			int channels, enc;
			
			m->mpg123_getformat(m->h, &rate, &channels, &enc);
			
			LOG_INFO("setting track_start");
			output.next_sample_rate = rate;
			output.track_start = outputbuf->writep;
			if (output.fade_mode) _checkfade(true);
			decode.new_stream = false;

		} else {
			LOG_WARN("format change mid stream - not supported");
		}
	}

	// expand 16bit output to 32bit samples
	if (m->use16bit) {
		s16_t *iptr;
		s32_t *optr;
		size_t count = size / 2;
		size = count * 4;
		iptr = (s16_t *)outputbuf->writep + count;
		optr = (s32_t *)outputbuf->writep + count;
		while (count--) {
			*--optr = *--iptr << 16;
		}
	}

	_buf_inc_readp(streambuf, bytes);
	_buf_inc_writep(outputbuf, size);

	UNLOCK_O;
	UNLOCK_S;

	LOG_SDEBUG("write %u frames", size / BYTES_PER_FRAME);

	if (ret == MPG123_DONE) {
		LOG_INFO("stream complete");
		return DECODE_COMPLETE;
	}

	if (ret == MPG123_ERR) {
		LOG_WARN("Error");
		return DECODE_COMPLETE;
	}

	// OK and NEED_MORE keep running
	return DECODE_RUNNING;
}
Beispiel #17
0
static decode_state faad_decode(void) {
	size_t bytes_total;
	size_t bytes_wrap;
	NeAACDecFrameInfo info;
	s32_t *iptr;
	bool endstream;
	frames_t frames;

	LOCK_S;
	bytes_total = _buf_used(streambuf);
	bytes_wrap  = min(bytes_total, _buf_cont_read(streambuf));

	if (stream.state <= DISCONNECT && !bytes_total) {
		UNLOCK_S;
		return DECODE_COMPLETE;
	}

	if (a->consume) {
		u32_t consume = min(a->consume, bytes_wrap);
		LOG_DEBUG("consume: %u of %u", consume, a->consume);
		_buf_inc_readp(streambuf, consume);
		a->pos += consume;
		a->consume -= consume;
		UNLOCK_S;
		return DECODE_RUNNING;
	}

	if (decode.new_stream) {
		int found = 0;
		static unsigned char channels;
		static unsigned long samplerate;

		if (a->type == '2') {

			// adts stream - seek for header
			while (bytes_wrap >= 2 && (*(streambuf->readp) != 0xFF || (*(streambuf->readp + 1) & 0xF6) != 0xF0)) {
				_buf_inc_readp(streambuf, 1);
				bytes_total--;
				bytes_wrap--;
			}
			
			if (bytes_wrap >= 2) {
				long n = NEAAC(a, Init, a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels);
				if (n < 0) {
					found = -1;
				} else {
					_buf_inc_readp(streambuf, n);
					found = 1;
				}
			}

		} else {

			// mp4 - read header
			found = read_mp4_header(&samplerate, &channels);
		}

		if (found == 1) {

			LOG_INFO("samplerate: %u channels: %u", samplerate, channels);
			bytes_total = _buf_used(streambuf);
			bytes_wrap  = min(bytes_total, _buf_cont_read(streambuf));

			LOCK_O;
			LOG_INFO("setting track_start");
			output.next_sample_rate = decode_newstream(samplerate, output.max_sample_rate);
			output.track_start = outputbuf->writep;
			if (output.fade_mode) _checkfade(true);
			decode.new_stream = false;
			UNLOCK_O;

		} else if (found == -1) {

			LOG_WARN("error reading stream header");
			UNLOCK_S;
			return DECODE_ERROR;

		} else {

			// not finished header parsing come back next time
			UNLOCK_S;
			return DECODE_RUNNING;
		}
	}

	if (bytes_wrap < WRAPBUF_LEN && bytes_total > WRAPBUF_LEN) {

		// make a local copy of frames which may have wrapped round the end of streambuf
		u8_t buf[WRAPBUF_LEN];
		memcpy(buf, streambuf->readp, bytes_wrap);
		memcpy(buf + bytes_wrap, streambuf->buf, WRAPBUF_LEN - bytes_wrap);

		iptr = NEAAC(a, Decode, a->hAac, &info, buf, WRAPBUF_LEN);

	} else {

		iptr = NEAAC(a, Decode, a->hAac, &info, streambuf->readp, bytes_wrap);
	}

	if (info.error) {
		LOG_WARN("error: %u %s", info.error, NEAAC(a, GetErrorMessage, info.error));
	}

	endstream = false;

	// mp4 end of chunk - skip to next offset
	if (a->chunkinfo && a->chunkinfo[a->nextchunk].offset && a->sample++ == a->chunkinfo[a->nextchunk].sample) {

		if (a->chunkinfo[a->nextchunk].offset > a->pos) {
			u32_t skip = a->chunkinfo[a->nextchunk].offset - a->pos;
			if (skip != info.bytesconsumed) {
				LOG_DEBUG("skipping to next chunk pos: %u consumed: %u != skip: %u", a->pos, info.bytesconsumed, skip);
			}
			if (bytes_total >= skip) {
				_buf_inc_readp(streambuf, skip);
				a->pos += skip;
			} else {
				a->consume = skip;
			}
			a->nextchunk++;
		} else {
			LOG_ERROR("error: need to skip backwards!");
			endstream = true;
		}

	// adts and mp4 when not at end of chunk 
	} else if (info.bytesconsumed != 0) {

		_buf_inc_readp(streambuf, info.bytesconsumed);
		a->pos += info.bytesconsumed;

	// error which doesn't advance streambuf - end
	} else {
		endstream = true;
	}

	UNLOCK_S;

	if (endstream) {
		LOG_WARN("unable to decode further");
		return DECODE_ERROR;
	}

	if (!info.samples) {
		a->empty = true;
		return DECODE_RUNNING;
	}

	frames = info.samples / info.channels;

	if (a->skip) {
		u32_t skip;
		if (a->empty) {
			a->empty = false;
			a->skip -= frames;
			LOG_DEBUG("gapless: first frame empty, skipped %u frames at start", frames);
		}
		skip = min(frames, a->skip);
		LOG_DEBUG("gapless: skipping %u frames at start", skip);
		frames -= skip;
		a->skip -= skip;
		iptr += skip * info.channels;
	}

	if (a->samples) {
		if (a->samples < frames) {
			LOG_DEBUG("gapless: trimming %u frames from end", frames - a->samples);
			frames = (frames_t)a->samples;
		}
		a->samples -= frames;
	}

	LOG_SDEBUG("write %u frames", frames);

	LOCK_O_direct;

	while (frames > 0) {
		frames_t f;
		frames_t count;
		s32_t *optr;

		IF_DIRECT(
			f = _buf_cont_write(outputbuf) / BYTES_PER_FRAME;
			optr = (s32_t *)outputbuf->writep;
		);
		IF_PROCESS(
			f = process.max_in_frames;
			optr = (s32_t *)process.inbuf;
		);