1 /*
2 * Copyright 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <dlfcn.h>
18
19 #include "common/OboeDebug.h"
20 #include "EngineOpenSLES.h"
21 #include "OpenSLESUtilities.h"
22
23 using namespace oboe;
24
25 // OpenSL ES is deprecated in SDK 30.
26 // So we use custom dynamic linking to access the library.
27 #define LIB_OPENSLES_NAME "libOpenSLES.so"
28
getInstance()29 EngineOpenSLES &EngineOpenSLES::getInstance() {
30 static EngineOpenSLES sInstance;
31 return sInstance;
32 }
33
34 // Satisfy extern in OpenSLES.h
35 // These are required because of b/337360630, which was causing
36 // Oboe to have link failures if libOpenSLES.so was not available.
37 // If you are statically linking Oboe and libOpenSLES.so is a shared library
38 // and you observe crashes, you can pass DO_NOT_DEFINE_OPENSL_ES_CONSTANTS to cmake.
39 #ifndef DO_NOT_DEFINE_OPENSL_ES_CONSTANTS
40 SL_API const SLInterfaceID SL_IID_ENGINE = nullptr;
41 SL_API const SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE = nullptr;
42 SL_API const SLInterfaceID SL_IID_ANDROIDCONFIGURATION = nullptr;
43 SL_API const SLInterfaceID SL_IID_RECORD = nullptr;
44 SL_API const SLInterfaceID SL_IID_BUFFERQUEUE = nullptr;
45 SL_API const SLInterfaceID SL_IID_VOLUME = nullptr;
46 SL_API const SLInterfaceID SL_IID_PLAY = nullptr;
47 #endif
48
getSafeDlerror()49 static const char *getSafeDlerror() {
50 static const char *defaultMessage = "not found?";
51 char *errorMessage = dlerror();
52 return (errorMessage == nullptr) ? defaultMessage : errorMessage;
53 }
54
55 // Load the OpenSL ES library and the one primary entry point.
56 // @return true if linked OK
linkOpenSLES()57 bool EngineOpenSLES::linkOpenSLES() {
58 if (mDynamicLinkState == kLinkStateBad) {
59 LOGE("%s(), OpenSL ES not available, based on previous link failure.", __func__);
60 } else if (mDynamicLinkState == kLinkStateUninitialized) {
61 // Set to BAD now in case we return because of an error.
62 // This is safe form race conditions because this function is always called
63 // under mLock amd the state is only accessed from this function.
64 mDynamicLinkState = kLinkStateBad;
65 // Use RTLD_NOW to avoid the unpredictable behavior that RTLD_LAZY can cause.
66 // Also resolving all the links now will prevent a run-time penalty later.
67 mLibOpenSlesLibraryHandle = dlopen(LIB_OPENSLES_NAME, RTLD_NOW);
68 if (mLibOpenSlesLibraryHandle == nullptr) {
69 LOGE("%s() could not dlopen(%s), %s", __func__, LIB_OPENSLES_NAME, getSafeDlerror());
70 return false;
71 } else {
72 mFunction_slCreateEngine = (prototype_slCreateEngine) dlsym(
73 mLibOpenSlesLibraryHandle,
74 "slCreateEngine");
75 LOGD("%s(): dlsym(%s) returned %p", __func__,
76 "slCreateEngine", mFunction_slCreateEngine);
77 if (mFunction_slCreateEngine == nullptr) {
78 LOGE("%s(): dlsym(slCreateEngine) returned null, %s", __func__, getSafeDlerror());
79 return false;
80 }
81
82 // Load IID interfaces.
83 LOCAL_SL_IID_ENGINE = getIidPointer("SL_IID_ENGINE");
84 if (LOCAL_SL_IID_ENGINE == nullptr) return false;
85 LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE = getIidPointer(
86 "SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
87 if (LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE == nullptr) return false;
88 LOCAL_SL_IID_ANDROIDCONFIGURATION = getIidPointer(
89 "SL_IID_ANDROIDCONFIGURATION");
90 if (LOCAL_SL_IID_ANDROIDCONFIGURATION == nullptr) return false;
91 LOCAL_SL_IID_RECORD = getIidPointer("SL_IID_RECORD");
92 if (LOCAL_SL_IID_RECORD == nullptr) return false;
93 LOCAL_SL_IID_BUFFERQUEUE = getIidPointer("SL_IID_BUFFERQUEUE");
94 if (LOCAL_SL_IID_BUFFERQUEUE == nullptr) return false;
95 LOCAL_SL_IID_VOLUME = getIidPointer("SL_IID_VOLUME");
96 if (LOCAL_SL_IID_VOLUME == nullptr) return false;
97 LOCAL_SL_IID_PLAY = getIidPointer("SL_IID_PLAY");
98 if (LOCAL_SL_IID_PLAY == nullptr) return false;
99
100 mDynamicLinkState = kLinkStateGood;
101 }
102 }
103 return (mDynamicLinkState == kLinkStateGood);
104 }
105
106 // A symbol like SL_IID_PLAY is a pointer to a structure.
107 // The dlsym() function returns the address of the pointer, not the structure.
108 // To get the address of the structure we have to dereference the pointer.
getIidPointer(const char * symbolName)109 SLInterfaceID EngineOpenSLES::getIidPointer(const char *symbolName) {
110 SLInterfaceID *iid_address = (SLInterfaceID *) dlsym(
111 mLibOpenSlesLibraryHandle,
112 symbolName);
113 if (iid_address == nullptr) {
114 LOGE("%s(): dlsym(%s) returned null, %s", __func__, symbolName, getSafeDlerror());
115 return (SLInterfaceID) nullptr;
116 }
117 return *iid_address; // Get address of the structure.
118 }
119
open()120 SLresult EngineOpenSLES::open() {
121 std::lock_guard<std::mutex> lock(mLock);
122
123 SLresult result = SL_RESULT_SUCCESS;
124 if (mOpenCount++ == 0) {
125 // load the library and link to it
126 if (!linkOpenSLES()) {
127 result = SL_RESULT_FEATURE_UNSUPPORTED;
128 goto error;
129 };
130
131 // create engine
132 result = (*mFunction_slCreateEngine)(&mEngineObject, 0, NULL, 0, NULL, NULL);
133 if (SL_RESULT_SUCCESS != result) {
134 LOGE("EngineOpenSLES - slCreateEngine() result:%s", getSLErrStr(result));
135 goto error;
136 }
137
138 // realize the engine
139 result = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE);
140 if (SL_RESULT_SUCCESS != result) {
141 LOGE("EngineOpenSLES - Realize() engine result:%s", getSLErrStr(result));
142 goto error;
143 }
144
145 // get the engine interface, which is needed in order to create other objects
146 result = (*mEngineObject)->GetInterface(mEngineObject,
147 EngineOpenSLES::getInstance().getIidEngine(),
148 &mEngineInterface);
149 if (SL_RESULT_SUCCESS != result) {
150 LOGE("EngineOpenSLES - GetInterface() engine result:%s", getSLErrStr(result));
151 goto error;
152 }
153 }
154
155 return result;
156
157 error:
158 close_l();
159 return result;
160 }
161
close()162 void EngineOpenSLES::close() {
163 std::lock_guard<std::mutex> lock(mLock);
164 close_l();
165 }
166
167 // This must be called under mLock
close_l()168 void EngineOpenSLES::close_l() {
169 if (--mOpenCount == 0) {
170 if (mEngineObject != nullptr) {
171 (*mEngineObject)->Destroy(mEngineObject);
172 mEngineObject = nullptr;
173 mEngineInterface = nullptr;
174 }
175 }
176 }
177
createOutputMix(SLObjectItf * objectItf)178 SLresult EngineOpenSLES::createOutputMix(SLObjectItf *objectItf) {
179 return (*mEngineInterface)->CreateOutputMix(mEngineInterface, objectItf, 0, 0, 0);
180 }
181
createAudioPlayer(SLObjectItf * objectItf,SLDataSource * audioSource,SLDataSink * audioSink)182 SLresult EngineOpenSLES::createAudioPlayer(SLObjectItf *objectItf,
183 SLDataSource *audioSource,
184 SLDataSink *audioSink) {
185
186 SLInterfaceID ids[] = {LOCAL_SL_IID_BUFFERQUEUE, LOCAL_SL_IID_ANDROIDCONFIGURATION};
187 SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
188
189 return (*mEngineInterface)->CreateAudioPlayer(mEngineInterface, objectItf, audioSource,
190 audioSink,
191 sizeof(ids) / sizeof(ids[0]), ids, reqs);
192 }
193
createAudioRecorder(SLObjectItf * objectItf,SLDataSource * audioSource,SLDataSink * audioSink)194 SLresult EngineOpenSLES::createAudioRecorder(SLObjectItf *objectItf,
195 SLDataSource *audioSource,
196 SLDataSink *audioSink) {
197
198 SLInterfaceID ids[] = {LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
199 LOCAL_SL_IID_ANDROIDCONFIGURATION };
200 SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
201
202 return (*mEngineInterface)->CreateAudioRecorder(mEngineInterface, objectItf, audioSource,
203 audioSink,
204 sizeof(ids) / sizeof(ids[0]), ids, reqs);
205 }
206
207