• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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