output/sles: new output plugin for Android
This commit is contained in:
parent
3d4689756c
commit
9574d11dc8
11
Makefile.am
11
Makefile.am
|
@ -1078,6 +1078,17 @@ liboutput_plugins_a_SOURCES += \
|
||||||
libmixer_plugins_a_SOURCES += src/mixer/plugins/AlsaMixerPlugin.cxx
|
libmixer_plugins_a_SOURCES += src/mixer/plugins/AlsaMixerPlugin.cxx
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if ANDROID
|
||||||
|
liboutput_plugins_a_SOURCES += \
|
||||||
|
src/output/plugins/sles/Object.hxx \
|
||||||
|
src/output/plugins/sles/Engine.hxx \
|
||||||
|
src/output/plugins/sles/Play.hxx \
|
||||||
|
src/output/plugins/sles/AndroidSimpleBufferQueue.hxx \
|
||||||
|
src/output/plugins/sles/SlesOutputPlugin.cxx \
|
||||||
|
src/output/plugins/sles/SlesOutputPlugin.hxx
|
||||||
|
OUTPUT_LIBS += -lOpenSLES
|
||||||
|
endif
|
||||||
|
|
||||||
if HAVE_ROAR
|
if HAVE_ROAR
|
||||||
liboutput_plugins_a_SOURCES += \
|
liboutput_plugins_a_SOURCES += \
|
||||||
src/output/plugins/RoarOutputPlugin.cxx \
|
src/output/plugins/RoarOutputPlugin.cxx \
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "plugins/RecorderOutputPlugin.hxx"
|
#include "plugins/RecorderOutputPlugin.hxx"
|
||||||
#include "plugins/RoarOutputPlugin.hxx"
|
#include "plugins/RoarOutputPlugin.hxx"
|
||||||
#include "plugins/ShoutOutputPlugin.hxx"
|
#include "plugins/ShoutOutputPlugin.hxx"
|
||||||
|
#include "plugins/sles/SlesOutputPlugin.hxx"
|
||||||
#include "plugins/SolarisOutputPlugin.hxx"
|
#include "plugins/SolarisOutputPlugin.hxx"
|
||||||
#include "plugins/WinmmOutputPlugin.hxx"
|
#include "plugins/WinmmOutputPlugin.hxx"
|
||||||
|
|
||||||
|
@ -44,6 +45,9 @@ const AudioOutputPlugin *const audio_output_plugins[] = {
|
||||||
&shout_output_plugin,
|
&shout_output_plugin,
|
||||||
#endif
|
#endif
|
||||||
&null_output_plugin,
|
&null_output_plugin,
|
||||||
|
#ifdef ANDROID
|
||||||
|
&sles_output_plugin,
|
||||||
|
#endif
|
||||||
#ifdef HAVE_FIFO
|
#ifdef HAVE_FIFO
|
||||||
&fifo_output_plugin,
|
&fifo_output_plugin,
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2012 Max Kellermann <max@duempel.org>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SLES_ANDROID_SIMPLE_BUFFER_QUEUE_HPP
|
||||||
|
#define SLES_ANDROID_SIMPLE_BUFFER_QUEUE_HPP
|
||||||
|
|
||||||
|
#include <SLES/OpenSLES_Android.h>
|
||||||
|
|
||||||
|
namespace SLES {
|
||||||
|
/**
|
||||||
|
* OO wrapper for an OpenSL/ES SLAndroidSimpleBufferQueueItf
|
||||||
|
* variable.
|
||||||
|
*/
|
||||||
|
class AndroidSimpleBufferQueue {
|
||||||
|
SLAndroidSimpleBufferQueueItf queue;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AndroidSimpleBufferQueue() = default;
|
||||||
|
explicit AndroidSimpleBufferQueue(SLAndroidSimpleBufferQueueItf _queue)
|
||||||
|
:queue(_queue) {}
|
||||||
|
|
||||||
|
SLresult Enqueue(const void *pBuffer, SLuint32 size) {
|
||||||
|
return (*queue)->Enqueue(queue, pBuffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
SLresult Clear() {
|
||||||
|
return (*queue)->Clear(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
SLresult GetState(SLAndroidSimpleBufferQueueState *pState) {
|
||||||
|
return (*queue)->GetState(queue, pState);
|
||||||
|
}
|
||||||
|
|
||||||
|
SLresult RegisterCallback(slAndroidSimpleBufferQueueCallback callback,
|
||||||
|
void *pContext) {
|
||||||
|
return (*queue)->RegisterCallback(queue, callback, pContext);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2012 Max Kellermann <max@duempel.org>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SLES_ENGINE_HPP
|
||||||
|
#define SLES_ENGINE_HPP
|
||||||
|
|
||||||
|
#include <SLES/OpenSLES.h>
|
||||||
|
|
||||||
|
namespace SLES {
|
||||||
|
/**
|
||||||
|
* OO wrapper for an OpenSL/ES SLEngineItf variable.
|
||||||
|
*/
|
||||||
|
class Engine {
|
||||||
|
SLEngineItf engine;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Engine() = default;
|
||||||
|
explicit Engine(SLEngineItf _engine):engine(_engine) {}
|
||||||
|
|
||||||
|
SLresult CreateAudioPlayer(SLObjectItf *pPlayer,
|
||||||
|
SLDataSource *pAudioSrc, SLDataSink *pAudioSnk,
|
||||||
|
SLuint32 numInterfaces,
|
||||||
|
const SLInterfaceID *pInterfaceIds,
|
||||||
|
const SLboolean *pInterfaceRequired) {
|
||||||
|
return (*engine)->CreateAudioPlayer(engine, pPlayer,
|
||||||
|
pAudioSrc, pAudioSnk,
|
||||||
|
numInterfaces, pInterfaceIds,
|
||||||
|
pInterfaceRequired);
|
||||||
|
}
|
||||||
|
|
||||||
|
SLresult CreateOutputMix(SLObjectItf *pMix,
|
||||||
|
SLuint32 numInterfaces,
|
||||||
|
const SLInterfaceID *pInterfaceIds,
|
||||||
|
const SLboolean *pInterfaceRequired) {
|
||||||
|
return (*engine)->CreateOutputMix(engine, pMix,
|
||||||
|
numInterfaces, pInterfaceIds,
|
||||||
|
pInterfaceRequired);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2012 Max Kellermann <max@duempel.org>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SLES_OBJECT_HPP
|
||||||
|
#define SLES_OBJECT_HPP
|
||||||
|
|
||||||
|
#include <SLES/OpenSLES.h>
|
||||||
|
|
||||||
|
namespace SLES {
|
||||||
|
/**
|
||||||
|
* OO wrapper for an OpenSL/ES SLObjectItf variable.
|
||||||
|
*/
|
||||||
|
class Object {
|
||||||
|
SLObjectItf object;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Object() = default;
|
||||||
|
explicit Object(SLObjectItf _object):object(_object) {}
|
||||||
|
|
||||||
|
operator SLObjectItf() {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
SLresult Realize(bool async) {
|
||||||
|
return (*object)->Realize(object, async);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Destroy() {
|
||||||
|
(*object)->Destroy(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
SLresult GetInterface(const SLInterfaceID iid, void *pInterface) {
|
||||||
|
return (*object)->GetInterface(object, iid, pInterface);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2012 Max Kellermann <max@duempel.org>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SLES_PLAY_HPP
|
||||||
|
#define SLES_PLAY_HPP
|
||||||
|
|
||||||
|
#include <SLES/OpenSLES.h>
|
||||||
|
|
||||||
|
namespace SLES {
|
||||||
|
/**
|
||||||
|
* OO wrapper for an OpenSL/ES SLPlayItf variable.
|
||||||
|
*/
|
||||||
|
class Play {
|
||||||
|
SLPlayItf play;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Play() = default;
|
||||||
|
explicit Play(SLPlayItf _play):play(_play) {}
|
||||||
|
|
||||||
|
SLresult SetPlayState(SLuint32 state) {
|
||||||
|
return (*play)->SetPlayState(play, state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,537 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||||
|
* 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 "config.h"
|
||||||
|
#include "SlesOutputPlugin.hxx"
|
||||||
|
#include "Object.hxx"
|
||||||
|
#include "Engine.hxx"
|
||||||
|
#include "Play.hxx"
|
||||||
|
#include "AndroidSimpleBufferQueue.hxx"
|
||||||
|
#include "../../OutputAPI.hxx"
|
||||||
|
#include "util/Macros.hxx"
|
||||||
|
#include "util/Error.hxx"
|
||||||
|
#include "util/Domain.hxx"
|
||||||
|
#include "system/ByteOrder.hxx"
|
||||||
|
#include "Log.hxx"
|
||||||
|
|
||||||
|
#include <SLES/OpenSLES.h>
|
||||||
|
#include <SLES/OpenSLES_Android.h>
|
||||||
|
|
||||||
|
class SlesOutput {
|
||||||
|
static constexpr unsigned N_BUFFERS = 3;
|
||||||
|
static constexpr size_t BUFFER_SIZE = 65536;
|
||||||
|
|
||||||
|
AudioOutput base;
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
public:
|
||||||
|
SlesOutput()
|
||||||
|
:base(sles_output_plugin) {}
|
||||||
|
|
||||||
|
operator AudioOutput *() {
|
||||||
|
return &base;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Initialize(const config_param ¶m, Error &error) {
|
||||||
|
return base.Configure(param, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Configure(const config_param ¶m, Error &error);
|
||||||
|
|
||||||
|
bool Open(AudioFormat &audio_format, Error &error);
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
unsigned Delay() {
|
||||||
|
return pause && !cancel ? 100 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Play(const void *chunk, size_t size, Error &error);
|
||||||
|
|
||||||
|
void Drain();
|
||||||
|
void Cancel();
|
||||||
|
bool Pause();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void PlayedCallback();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenSL/ES callback which gets invoked when a buffer has
|
||||||
|
* been consumed. It synthesises and enqueues the next
|
||||||
|
* buffer.
|
||||||
|
*/
|
||||||
|
static void PlayedCallback(gcc_unused SLAndroidSimpleBufferQueueItf caller,
|
||||||
|
void *pContext)
|
||||||
|
{
|
||||||
|
SlesOutput &sles = *(SlesOutput *)pContext;
|
||||||
|
sles.PlayedCallback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr Domain sles_domain("sles");
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
SlesOutput::Configure(const config_param &, Error &)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
SlesOutput::Open(AudioFormat &audio_format, Error &error)
|
||||||
|
{
|
||||||
|
SLresult result;
|
||||||
|
SLObjectItf _object;
|
||||||
|
|
||||||
|
result = slCreateEngine(&_object, 0, nullptr, 0,
|
||||||
|
nullptr, nullptr);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result), "slCreateEngine() failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
engine_object = SLES::Object(_object);
|
||||||
|
|
||||||
|
result = engine_object.Realize(false);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result), "Engine.Realize() failed");
|
||||||
|
engine_object.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SLEngineItf _engine;
|
||||||
|
result = engine_object.GetInterface(SL_IID_ENGINE, &_engine);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"Engine.GetInterface(IID_ENGINE) failed");
|
||||||
|
engine_object.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SLES::Engine engine(_engine);
|
||||||
|
|
||||||
|
result = engine.CreateOutputMix(&_object, 0, nullptr, nullptr);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"Engine.CreateOutputMix() failed");
|
||||||
|
engine_object.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mix_object = SLES::Object(_object);
|
||||||
|
|
||||||
|
result = mix_object.Realize(false);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"Mix.Realize() failed");
|
||||||
|
mix_object.Destroy();
|
||||||
|
engine_object.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
|
||||||
|
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
|
||||||
|
N_BUFFERS,
|
||||||
|
};
|
||||||
|
|
||||||
|
SLDataFormat_PCM format_pcm;
|
||||||
|
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||||
|
format_pcm.numChannels = 1;
|
||||||
|
/* from the Android NDK docs: "Note that the field samplesPerSec is
|
||||||
|
actually in units of milliHz, despite the misleading name." */
|
||||||
|
format_pcm.samplesPerSec = audio_format.sample_rate * 1000u;
|
||||||
|
format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||||
|
format_pcm.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||||
|
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
|
||||||
|
format_pcm.endianness = IsLittleEndian()
|
||||||
|
? SL_BYTEORDER_LITTLEENDIAN
|
||||||
|
: SL_BYTEORDER_BIGENDIAN;
|
||||||
|
|
||||||
|
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,
|
||||||
|
ARRAY_SIZE(ids2), ids2, req2);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"Engine.CreateAudioPlayer() failed");
|
||||||
|
mix_object.Destroy();
|
||||||
|
engine_object.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
result = play_object.Realize(false);
|
||||||
|
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"Play.Realize() failed");
|
||||||
|
play_object.Destroy();
|
||||||
|
mix_object.Destroy();
|
||||||
|
engine_object.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SLPlayItf _play;
|
||||||
|
result = play_object.GetInterface(SL_IID_PLAY, &_play);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"Play.GetInterface(IID_PLAY) failed");
|
||||||
|
play_object.Destroy();
|
||||||
|
mix_object.Destroy();
|
||||||
|
engine_object.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
play = SLES::Play(_play);
|
||||||
|
|
||||||
|
SLAndroidSimpleBufferQueueItf _queue;
|
||||||
|
result = play_object.GetInterface(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||||
|
&_queue);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"Play.GetInterface(IID_ANDROIDSIMPLEBUFFERQUEUE) failed");
|
||||||
|
play_object.Destroy();
|
||||||
|
mix_object.Destroy();
|
||||||
|
engine_object.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue = SLES::AndroidSimpleBufferQueue(_queue);
|
||||||
|
result = queue.RegisterCallback(PlayedCallback, (void *)this);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"Play.RegisterCallback() failed");
|
||||||
|
play_object.Destroy();
|
||||||
|
mix_object.Destroy();
|
||||||
|
engine_object.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = play.SetPlayState(SL_PLAYSTATE_PLAYING);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"Play.SetPlayState(PLAYING) failed");
|
||||||
|
play_object.Destroy();
|
||||||
|
mix_object.Destroy();
|
||||||
|
engine_object.Destroy();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pause = cancel = false;
|
||||||
|
n_queued = 0;
|
||||||
|
next = 0;
|
||||||
|
filled = 0;
|
||||||
|
|
||||||
|
// TODO: support other sample formats
|
||||||
|
audio_format.format = SampleFormat::S16;
|
||||||
|
|
||||||
|
// TODO: support stereo
|
||||||
|
audio_format.channels = 1;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
SlesOutput::Close()
|
||||||
|
{
|
||||||
|
play.SetPlayState(SL_PLAYSTATE_STOPPED);
|
||||||
|
play_object.Destroy();
|
||||||
|
mix_object.Destroy();
|
||||||
|
engine_object.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t
|
||||||
|
SlesOutput::Play(const void *chunk, size_t size, Error &error)
|
||||||
|
{
|
||||||
|
cancel = false;
|
||||||
|
|
||||||
|
if (pause) {
|
||||||
|
SLresult result = play.SetPlayState(SL_PLAYSTATE_PLAYING);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"Play.SetPlayState(PLAYING) failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pause = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ScopeLock protect(mutex);
|
||||||
|
|
||||||
|
assert(filled < BUFFER_SIZE);
|
||||||
|
|
||||||
|
while (n_queued == N_BUFFERS) {
|
||||||
|
assert(filled == 0);
|
||||||
|
cond.wait(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
error.Set(sles_domain, int(result),
|
||||||
|
"AndroidSimpleBufferQueue.Enqueue() failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
++n_queued;
|
||||||
|
next = (next + 1) % N_BUFFERS;
|
||||||
|
filled = 0;
|
||||||
|
|
||||||
|
return nbytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
SlesOutput::Drain()
|
||||||
|
{
|
||||||
|
const ScopeLock protect(mutex);
|
||||||
|
|
||||||
|
assert(filled < BUFFER_SIZE);
|
||||||
|
|
||||||
|
while (n_queued > 0)
|
||||||
|
cond.wait(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
SlesOutput::Cancel()
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
|
||||||
|
const ScopeLock protect(mutex);
|
||||||
|
n_queued = 0;
|
||||||
|
filled = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
SlesOutput::Pause()
|
||||||
|
{
|
||||||
|
cancel = false;
|
||||||
|
|
||||||
|
if (pause)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
pause = true;
|
||||||
|
|
||||||
|
SLresult result = play.SetPlayState(SL_PLAYSTATE_PAUSED);
|
||||||
|
if (result != SL_RESULT_SUCCESS) {
|
||||||
|
FormatError(sles_domain, "Play.SetPlayState(PAUSED) failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
SlesOutput::PlayedCallback()
|
||||||
|
{
|
||||||
|
const ScopeLock protect(mutex);
|
||||||
|
assert(n_queued > 0);
|
||||||
|
--n_queued;
|
||||||
|
cond.signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sles_test_default_device()
|
||||||
|
{
|
||||||
|
/* this is the default output plugin on Android, and it should
|
||||||
|
be available in any case */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AudioOutput *
|
||||||
|
sles_output_init(const config_param ¶m, Error &error)
|
||||||
|
{
|
||||||
|
SlesOutput *sles = new SlesOutput();
|
||||||
|
|
||||||
|
if (!sles->Initialize(param, error) ||
|
||||||
|
!sles->Configure(param, error)) {
|
||||||
|
delete sles;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *sles;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sles_output_finish(AudioOutput *ao)
|
||||||
|
{
|
||||||
|
SlesOutput *sles = (SlesOutput *)ao;
|
||||||
|
|
||||||
|
delete sles;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sles_output_open(AudioOutput *ao, AudioFormat &audio_format, Error &error)
|
||||||
|
{
|
||||||
|
SlesOutput &sles = *(SlesOutput *)ao;
|
||||||
|
|
||||||
|
return sles.Open(audio_format, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sles_output_close(AudioOutput *ao)
|
||||||
|
{
|
||||||
|
SlesOutput &sles = *(SlesOutput *)ao;
|
||||||
|
|
||||||
|
sles.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned
|
||||||
|
sles_output_delay(AudioOutput *ao)
|
||||||
|
{
|
||||||
|
SlesOutput &sles = *(SlesOutput *)ao;
|
||||||
|
|
||||||
|
return sles.Delay();
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
sles_output_play(AudioOutput *ao, const void *chunk, size_t size,
|
||||||
|
Error &error)
|
||||||
|
{
|
||||||
|
SlesOutput &sles = *(SlesOutput *)ao;
|
||||||
|
|
||||||
|
return sles.Play(chunk, size, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sles_output_drain(AudioOutput *ao)
|
||||||
|
{
|
||||||
|
SlesOutput &sles = *(SlesOutput *)ao;
|
||||||
|
|
||||||
|
sles.Drain();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sles_output_cancel(AudioOutput *ao)
|
||||||
|
{
|
||||||
|
SlesOutput &sles = *(SlesOutput *)ao;
|
||||||
|
|
||||||
|
sles.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sles_output_pause(AudioOutput *ao)
|
||||||
|
{
|
||||||
|
SlesOutput &sles = *(SlesOutput *)ao;
|
||||||
|
|
||||||
|
return sles.Pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct AudioOutputPlugin sles_output_plugin = {
|
||||||
|
"sles",
|
||||||
|
sles_test_default_device,
|
||||||
|
sles_output_init,
|
||||||
|
sles_output_finish,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
sles_output_open,
|
||||||
|
sles_output_close,
|
||||||
|
sles_output_delay,
|
||||||
|
nullptr,
|
||||||
|
sles_output_play,
|
||||||
|
sles_output_drain,
|
||||||
|
sles_output_cancel,
|
||||||
|
sles_output_pause,
|
||||||
|
nullptr,
|
||||||
|
};
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2014 The Music Player Daemon Project
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPD_SLES_OUTPUT_PLUGIN_HXX
|
||||||
|
#define MPD_SLES_OUTPUT_PLUGIN_HXX
|
||||||
|
|
||||||
|
extern const struct AudioOutputPlugin sles_output_plugin;
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue