Thread.cxx 10.9 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2021 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 "Control.hxx"
21
#include "Error.hxx"
22
#include "Filtered.hxx"
23
#include "Client.hxx"
24
#include "Domain.hxx"
25
#include "thread/Util.hxx"
26
#include "thread/Slack.hxx"
27
#include "thread/Name.hxx"
28
#include "util/StringBuffer.hxx"
29
#include "util/ScopeExit.hxx"
30
#include "util/RuntimeError.hxx"
31
#include "Log.hxx"
32

33 34
#include <cassert>

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

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

43
	client_cond.notify_one();
44 45
}

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

51
	const auto cf = in_audio_format.WithMask(output->config_audio_format);
52

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

58
	output->filter_audio_format = cf;
59

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

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

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

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

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

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

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

99 100 101
	last_error = nullptr;

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

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

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

122
	InternalCheckClose(false);
123

124
	really_enabled = false;
125 126

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

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

138 139
	last_error = nullptr;
	fail_timer.Reset();
140
	caught_interrupted = false;
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
		return;
166
	}
167 168 169 170 171 172

	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());
173 174
}

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

	open = false;

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

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

191
	open = false;
192 193 194 195 196 197

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

198
	source.Close();
199 200
}

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

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

222
		(void)wake_cond.wait_for(lock, delay);
223

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

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

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

259
	while (command == Command::NONE) {
260
		const auto data = source.PeekData();
261
		if (data.empty())
262
			break;
263

264 265
		if (skip_delay)
			skip_delay = false;
266
		else if (!WaitForDelay(lock))
267 268
			break;

269 270
		size_t nbytes;

271
		try {
272
			const ScopeUnlock unlock(mutex);
273
			nbytes = output->Play(data.data, data.size);
274
			assert(nbytes > 0);
275
			assert(nbytes <= data.size);
276 277 278
		} catch (AudioOutputInterrupted) {
			caught_interrupted = true;
			return false;
279 280 281
		} catch (...) {
			FormatError(std::current_exception(),
				    "Failed to play on %s", GetLogName());
282
			InternalCloseError(std::current_exception());
283
			return false;
284 285
		}

286
		assert(nbytes % output->out_audio_format.GetFrameSize() == 0);
287

288
		source.ConsumeData(nbytes);
289 290 291

		/* there's data to be drained from now on */
		playing = true;
292 293
	}

294 295 296
	return true;
}

297
inline bool
298
AudioOutputControl::InternalPlay(std::unique_lock<Mutex> &lock) noexcept
299
{
300
	if (!FillSourceOrClose())
301 302
		/* no chunk available */
		return false;
303

304 305
	assert(!in_playback_loop);
	in_playback_loop = true;
306

307 308 309 310 311
	AtScopeExit(this) {
		assert(in_playback_loop);
		in_playback_loop = false;
	};

312 313
	unsigned n = 0;

314
	do {
315 316 317
		if (command != Command::NONE)
			return true;

318 319 320 321 322
		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);
323
			client.ChunksConsumed();
324 325 326
			n = 0;
		}

327
		if (!PlayChunk(lock))
328
			break;
329
	} while (FillSourceOrClose());
330

331
	const ScopeUnlock unlock(mutex);
332
	client.ChunksConsumed();
333 334

	return true;
335 336
}

337
inline void
338
AudioOutputControl::InternalPause(std::unique_lock<Mutex> &lock) noexcept
339
{
340 341 342 343 344
	{
		const ScopeUnlock unlock(mutex);
		output->BeginPause();
	}

345 346
	pause = true;

347
	CommandFinished();
348 349

	do {
350
		if (!WaitForDelay(lock))
351 352
			break;

353 354
		bool success = false;
		try {
355 356
			const ScopeUnlock unlock(mutex);
			success = output->IteratePause();
357
		} catch (AudioOutputInterrupted) {
358 359 360 361
		} catch (...) {
			FormatError(std::current_exception(),
				    "Failed to pause %s",
				    GetLogName());
362 363 364
		}

		if (!success) {
365
			InternalClose(false);
366
			break;
367
		}
368
	} while (command == Command::NONE);
369

370
	pause = false;
371 372 373 374 375

	{
		const ScopeUnlock unlock(mutex);
		output->EndPause();
	}
376 377

	skip_delay = true;
378 379 380

	/* ignore drain commands until we got something new to play */
	playing = false;
381 382
}

383 384 385 386 387 388 389 390 391 392 393 394 395 396
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);
	}

}

397 398 399
inline void
AudioOutputControl::InternalDrain() noexcept
{
400 401 402 403
	/* after this method finishes, there's nothing left to be
	   drained */
	playing = false;

404 405 406
	try {
		/* flush the filter and play its remaining output */

407 408
		const ScopeUnlock unlock(mutex);

409 410 411 412 413 414 415
		while (true) {
			auto buffer = source.Flush();
			if (buffer.IsNull())
				break;

			PlayFull(*output, buffer);
		}
416 417

		output->Drain();
418 419 420 421 422 423
	} catch (...) {
		FormatError(std::current_exception(),
			    "Failed to flush filter on %s", GetLogName());
		InternalCloseError(std::current_exception());
		return;
	}
424 425
}

426
void
427
AudioOutputControl::Task() noexcept
428
{
429
	FormatThreadName("output:%s", GetName());
430

431 432
	try {
		SetThreadRealtime();
433
	} catch (...) {
434 435
		Log(LogLevel::INFO, std::current_exception(),
		    "OutputThread could not get realtime scheduling, continuing anyway");
436
	}
437

438
	SetThreadTimerSlack(std::chrono::microseconds(100));
439

440
	std::unique_lock<Mutex> lock(mutex);
441

442
	while (true) {
443
		switch (command) {
444
		case Command::NONE:
445 446 447
			/* no pending command: play (or wait for a
			   command) */

448 449
			if (open && allow_play && !caught_interrupted &&
			    InternalPlay(lock))
450 451 452 453 454 455
				/* don't wait for an event if there
				   are more chunks in the pipe */
				continue;

			woken_for_play = false;
			wake_cond.wait(lock);
456 457
			break;

458
		case Command::ENABLE:
459
			InternalEnable();
460
			CommandFinished();
461 462
			break;

463
		case Command::DISABLE:
464
			InternalDisable();
465
			CommandFinished();
466 467
			break;

468
		case Command::OPEN:
469
			InternalOpen(request.audio_format, *request.pipe);
470
			CommandFinished();
471 472
			break;

473
		case Command::CLOSE:
474
			InternalCheckClose(false);
475
			CommandFinished();
476 477
			break;

478
		case Command::PAUSE:
479
			if (!open) {
480
				/* the output has failed after
481
				   the PAUSE command was submitted; bail
482
				   out */
483
				CommandFinished();
484 485 486
				break;
			}

487 488
			caught_interrupted = false;

489
			InternalPause(lock);
490
			break;
491

492 493 494
		case Command::RELEASE:
			if (!open) {
				/* the output has failed after
495
				   the RELEASE command was submitted; bail
496 497 498 499 500
				   out */
				CommandFinished();
				break;
			}

501 502
			caught_interrupted = false;

503 504
			if (always_on) {
				/* in "always_on" mode, the output is
505
				   paused instead of being closed;
506
				   however we need to flush the
507 508 509
				   AudioOutputSource because its data
				   have been invalidated by stopping
				   the actual playback */
510
				source.Cancel();
511
				InternalPause(lock);
512 513 514 515 516
			} else {
				InternalClose(false);
				CommandFinished();
			}

517
			break;
518

519
		case Command::DRAIN:
520 521
			if (open)
				InternalDrain();
522

523
			CommandFinished();
524
			break;
525

526
		case Command::CANCEL:
527 528
			caught_interrupted = false;

529
			source.Cancel();
530

531
			if (open) {
532
				playing = false;
533
				const ScopeUnlock unlock(mutex);
534
				output->Cancel();
535 536
			}

537
			CommandFinished();
538
			break;
539

540
		case Command::KILL:
541
			InternalDisable();
542
			source.Cancel();
543
			CommandFinished();
544
			return;
545 546 547 548
		}
	}
}

549
void
550
AudioOutputControl::StartThread()
551
{
552
	assert(command == Command::NONE);
553

554 555
	killed = false;

556
	const ScopeUnlock unlock(mutex);
557
	thread.Start();
558
}