outputBuffer.c 4.5 KB
Newer Older
1
/* the Music Player Daemon (MPD)
2
 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * This project's homepage is: http://www.musicpd.org
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "outputBuffer.h"
Max Kellermann's avatar
Max Kellermann committed
20
#include "playerData.h"
21 22 23

#include "utils.h"

24
void ob_init(unsigned int size, Notify *notify)
25
{
26 27
	assert(size > 0);

28 29 30 31
	ob.chunks = xmalloc(size * sizeof(*ob.chunks));
	ob.size = size;
	ob.begin = 0;
	ob.end = 0;
Max Kellermann's avatar
Max Kellermann committed
32
	ob.lazy = 0;
33
	ob.notify = notify;
34
	ob.chunks[0].chunkSize = 0;
Warren Dukes's avatar
Warren Dukes committed
35 36
}

37
void ob_free(void)
38
{
39 40
	assert(ob.chunks != NULL);
	free(ob.chunks);
41 42
}

43
void ob_clear(void)
Avuton Olrich's avatar
Avuton Olrich committed
44
{
45 46
	ob.end = ob.begin;
	ob.chunks[ob.end].chunkSize = 0;
47 48
}

49
/** return the index of the chunk after i */
50
static inline unsigned successor(unsigned i)
51
{
52
	assert(i <= ob.size);
53 54

	++i;
55
	return i == ob.size ? 0 : i;
56 57
}

58 59 60 61
/**
 * Mark the tail chunk as "full" and wake up the player if is waiting
 * for the decoder.
 */
62
static void output_buffer_expand(unsigned i)
63
{
64
	int was_empty = ob.notify != NULL && (!ob.lazy || ob_is_empty());
65

66 67
	assert(i == (ob.end + 1) % ob.size);
	assert(i != ob.end);
68

69 70
	ob.end = i;
	ob.chunks[i].chunkSize = 0;
71 72 73 74
	if (was_empty)
		/* if the buffer was empty, the player thread might be
		   waiting for us; wake it up now that another decoded
		   buffer has become available. */
75
		notify_signal(ob.notify);
76 77
}

78
void ob_flush(void)
Avuton Olrich's avatar
Avuton Olrich committed
79
{
80
	ob_chunk *chunk = ob_get_chunk(ob.end);
81 82

	if (chunk->chunkSize > 0) {
83 84
		unsigned int next = successor(ob.end);
		if (next == ob.begin)
85 86 87 88 89
			/* all buffers are full; we have to wait for
			   the player to free one, so don't flush
			   right now */
			return;

90
		output_buffer_expand(next);
91
	}
92 93
}

Max Kellermann's avatar
Max Kellermann committed
94 95 96 97 98
void ob_set_lazy(int lazy)
{
	ob.lazy = lazy;
}

99
int ob_is_empty(void)
100
{
101
	return ob.begin == ob.end;
102 103
}

104
void ob_shift(void)
105
{
106 107
	assert(ob.begin != ob.end);
	assert(ob.begin < ob.size);
108

109
	ob.begin = successor(ob.begin);
110 111
}

112
unsigned int ob_relative(const unsigned i)
113
{
114 115
	if (i >= ob.begin)
		return i - ob.begin;
116
	else
117
		return i + ob.size - ob.begin;
118 119
}

120
unsigned ob_available(void)
121
{
122
	return ob_relative(ob.end);
123 124
}

125
int ob_absolute(const unsigned relative)
126 127 128
{
	unsigned i, max;

129 130 131 132
	max = ob.end;
	if (max < ob.begin)
		max += ob.size;
	i = (unsigned)ob.begin + relative;
133 134 135
	if (i >= max)
		return -1;

136 137
	if (i >= ob.size)
		i -= ob.size;
138 139 140 141

	return (int)i;
}

142
ob_chunk * ob_get_chunk(const unsigned i)
143
{
144
	assert(i < ob.size);
145

146
	return &ob.chunks[i];
147 148
}

149
/**
Max Kellermann's avatar
Max Kellermann committed
150
 * Return the tail chunk which has room for additional data.
151
 *
152
 * @return the chunk which has room for more data; NULL if there is no
Max Kellermann's avatar
Max Kellermann committed
153
 * room.
154
 */
155
static ob_chunk *tail_chunk(float data_time, mpd_uint16 bitRate)
156 157
{
	unsigned int next;
158
	ob_chunk *chunk;
159

160
	chunk = ob_get_chunk(ob.end);
161 162 163
	assert(chunk->chunkSize <= sizeof(chunk->data));
	if (chunk->chunkSize == sizeof(chunk->data)) {
		/* this chunk is full; allocate a new chunk */
164
		next = successor(ob.end);
Max Kellermann's avatar
Max Kellermann committed
165 166
		if (ob.begin == next)
			/* no chunks available */
167
			return NULL;
168

169
		output_buffer_expand(next);
170
		chunk = ob_get_chunk(next);
171
		assert(chunk->chunkSize == 0);
172 173
	}

174 175 176 177 178 179 180
	if (chunk->chunkSize == 0) {
		/* if the chunk is empty, nobody has set bitRate and
		   times yet */

		chunk->bitRate = bitRate;
		chunk->times = data_time;
	}
181

182
	return chunk;
183 184
}

Max Kellermann's avatar
Max Kellermann committed
185 186
size_t ob_append(const void *data0, size_t datalen,
		 float data_time, mpd_uint16 bitRate)
187
{
Max Kellermann's avatar
Max Kellermann committed
188 189
	const unsigned char *data = data0;
	size_t ret = 0, dataToSend;
190
	ob_chunk *chunk = NULL;
191

Avuton Olrich's avatar
Avuton Olrich committed
192
	while (datalen) {
193 194
		chunk = tail_chunk(data_time, bitRate);
		if (chunk == NULL)
Max Kellermann's avatar
Max Kellermann committed
195
			return ret;
196

197 198 199
		dataToSend = sizeof(chunk->data) - chunk->chunkSize;
		if (dataToSend > datalen)
			dataToSend = datalen;
200

201 202
		memcpy(chunk->data + chunk->chunkSize, data, dataToSend);
		chunk->chunkSize += dataToSend;
Avuton Olrich's avatar
Avuton Olrich committed
203 204
		datalen -= dataToSend;
		data += dataToSend;
Max Kellermann's avatar
Max Kellermann committed
205
		ret += dataToSend;
Avuton Olrich's avatar
Avuton Olrich committed
206
	}
207

208
	if (chunk != NULL && chunk->chunkSize == sizeof(chunk->data))
209
		ob_flush();
210

Max Kellermann's avatar
Max Kellermann committed
211
	return ret;
212
}
Warren Dukes's avatar
Warren Dukes committed
213

214
void ob_skip(unsigned num)
215
{
216
	int i = ob_absolute(num);
217
	if (i >= 0)
218
		ob.begin = i;
219
}