Thread.cxx 10.3 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2018 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 "Filtered.hxx"
22
#include "Client.hxx"
23
#include "Domain.hxx"
24
#include "mixer/MixerInternal.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
#include <assert.h>
Max Kellermann's avatar
Max Kellermann committed
34
#include <string.h>
35

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

42
	client_cond.signal();
43 44
}

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

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

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

57
	output->filter_audio_format = cf;
58

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

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

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

			{
				const ScopeUnlock unlock(mutex);
77
				output->CloseOutput(false);
78 79
			}

80
			throw;
81
		}
82
	}
83 84 85 86 87

	{
		const ScopeUnlock unlock(mutex);
		output->OpenSoftwareMixer();
	}
88 89
}

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

97 98 99
	last_error = nullptr;

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

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

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

120
	InternalCheckClose(false);
121

122
	really_enabled = false;
123 124

	const ScopeUnlock unlock(mutex);
125 126 127
	output->Disable();
}

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

136 137
	last_error = nullptr;
	fail_timer.Reset();
138
	skip_delay = true;
139

140 141
	AudioFormat f;

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

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

	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());
170 171
}

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

	open = false;

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

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

188
	open = false;
189 190 191 192 193 194

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

195
	source.Close();
196 197
}

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

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

219
		(void)wake_cond.timed_wait(mutex, delay);
220

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

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

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

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

258 259 260
		if (skip_delay)
			skip_delay = false;
		else if (!WaitForDelay())
261 262
			break;

263 264
		size_t nbytes;

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

277
		assert(nbytes % output->out_audio_format.GetFrameSize() == 0);
278

279
		source.ConsumeData(nbytes);
280 281
	}

282 283 284
	return true;
}

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

292 293
	assert(!in_playback_loop);
	in_playback_loop = true;
294

295 296 297 298 299
	AtScopeExit(this) {
		assert(in_playback_loop);
		in_playback_loop = false;
	};

300 301
	unsigned n = 0;

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

306 307 308 309 310
		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);
311
			client.ChunksConsumed();
312 313 314
			n = 0;
		}

315
		if (!PlayChunk())
316
			break;
317
	} while (FillSourceOrClose());
318

319
	const ScopeUnlock unlock(mutex);
320
	client.ChunksConsumed();
321 322

	return true;
323 324
}

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

333 334
	pause = true;

335
	CommandFinished();
336 337

	do {
338
		if (!WaitForDelay())
339 340
			break;

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

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

353
	pause = false;
354 355 356 357 358

	{
		const ScopeUnlock unlock(mutex);
		output->EndPause();
	}
359 360

	skip_delay = true;
361 362
}

363 364 365 366 367 368 369 370 371 372 373 374 375 376
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);
	}

}

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

383 384
		const ScopeUnlock unlock(mutex);

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

			PlayFull(*output, buffer);
		}
392 393

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

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

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

414
	SetThreadTimerSlackUS(100);
415

416
	const std::lock_guard<Mutex> lock(mutex);
417

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

423
		case Command::ENABLE:
424
			InternalEnable();
425
			CommandFinished();
426 427
			break;

428
		case Command::DISABLE:
429
			InternalDisable();
430
			CommandFinished();
431 432
			break;

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

438
		case Command::CLOSE:
439
			InternalCheckClose(false);
440
			CommandFinished();
441 442
			break;

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

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

459 460 461
		case Command::RELEASE:
			if (!open) {
				/* the output has failed after
462
				   the RELEASE command was submitted; bail
463 464 465 466 467 468 469
				   out */
				CommandFinished();
				break;
			}

			if (always_on) {
				/* in "always_on" mode, the output is
470
				   paused instead of being closed;
471
				   however we need to flush the
472 473 474
				   AudioOutputSource because its data
				   have been invalidated by stopping
				   the actual playback */
475
				source.Cancel();
476 477 478 479 480 481 482 483 484 485 486 487
				InternalPause();
			} else {
				InternalClose(false);
				CommandFinished();
			}

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

488
		case Command::DRAIN:
489 490
			if (open)
				InternalDrain();
491

492
			CommandFinished();
493 494
			continue;

495
		case Command::CANCEL:
496
			source.Cancel();
497

498
			if (open) {
499
				const ScopeUnlock unlock(mutex);
500
				output->Cancel();
501 502
			}

503
			CommandFinished();
504
			continue;
505

506
		case Command::KILL:
507
			InternalDisable();
508
			source.Cancel();
509
			CommandFinished();
510
			return;
511 512
		}

513
		if (open && allow_play && InternalPlay())
514 515 516
			/* don't wait for an event if there are more
			   chunks in the pipe */
			continue;
517

518
		if (command == Command::NONE) {
519
			woken_for_play = false;
520
			wake_cond.wait(mutex);
521
		}
522 523 524
	}
}

525
void
526
AudioOutputControl::StartThread()
527
{
528
	assert(command == Command::NONE);
529

530
	const ScopeUnlock unlock(mutex);
531
	thread.Start();
532
}