SlesOutputPlugin.cxx 11.2 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2021 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * http://www.musicpd.org
 *
 * 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.
 *
 * 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.
 */

#include "SlesOutputPlugin.hxx"
#include "Object.hxx"
#include "Engine.hxx"
#include "Play.hxx"
#include "AndroidSimpleBufferQueue.hxx"
#include "../../OutputAPI.hxx"
26
#include "thread/Mutex.hxx"
27
#include "thread/Cond.hxx"
28
#include "util/Domain.hxx"
29
#include "util/ByteOrder.hxx"
30
#include "mixer/MixerList.hxx"
31 32 33 34 35
#include "Log.hxx"

#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

36
#include <cassert>
37
#include <iterator>
38 39
#include <stdexcept>

40
class SlesOutput final : AudioOutput  {
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
	static constexpr unsigned N_BUFFERS = 3;
	static constexpr size_t BUFFER_SIZE = 65536;

	SLES::Object engine_object, mix_object, play_object;
	SLES::Play play;
	SLES::AndroidSimpleBufferQueue queue;

	/**
	 * This mutex protects the attributes "next" and "filled".  It
	 * is only needed while playback is launched, when the initial
	 * buffers are being enqueued in the caller thread, while
	 * another thread may invoke the registered callback.
	 */
	Mutex mutex;

	Cond cond;

	bool pause, cancel;

	/**
	 * The number of buffers queued to OpenSLES.
	 */
	unsigned n_queued;

	/**
	 * The index of the next buffer to be enqueued.
	 */
	unsigned next;

	/**
	 * Does the "next" buffer already contain synthesised samples?
	 * This can happen when PCMSynthesiser::Synthesise() has been
	 * called, but the OpenSL/ES buffer queue was full.  The
	 * buffer will then be postponed.
	 */
	unsigned filled;

	/**
	 * An array of buffers.  It's one more than being managed by
	 * OpenSL/ES, and the one not enqueued (see attribute #next)
	 * will be written to.
	 */
	uint8_t buffers[N_BUFFERS][BUFFER_SIZE];

85
	SlesOutput():AudioOutput(FLAG_PAUSE) {}
86

87 88 89
public:
	static AudioOutput *Create(EventLoop &, const ConfigBlock &) {
		return new SlesOutput();
90 91
	}

92 93 94
private:
	void Open(AudioFormat &audio_format) override;
	void Close() noexcept override;
95

96
	std::chrono::steady_clock::duration Delay() const noexcept override {
97 98 99
		return pause && !cancel
			? std::chrono::milliseconds(100)
			: std::chrono::steady_clock::duration::zero();
100 101
	}

102
	size_t Play(const void *chunk, size_t size) override;
103

104 105
	void Drain() override;
	void Cancel() noexcept override;
106
	bool Pause() override;
107 108 109 110 111 112 113 114 115

private:
	void PlayedCallback();

	/**
	 * OpenSL/ES callback which gets invoked when a buffer has
	 * been consumed.  It synthesises and enqueues the next
	 * buffer.
	 */
Rosen Penev's avatar
Rosen Penev committed
116
	static void PlayedCallback([[maybe_unused]] SLAndroidSimpleBufferQueueItf caller,
117 118 119 120 121 122 123 124 125
				   void *pContext)
	{
		SlesOutput &sles = *(SlesOutput *)pContext;
		sles.PlayedCallback();
	}
};

static constexpr Domain sles_domain("sles");

126
void
127
SlesOutput::Open(AudioFormat &audio_format)
128 129 130 131 132 133
{
	SLresult result;
	SLObjectItf _object;

	result = slCreateEngine(&_object, 0, nullptr, 0,
				nullptr, nullptr);
134 135
	if (result != SL_RESULT_SUCCESS)
		throw std::runtime_error("slCreateEngine() failed");
136 137 138 139 140 141

	engine_object = SLES::Object(_object);

	result = engine_object.Realize(false);
	if (result != SL_RESULT_SUCCESS) {
		engine_object.Destroy();
142
		throw std::runtime_error("Engine.Realize() failed");
143 144 145 146 147 148
	}

	SLEngineItf _engine;
	result = engine_object.GetInterface(SL_IID_ENGINE, &_engine);
	if (result != SL_RESULT_SUCCESS) {
		engine_object.Destroy();
149
		throw std::runtime_error("Engine.GetInterface(IID_ENGINE) failed");
150 151 152 153 154 155 156
	}

	SLES::Engine engine(_engine);

	result = engine.CreateOutputMix(&_object, 0, nullptr, nullptr);
	if (result != SL_RESULT_SUCCESS) {
		engine_object.Destroy();
157
		throw std::runtime_error("Engine.CreateOutputMix() failed");
158 159 160 161 162 163 164 165
	}

	mix_object = SLES::Object(_object);

	result = mix_object.Realize(false);
	if (result != SL_RESULT_SUCCESS) {
		mix_object.Destroy();
		engine_object.Destroy();
166
		throw std::runtime_error("Mix.Realize() failed");
167 168 169 170 171 172 173
	}

	SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
		SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
		N_BUFFERS,
	};

174 175 176
	if (audio_format.channels > 2)
		audio_format.channels = 1;

177
	SLAndroidDataFormat_PCM_EX format_pcm;
178
	format_pcm.formatType = SL_DATAFORMAT_PCM;
179
	format_pcm.numChannels = audio_format.channels;
180 181
	/* from the Android NDK docs: "Note that the field samplesPerSec is
	   actually in units of milliHz, despite the misleading name." */
182
	format_pcm.sampleRate = audio_format.sample_rate * 1000u;
183 184
	format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
	format_pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
185 186 187
	format_pcm.channelMask = audio_format.channels == 1
		? SL_SPEAKER_FRONT_CENTER
		: SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
188 189 190
	format_pcm.endianness = IsLittleEndian()
		? SL_BYTEORDER_LITTLEENDIAN
		: SL_BYTEORDER_BIGENDIAN;
191
	format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
192

193 194 195 196 197 198 199 200 201 202 203 204 205
	switch (audio_format.format) {
		/* note: Android doesn't support
		   SL_PCMSAMPLEFORMAT_FIXED_24 and
		   SL_PCMSAMPLEFORMAT_FIXED_32, so let's not bother
		   implement it here; SL_PCMSAMPLEFORMAT_FIXED_8
		   appears to be unsigned, so not usable for us (and
		   converting S8 to U8 is not worth the trouble) */

	case SampleFormat::S16:
		/* bitsPerSample and containerSize already set for 16
		   bit */
		break;

206 207 208 209 210 211 212 213 214 215
	case SampleFormat::FLOAT:
		/* Android has an OpenSLES extension for floating
		   point samples:
		   https://developer.android.com/ndk/guides/audio/opensl/android-extensions */
		format_pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
		format_pcm.bitsPerSample = format_pcm.containerSize =
			SL_PCMSAMPLEFORMAT_FIXED_32;
		format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
		break;

216 217 218 219 220
	default:
		/* fall back to 16 bit */
		audio_format.format = SampleFormat::S16;
		break;
	}
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

	SLDataSource audioSrc = { &loc_bufq, &format_pcm };

	SLDataLocator_OutputMix loc_outmix = {
		SL_DATALOCATOR_OUTPUTMIX,
		mix_object,
	};

	SLDataSink audioSnk = {
		&loc_outmix,
		nullptr,
	};

	const SLInterfaceID ids2[] = {
		SL_IID_PLAY,
		SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
		SL_IID_ANDROIDCONFIGURATION,
	};

	static constexpr SLboolean req2[] = {
		SL_BOOLEAN_TRUE,
		SL_BOOLEAN_TRUE,
		SL_BOOLEAN_TRUE,
	};

	result = engine.CreateAudioPlayer(&_object, &audioSrc, &audioSnk,
247
					  std::size(ids2), ids2, req2);
248 249 250
	if (result != SL_RESULT_SUCCESS) {
		mix_object.Destroy();
		engine_object.Destroy();
251
		throw std::runtime_error("Engine.CreateAudioPlayer() failed");
252 253 254 255 256 257 258 259 260 261 262 263
	}

	play_object = SLES::Object(_object);

	SLAndroidConfigurationItf android_config;
	if (play_object.GetInterface(SL_IID_ANDROIDCONFIGURATION,
				     &android_config) == SL_RESULT_SUCCESS) {
		SLint32 stream_type = SL_ANDROID_STREAM_MEDIA;
		(*android_config)->SetConfiguration(android_config,
						    SL_ANDROID_KEY_STREAM_TYPE,
						    &stream_type,
						    sizeof(stream_type));
264 265 266 267 268 269 270 271

		/* MPD doesn't care much about latency, so let's
		   configure power saving mode */
		SLuint32 performance_mode = SL_ANDROID_PERFORMANCE_POWER_SAVING;
		(*android_config)->SetConfiguration(android_config,
						    SL_ANDROID_KEY_PERFORMANCE_MODE,
						    &performance_mode,
						    sizeof(performance_mode));
272 273 274 275 276 277 278 279
	}

	result = play_object.Realize(false);

	if (result != SL_RESULT_SUCCESS) {
		play_object.Destroy();
		mix_object.Destroy();
		engine_object.Destroy();
280
		throw std::runtime_error("Play.Realize() failed");
281 282 283 284 285 286 287 288
	}

	SLPlayItf _play;
	result = play_object.GetInterface(SL_IID_PLAY, &_play);
	if (result != SL_RESULT_SUCCESS) {
		play_object.Destroy();
		mix_object.Destroy();
		engine_object.Destroy();
289
		throw std::runtime_error("Play.GetInterface(IID_PLAY) failed");
290 291 292 293 294 295 296 297 298 299 300
	}

	play = SLES::Play(_play);

	SLAndroidSimpleBufferQueueItf _queue;
	result = play_object.GetInterface(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
					  &_queue);
	if (result != SL_RESULT_SUCCESS) {
		play_object.Destroy();
		mix_object.Destroy();
		engine_object.Destroy();
301
		throw std::runtime_error("Play.GetInterface(IID_ANDROIDSIMPLEBUFFERQUEUE) failed");
302 303 304 305 306 307 308 309
	}

	queue = SLES::AndroidSimpleBufferQueue(_queue);
	result = queue.RegisterCallback(PlayedCallback, (void *)this);
	if (result != SL_RESULT_SUCCESS) {
		play_object.Destroy();
		mix_object.Destroy();
		engine_object.Destroy();
310
		throw std::runtime_error("Play.RegisterCallback() failed");
311 312 313 314 315 316 317
	}

	result = play.SetPlayState(SL_PLAYSTATE_PLAYING);
	if (result != SL_RESULT_SUCCESS) {
		play_object.Destroy();
		mix_object.Destroy();
		engine_object.Destroy();
318
		throw std::runtime_error("Play.SetPlayState(PLAYING) failed");
319 320 321 322 323 324 325 326
	}

	pause = cancel = false;
	n_queued = 0;
	next = 0;
	filled = 0;
}

327 328
void
SlesOutput::Close() noexcept
329 330 331 332 333 334 335
{
	play.SetPlayState(SL_PLAYSTATE_STOPPED);
	play_object.Destroy();
	mix_object.Destroy();
	engine_object.Destroy();
}

336
size_t
337
SlesOutput::Play(const void *chunk, size_t size)
338 339 340 341 342
{
	cancel = false;

	if (pause) {
		SLresult result = play.SetPlayState(SL_PLAYSTATE_PLAYING);
343 344
		if (result != SL_RESULT_SUCCESS)
			throw std::runtime_error("Play.SetPlayState(PLAYING) failed");
345 346 347 348

		pause = false;
	}

349
	std::unique_lock<Mutex> lock(mutex);
350 351 352

	assert(filled < BUFFER_SIZE);

353
	cond.wait(lock, [this]{
354 355 356
		bool ret = n_queued != N_BUFFERS;
		assert(ret || filled == 0);
		return ret;
357
	});
358 359 360 361 362 363 364 365

	size_t nbytes = std::min(BUFFER_SIZE - filled, size);
	memcpy(buffers[next] + filled, chunk, nbytes);
	filled += nbytes;
	if (filled < BUFFER_SIZE)
		return nbytes;

	SLresult result = queue.Enqueue(buffers[next], BUFFER_SIZE);
366 367
	if (result != SL_RESULT_SUCCESS)
		throw std::runtime_error("AndroidSimpleBufferQueue.Enqueue() failed");
368 369 370 371 372 373 374 375

	++n_queued;
	next = (next + 1) % N_BUFFERS;
	filled = 0;

	return nbytes;
}

376
void
377 378
SlesOutput::Drain()
{
379
	std::unique_lock<Mutex> lock(mutex);
380 381 382

	assert(filled < BUFFER_SIZE);

383
	cond.wait(lock, [this]{ return n_queued == 0; });
384 385
}

386 387
void
SlesOutput::Cancel() noexcept
388 389 390 391 392 393 394 395 396 397 398 399 400
{
	pause = true;
	cancel = true;

	SLresult result = play.SetPlayState(SL_PLAYSTATE_PAUSED);
	if (result != SL_RESULT_SUCCESS)
		FormatError(sles_domain,  "Play.SetPlayState(PAUSED) failed");

	result = queue.Clear();
	if (result != SL_RESULT_SUCCESS)
		FormatWarning(sles_domain,
			      "AndroidSimpleBufferQueue.Clear() failed");

401
	const std::lock_guard<Mutex> protect(mutex);
402 403 404 405
	n_queued = 0;
	filled = 0;
}

406
bool
407
SlesOutput::Pause()
408 409 410 411 412 413 414 415 416
{
	cancel = false;

	if (pause)
		return true;

	pause = true;

	SLresult result = play.SetPlayState(SL_PLAYSTATE_PAUSED);
417 418
	if (result != SL_RESULT_SUCCESS)
		throw std::runtime_error("Play.SetPlayState(PAUSED) failed");
419 420 421 422 423 424 425

	return true;
}

inline void
SlesOutput::PlayedCallback()
{
426
	const std::lock_guard<Mutex> protect(mutex);
427 428
	assert(n_queued > 0);
	--n_queued;
429
	cond.notify_one();
430 431 432 433 434 435 436 437 438 439 440 441 442
}

static bool
sles_test_default_device()
{
	/* this is the default output plugin on Android, and it should
	   be available in any case */
	return true;
}

const struct AudioOutputPlugin sles_output_plugin = {
	"sles",
	sles_test_default_device,
443
	SlesOutput::Create,
444
	&android_mixer_plugin,
445
};