output_thread.c 6.15 KB
Newer Older
1 2 3
/*
 * Copyright (C) 2003-2009 The Music Player Daemon Project
 * http://www.musicpd.org
4 5 6 7 8 9 10 11 12 13
 *
 * 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.
14 15 16 17
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 19 20 21
 */

#include "output_thread.h"
#include "output_api.h"
22
#include "output_internal.h"
23 24
#include "chunk.h"
#include "pipe.h"
25

26
#include <glib.h>
27

28
#include <assert.h>
29 30
#include <stdlib.h>
#include <errno.h>
31

32 33 34
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "output"

35 36 37 38 39 40 41
static void ao_command_finished(struct audio_output *ao)
{
	assert(ao->command != AO_COMMAND_NONE);
	ao->command = AO_COMMAND_NONE;
	notify_signal(&audio_output_client_notify);
}

42 43 44 45 46
static void
ao_close(struct audio_output *ao)
{
	assert(ao->open);

47 48 49 50 51 52
	ao->pipe = NULL;

	g_mutex_lock(ao->mutex);
	ao->chunk = NULL;
	g_mutex_unlock(ao->mutex);

53
	ao_plugin_close(ao->plugin, ao->data);
54 55
	pcm_convert_deinit(&ao->convert_state);
	ao->open = false;
56 57

	g_debug("closed plugin=%s name=\"%s\"", ao->plugin->name, ao->name);
58 59
}

60 61
static bool
ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
62
{
63 64
	const char *data = chunk->data;
	size_t size = chunk->length;
65
	GError *error = NULL;
66

67 68
	assert(!music_chunk_is_empty(chunk));
	assert(music_chunk_check_format(chunk, &ao->in_audio_format));
69
	assert(size % audio_format_frame_size(&ao->in_audio_format) == 0);
70

71 72 73 74 75 76 77 78
	if (chunk->tag != NULL)
		ao_plugin_send_tag(ao->plugin, ao->data, chunk->tag);

	if (size == 0)
		return true;

	if (!audio_format_equals(&ao->in_audio_format,
				 &ao->out_audio_format)) {
79 80 81
		data = pcm_convert(&ao->convert_state,
				   &ao->in_audio_format, data, size,
				   &ao->out_audio_format, &size);
82

83 84 85 86
		/* under certain circumstances, pcm_convert() may
		   return an empty buffer - this condition should be
		   investigated further, but for now, do this check as
		   a workaround: */
87
		if (data == NULL)
88
			return true;
89 90
	}

91
	while (size > 0 && ao->command == AO_COMMAND_NONE) {
92 93
		size_t nbytes;

94 95
		nbytes = ao_plugin_play(ao->plugin, ao->data, data, size,
					&error);
96 97
		if (nbytes == 0) {
			/* play()==0 means failure */
98 99 100 101
			g_warning("\"%s\" [%s] failed to play: %s",
				  ao->name, ao->plugin->name, error->message);
			g_error_free(error);

102 103
			ao_plugin_cancel(ao->plugin, ao->data);
			ao_close(ao);
104 105 106 107

			/* don't automatically reopen this device for
			   10 seconds */
			ao->fail_timer = g_timer_new();
108
			return false;
109 110 111 112 113 114 115
		}

		assert(nbytes <= size);
		assert(nbytes % audio_format_frame_size(&ao->out_audio_format) == 0);

		data += nbytes;
		size -= nbytes;
116 117
	}

118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
	return true;
}

static void ao_play(struct audio_output *ao)
{
	bool success;
	const struct music_chunk *chunk;

	assert(ao->pipe != NULL);

	g_mutex_lock(ao->mutex);
	chunk = ao->chunk;
	if (chunk != NULL)
		/* continue the previous play() call */
		chunk = chunk->next;
	else
		chunk = music_pipe_peek(ao->pipe);
	ao->chunk_finished = false;

	while (chunk != NULL && ao->command == AO_COMMAND_NONE) {
		assert(!ao->chunk_finished);

		ao->chunk = chunk;
		g_mutex_unlock(ao->mutex);

		success = ao_play_chunk(ao, chunk);

		g_mutex_lock(ao->mutex);

		if (!success) {
			assert(ao->chunk == NULL);
			break;
		}

		assert(ao->chunk == chunk);
		chunk = chunk->next;
	}

	ao->chunk_finished = true;
	g_mutex_unlock(ao->mutex);
158 159
}

160 161
static void ao_pause(struct audio_output *ao)
{
162 163 164 165 166 167 168 169 170 171 172 173
	bool ret;

	ao_plugin_cancel(ao->plugin, ao->data);
	ao_command_finished(ao);

	do {
		ret = ao_plugin_pause(ao->plugin, ao->data);
		if (!ret) {
			ao_close(ao);
			break;
		}
	} while (ao->command == AO_COMMAND_NONE);
174 175
}

176
static gpointer audio_output_task(gpointer arg)
177 178
{
	struct audio_output *ao = arg;
179
	bool ret;
180
	GError *error;
181 182 183 184 185 186 187 188

	while (1) {
		switch (ao->command) {
		case AO_COMMAND_NONE:
			break;

		case AO_COMMAND_OPEN:
			assert(!ao->open);
189
			assert(ao->fail_timer == NULL);
190 191
			assert(ao->pipe != NULL);
			assert(ao->chunk == NULL);
192

193
			error = NULL;
194
			ret = ao_plugin_open(ao->plugin, ao->data,
195 196
					     &ao->out_audio_format,
					     &error);
197 198

			assert(!ao->open);
199 200
			if (ret) {
				pcm_convert_init(&ao->convert_state);
201
				ao->open = true;
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217


				g_debug("opened plugin=%s name=\"%s\" "
					"audio_format=%u:%u:%u",
					ao->plugin->name,
					ao->name,
					ao->out_audio_format.sample_rate,
					ao->out_audio_format.bits,
					ao->out_audio_format.channels);

				if (!audio_format_equals(&ao->in_audio_format,
							 &ao->out_audio_format))
					g_debug("converting from %u:%u:%u",
						ao->in_audio_format.sample_rate,
						ao->in_audio_format.bits,
						ao->in_audio_format.channels);
218 219 220 221 222 223
			} else {
				g_warning("Failed to open \"%s\" [%s]: %s",
					  ao->name, ao->plugin->name,
					  error->message);
				g_error_free(error);

224
				ao->fail_timer = g_timer_new();
225
			}
226

227 228 229 230 231
			ao_command_finished(ao);
			break;

		case AO_COMMAND_CLOSE:
			assert(ao->open);
232 233 234 235
			assert(ao->pipe != NULL);

			ao->pipe = NULL;
			ao->chunk = NULL;
236

237
			ao_plugin_cancel(ao->plugin, ao->data);
238
			ao_close(ao);
239 240 241
			ao_command_finished(ao);
			break;

242 243 244 245
		case AO_COMMAND_PAUSE:
			ao_pause(ao);
			break;

246
		case AO_COMMAND_CANCEL:
247
			ao->chunk = NULL;
248
			ao_plugin_cancel(ao->plugin, ao->data);
249 250
			ao_command_finished(ao);

251 252 253 254 255
			/* the player thread will now clear our music
			   pipe - wait for a notify, to give it some
			   time */
			notify_wait(&ao->notify);
			continue;
256 257

		case AO_COMMAND_KILL:
258
			ao->chunk = NULL;
259 260 261 262
			ao_command_finished(ao);
			return NULL;
		}

263 264 265
		if (ao->open)
			ao_play(ao);

266 267 268 269 270 271
		notify_wait(&ao->notify);
	}
}

void audio_output_thread_start(struct audio_output *ao)
{
272
	GError *e = NULL;
273 274 275

	assert(ao->command == AO_COMMAND_NONE);

276
	if (!(ao->thread = g_thread_create(audio_output_task, ao, true, &e)))
277
		g_error("Failed to spawn output task: %s\n", e->message);
278
}