Thread.cxx 9.23 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 (const std::runtime_error &e) {
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 110 111 112 113 114 115 116 117
		return true;
	} catch (const std::runtime_error &e) {
		LogError(e);
		fail_timer.Update();
		last_error = std::current_exception();
		return false;
	}
}

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

124
	InternalCheckClose(false);
125

126
	really_enabled = false;
127 128

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

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

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

144 145
	AudioFormat f;

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

		try {
158
			InternalOpen2(f);
159
		} catch (...) {
160
			source.Close();
161 162
			throw;
		}
163 164 165 166 167
	} catch (const std::runtime_error &e) {
		LogError(e);
		fail_timer.Update();
		last_error = std::current_exception();
	}
168 169 170 171 172 173

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

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

	open = false;

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

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

192
	open = false;
193 194 195 196 197 198

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

199
	source.Close();
200 201
}

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

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

223
		(void)cond.timed_wait(mutex, delay);
224

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

230
bool
231
AudioOutputControl::FillSourceOrClose()
232
try {
233
	return source.Fill(mutex);
234
} catch (const std::runtime_error &e) {
235
	FormatError(e, "Failed to filter for %s", GetLogName());
236

237
	InternalClose(false);
238 239 240 241 242 243 244

	/* don't automatically reopen this device for 10
	   seconds */
	fail_timer.Update();
	return false;
}

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

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

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

270 271
		size_t nbytes;

272
		try {
273
			const ScopeUnlock unlock(mutex);
274
			nbytes = output->Play(data.data, data.size);
275
			assert(nbytes <= data.size);
276
		} catch (const std::runtime_error &e) {
277
			FormatError(e, "Failed to play on %s", GetLogName());
278 279 280 281
			nbytes = 0;
		}

		if (nbytes == 0) {
282
			InternalClose(false);
283 284 285

			/* don't automatically reopen this device for
			   10 seconds */
286 287
			assert(!fail_timer.IsDefined());
			fail_timer.Update();
288

289
			return false;
290 291
		}

292
		assert(nbytes % output->out_audio_format.GetFrameSize() == 0);
293

294
		source.ConsumeData(nbytes);
295 296
	}

297 298 299
	return true;
}

300
inline bool
301
AudioOutputControl::InternalPlay() noexcept
302
{
303
	if (!FillSourceOrClose())
304 305
		/* no chunk available */
		return false;
306

307 308
	assert(!in_playback_loop);
	in_playback_loop = true;
309

310 311 312 313 314
	AtScopeExit(this) {
		assert(in_playback_loop);
		in_playback_loop = false;
	};

315 316
	unsigned n = 0;

317
	do {
318 319 320
		if (command != Command::NONE)
			return true;

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

330
		if (!PlayChunk())
331
			break;
332
	} while (FillSourceOrClose());
333

334
	const ScopeUnlock unlock(mutex);
335
	client.ChunksConsumed();
336 337

	return true;
338 339
}

340
inline void
341
AudioOutputControl::InternalPause() noexcept
342
{
343 344 345 346 347
	{
		const ScopeUnlock unlock(mutex);
		output->BeginPause();
	}

348 349
	pause = true;

350
	CommandFinished();
351 352

	do {
353
		if (!WaitForDelay())
354 355
			break;

356 357 358 359 360 361 362
		bool success;
		{
			const ScopeUnlock unlock(mutex);
			success = output->IteratePause();
		}

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

368
	pause = false;
369 370 371 372 373

	{
		const ScopeUnlock unlock(mutex);
		output->EndPause();
	}
374 375

	skip_delay = true;
376 377
}

378
void
379
AudioOutputControl::Task()
380
{
381
	FormatThreadName("output:%s", GetName());
382

383 384 385 386 387
	try {
		SetThreadRealtime();
	} catch (const std::runtime_error &e) {
		LogError(e,
			 "OutputThread could not get realtime scheduling, continuing anyway");
388
	}
389

390
	SetThreadTimerSlackUS(100);
391

392
	const std::lock_guard<Mutex> lock(mutex);
393

394
	while (true) {
395
		switch (command) {
396
		case Command::NONE:
397 398
			break;

399
		case Command::ENABLE:
400
			InternalEnable();
401
			CommandFinished();
402 403
			break;

404
		case Command::DISABLE:
405
			InternalDisable();
406
			CommandFinished();
407 408
			break;

409
		case Command::OPEN:
410
			InternalOpen(request.audio_format, *request.pipe);
411
			CommandFinished();
412 413
			break;

414
		case Command::CLOSE:
415
			InternalCheckClose(false);
416
			CommandFinished();
417 418
			break;

419
		case Command::PAUSE:
420
			if (!open) {
421 422 423 424
				/* the output has failed after
				   audio_output_all_pause() has
				   submitted the PAUSE command; bail
				   out */
425
				CommandFinished();
426 427 428
				break;
			}

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

436
		case Command::DRAIN:
437
			if (open) {
438
				const ScopeUnlock unlock(mutex);
439
				output->Drain();
440 441
			}

442
			CommandFinished();
443 444
			continue;

445
		case Command::CANCEL:
446
			source.Cancel();
447

448
			if (open) {
449
				const ScopeUnlock unlock(mutex);
450
				output->Cancel();
451 452
			}

453
			CommandFinished();
454
			continue;
455

456
		case Command::KILL:
457
			InternalDisable();
458
			source.Cancel();
459
			CommandFinished();
460
			return;
461 462
		}

463
		if (open && allow_play && InternalPlay())
464 465 466
			/* don't wait for an event if there are more
			   chunks in the pipe */
			continue;
467

468
		if (command == Command::NONE) {
469
			woken_for_play = false;
470
			cond.wait(mutex);
471
		}
472 473 474
	}
}

475
void
476
AudioOutputControl::StartThread()
477
{
478
	assert(command == Command::NONE);
479

480
	thread.Start();
481
}