Thread.cxx 9.71 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2017 The Music Player Daemon Project
3
 * 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
#include "config.h"
21
#include "Control.hxx"
22
#include "Filtered.hxx"
23
#include "Client.hxx"
24
#include "Domain.hxx"
Max Kellermann's avatar
Max Kellermann committed
25
#include "notify.hxx"
26
#include "mixer/MixerInternal.hxx"
27
#include "thread/Util.hxx"
28
#include "thread/Slack.hxx"
29
#include "thread/Name.hxx"
30
#include "util/StringBuffer.hxx"
31
#include "util/ScopeExit.hxx"
32
#include "util/RuntimeError.hxx"
33
#include "Log.hxx"
34

35
#include <assert.h>
Max Kellermann's avatar
Max Kellermann committed
36
#include <string.h>
37

38
void
39
AudioOutputControl::CommandFinished() noexcept
40
{
41 42
	assert(command != Command::NONE);
	command = Command::NONE;
43

44
	const ScopeUnlock unlock(mutex);
Max Kellermann's avatar
Max Kellermann committed
45
	audio_output_client_notify.Signal();
46 47
}

48
inline void
49
AudioOutputControl::InternalOpen2(const AudioFormat in_audio_format)
50
{
51
	assert(in_audio_format.IsValid());
52

53
	const auto cf = in_audio_format.WithMask(output->config_audio_format);
54

55
	if (open && cf != output->filter_audio_format)
56 57
		/* if the filter's output format changes, the output
		   must be reopened as well */
58
		InternalCloseOutput(true);
59

60
	output->filter_audio_format = cf;
61

62
	if (!open) {
63
		{
64
			const ScopeUnlock unlock(mutex);
65
			output->OpenOutputAndConvert(output->filter_audio_format);
66
		}
67 68

		open = true;
69
	} else if (in_audio_format != output->out_audio_format) {
70 71 72 73
		/* reconfigure the final ConvertFilter for its new
		   input AudioFormat */

		try {
74
			output->ConfigureConvertFilter();
75
		} catch (...) {
76
			open = false;
77 78 79

			{
				const ScopeUnlock unlock(mutex);
80
				output->CloseOutput(false);
81 82
			}

83
			throw;
84
		}
85
	}
86 87 88 89 90

	{
		const ScopeUnlock unlock(mutex);
		output->OpenSoftwareMixer();
	}
91 92
}

93 94 95
inline bool
AudioOutputControl::InternalEnable() noexcept
{
96 97 98 99
	if (really_enabled)
		/* already enabled */
		return true;

100 101 102
	last_error = nullptr;

	try {
103 104 105 106 107
		{
			const ScopeUnlock unlock(mutex);
			output->Enable();
		}

108
		really_enabled = true;
109
		return true;
110 111
	} catch (...) {
		LogError(std::current_exception());
112
		Failure(std::current_exception());
113 114 115 116
		return false;
	}
}

117 118 119
inline void
AudioOutputControl::InternalDisable() noexcept
{
120 121 122
	if (!really_enabled)
		return;

123
	InternalCheckClose(false);
124

125
	really_enabled = false;
126 127

	const ScopeUnlock unlock(mutex);
128 129 130
	output->Disable();
}

131
inline void
132
AudioOutputControl::InternalOpen(const AudioFormat in_audio_format,
133 134
				 const MusicPipe &pipe) noexcept
{
135 136 137 138
	/* enable the device (just in case the last enable has failed) */
	if (!InternalEnable())
		return;

139 140
	last_error = nullptr;
	fail_timer.Reset();
141
	skip_delay = true;
142

143 144
	AudioFormat f;

145
	try {
146 147
		try {
			f = source.Open(in_audio_format, pipe,
148 149
					output->prepared_replay_gain_filter.get(),
					output->prepared_other_replay_gain_filter.get(),
150
					*output->prepared_filter);
151
		} catch (...) {
152 153
			std::throw_with_nested(FormatRuntimeError("Failed to open filter for %s",
								  GetLogName()));
154 155 156
		}

		try {
157
			InternalOpen2(f);
158
		} catch (...) {
159
			source.Close();
160 161
			throw;
		}
162 163
	} catch (...) {
		LogError(std::current_exception());
164
		Failure(std::current_exception());
165
	}
166 167 168 169 170 171

	if (f != in_audio_format || f != output->out_audio_format)
		FormatDebug(output_domain, "converting in=%s -> f=%s -> out=%s",
			    ToString(in_audio_format).c_str(),
			    ToString(f).c_str(),
			    ToString(output->out_audio_format).c_str());
172 173
}

174 175 176 177 178 179 180 181 182 183 184
inline void
AudioOutputControl::InternalCloseOutput(bool drain) noexcept
{
	assert(IsOpen());

	open = false;

	const ScopeUnlock unlock(mutex);
	output->CloseOutput(drain);
}

185
inline void
186
AudioOutputControl::InternalClose(bool drain) noexcept
187
{
188
	assert(IsOpen());
189

190
	open = false;
191 192 193 194 195 196

	{
		const ScopeUnlock unlock(mutex);
		output->Close(drain);
	}

197
	source.Close();
198 199
}

200 201 202 203 204 205 206
inline void
AudioOutputControl::InternalCheckClose(bool drain) noexcept
{
	if (IsOpen())
		InternalClose(drain);
}

207 208 209 210 211 212
/**
 * Wait until the output's delay reaches zero.
 *
 * @return true if playback should be continued, false if a command
 * was issued
 */
213
inline bool
214
AudioOutputControl::WaitForDelay() noexcept
215 216
{
	while (true) {
217
		const auto delay = output->Delay();
218
		if (delay <= std::chrono::steady_clock::duration::zero())
219 220
			return true;

221
		(void)cond.timed_wait(mutex, delay);
222

223
		if (command != Command::NONE)
224 225 226 227
			return false;
	}
}

228
bool
229
AudioOutputControl::FillSourceOrClose() noexcept
230
try {
231
	return source.Fill(mutex);
232 233 234
} catch (...) {
	FormatError(std::current_exception(),
		    "Failed to filter for %s", GetLogName());
235
	InternalCloseError(std::current_exception());
236 237 238
	return false;
}

239
inline bool
240
AudioOutputControl::PlayChunk() noexcept
241
{
242 243 244 245 246
	// ensure pending tags are flushed in all cases
	const auto *tag = source.ReadTag();
	if (tags && tag != nullptr) {
		const ScopeUnlock unlock(mutex);
		try {
247
			output->SendTag(*tag);
248 249 250
		} catch (...) {
			FormatError(std::current_exception(),
				    "Failed to send tag to %s",
251
				    GetLogName());
252
		}
253 254
	}

255
	while (command == Command::NONE) {
256
		const auto data = source.PeekData();
257
		if (data.empty())
258
			break;
259

260 261 262
		if (skip_delay)
			skip_delay = false;
		else if (!WaitForDelay())
263 264
			break;

265 266
		size_t nbytes;

267
		try {
268
			const ScopeUnlock unlock(mutex);
269
			nbytes = output->Play(data.data, data.size);
270
			assert(nbytes > 0);
271
			assert(nbytes <= data.size);
272 273 274
		} catch (...) {
			FormatError(std::current_exception(),
				    "Failed to play on %s", GetLogName());
275
			InternalCloseError(std::current_exception());
276
			return false;
277 278
		}

279
		assert(nbytes % output->out_audio_format.GetFrameSize() == 0);
280

281
		source.ConsumeData(nbytes);
282 283
	}

284 285 286
	return true;
}

287
inline bool
288
AudioOutputControl::InternalPlay() noexcept
289
{
290
	if (!FillSourceOrClose())
291 292
		/* no chunk available */
		return false;
293

294 295
	assert(!in_playback_loop);
	in_playback_loop = true;
296

297 298 299 300 301
	AtScopeExit(this) {
		assert(in_playback_loop);
		in_playback_loop = false;
	};

302 303
	unsigned n = 0;

304
	do {
305 306 307
		if (command != Command::NONE)
			return true;

308 309 310 311 312
		if (++n >= 64) {
			/* wake up the player every now and then to
			   give it a chance to refill the pipe before
			   it runs empty */
			const ScopeUnlock unlock(mutex);
313
			client.ChunksConsumed();
314 315 316
			n = 0;
		}

317
		if (!PlayChunk())
318
			break;
319
	} while (FillSourceOrClose());
320

321
	const ScopeUnlock unlock(mutex);
322
	client.ChunksConsumed();
323 324

	return true;
325 326
}

327
inline void
328
AudioOutputControl::InternalPause() noexcept
329
{
330 331 332 333 334
	{
		const ScopeUnlock unlock(mutex);
		output->BeginPause();
	}

335 336
	pause = true;

337
	CommandFinished();
338 339

	do {
340
		if (!WaitForDelay())
341 342
			break;

343 344 345 346 347 348 349
		bool success;
		{
			const ScopeUnlock unlock(mutex);
			success = output->IteratePause();
		}

		if (!success) {
350
			InternalClose(false);
351
			break;
352
		}
353
	} while (command == Command::NONE);
354

355
	pause = false;
356 357 358 359 360

	{
		const ScopeUnlock unlock(mutex);
		output->EndPause();
	}
361 362

	skip_delay = true;
363 364
}

365 366 367 368 369 370 371 372 373 374 375 376 377 378
static void
PlayFull(FilteredAudioOutput &output, ConstBuffer<void> _buffer)
{
	auto buffer = ConstBuffer<uint8_t>::FromVoid(_buffer);

	while (!buffer.empty()) {
		size_t nbytes = output.Play(buffer.data, buffer.size);
		assert(nbytes > 0);

		buffer.skip_front(nbytes);
	}

}

379 380 381
inline void
AudioOutputControl::InternalDrain() noexcept
{
382 383 384
	try {
		/* flush the filter and play its remaining output */

385 386
		const ScopeUnlock unlock(mutex);

387 388 389 390 391 392 393
		while (true) {
			auto buffer = source.Flush();
			if (buffer.IsNull())
				break;

			PlayFull(*output, buffer);
		}
394 395

		output->Drain();
396 397 398 399 400 401
	} catch (...) {
		FormatError(std::current_exception(),
			    "Failed to flush filter on %s", GetLogName());
		InternalCloseError(std::current_exception());
		return;
	}
402 403
}

404
void
405
AudioOutputControl::Task() noexcept
406
{
407
	FormatThreadName("output:%s", GetName());
408

409 410
	try {
		SetThreadRealtime();
411 412
	} catch (...) {
		LogError(std::current_exception(),
413
			 "OutputThread could not get realtime scheduling, continuing anyway");
414
	}
415

416
	SetThreadTimerSlackUS(100);
417

418
	const std::lock_guard<Mutex> lock(mutex);
419

420
	while (true) {
421
		switch (command) {
422
		case Command::NONE:
423 424
			break;

425
		case Command::ENABLE:
426
			InternalEnable();
427
			CommandFinished();
428 429
			break;

430
		case Command::DISABLE:
431
			InternalDisable();
432
			CommandFinished();
433 434
			break;

435
		case Command::OPEN:
436
			InternalOpen(request.audio_format, *request.pipe);
437
			CommandFinished();
438 439
			break;

440
		case Command::CLOSE:
441
			InternalCheckClose(false);
442
			CommandFinished();
443 444
			break;

445
		case Command::PAUSE:
446
			if (!open) {
447 448 449 450
				/* the output has failed after
				   audio_output_all_pause() has
				   submitted the PAUSE command; bail
				   out */
451
				CommandFinished();
452 453 454
				break;
			}

455
			InternalPause();
456
			/* don't "break" here: this might cause
457
			   Play() to be called when command==CLOSE
458 459 460
			   ends the paused state - "continue" checks
			   the new command first */
			continue;
461

462
		case Command::DRAIN:
463 464
			if (open)
				InternalDrain();
465

466
			CommandFinished();
467 468
			continue;

469
		case Command::CANCEL:
470
			source.Cancel();
471

472
			if (open) {
473
				const ScopeUnlock unlock(mutex);
474
				output->Cancel();
475 476
			}

477
			CommandFinished();
478
			continue;
479

480
		case Command::KILL:
481
			InternalDisable();
482
			source.Cancel();
483
			CommandFinished();
484
			return;
485 486
		}

487
		if (open && allow_play && InternalPlay())
488 489 490
			/* don't wait for an event if there are more
			   chunks in the pipe */
			continue;
491

492
		if (command == Command::NONE) {
493
			woken_for_play = false;
494
			cond.wait(mutex);
495
		}
496 497 498
	}
}

499
void
500
AudioOutputControl::StartThread()
501
{
502
	assert(command == Command::NONE);
503

504
	const ScopeUnlock unlock(mutex);
505
	thread.Start();
506
}