Thread.cxx 10.5 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2019 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.notify_one();
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
	}
163 164 165 166 167 168

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

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

	open = false;

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

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

187
	open = false;
188 189 190 191 192 193

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

194
	source.Close();
195 196
}

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

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

218
		(void)wake_cond.wait_for(lock, delay);
219

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

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

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

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

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

262 263
		size_t nbytes;

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

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

278
		source.ConsumeData(nbytes);
279 280
	}

281 282 283
	return true;
}

284
inline bool
285
AudioOutputControl::InternalPlay(std::unique_lock<Mutex> &lock) noexcept
286
{
287
	if (!FillSourceOrClose())
288 289
		/* no chunk available */
		return false;
290

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

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

299 300
	unsigned n = 0;

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

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

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

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

	return true;
322 323
}

324
inline void
325
AudioOutputControl::InternalPause(std::unique_lock<Mutex> &lock) noexcept
326
{
327 328 329 330 331
	{
		const ScopeUnlock unlock(mutex);
		output->BeginPause();
	}

332 333
	pause = true;

334
	CommandFinished();
335 336

	do {
337
		if (!WaitForDelay(lock))
338 339
			break;

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

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

352
	pause = false;
353 354 355 356 357

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

	skip_delay = true;
360 361
}

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

}

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

382 383
		const ScopeUnlock unlock(mutex);

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

			PlayFull(*output, buffer);
		}
391 392

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

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

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

413
	SetThreadTimerSlack(std::chrono::microseconds(100));
414

415
	std::unique_lock<Mutex> lock(mutex);
416

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

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

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

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

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

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

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

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

			if (always_on) {
				/* in "always_on" mode, the output is
469
				   paused instead of being closed;
470
				   however we need to flush the
471 472 473
				   AudioOutputSource because its data
				   have been invalidated by stopping
				   the actual playback */
474
				source.Cancel();
475
				InternalPause(lock);
476 477 478 479 480 481 482 483 484 485 486
			} 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;

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

491
			CommandFinished();
492 493
			continue;

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

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

502
			CommandFinished();
503
			continue;
504

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

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

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

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

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