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 #include "common/OboeDebug.h"
19 #include "EngineOpenSLES.h"
20 #include "OpenSLESUtilities.h"
21
22 using namespace oboe;
23
24 // OpenSL ES is deprecated in SDK 30.
25 // So we use custom dynamic linking to access the library.
26 #define LIB_OPENSLES_NAME "libOpenSLES.so"
27 typedef SLresult (*prototype_slCreateEngine)(
28 SLObjectItf *pEngine,
29 SLuint32 numOptions,
30 const SLEngineOption *pEngineOptions,
31 SLuint32 numInterfaces,
32 const SLInterfaceID *pInterfaceIds,
33 const SLboolean *pInterfaceRequired
34 );
35 static prototype_slCreateEngine gFunction_slCreateEngine = nullptr;
36 static void *gLibOpenSlesLibraryHandle = nullptr;
37
38 // Load the OpenSL ES library and the one primary entry point.
39 // @return true if linked OK
linkOpenSLES()40 static bool linkOpenSLES() {
41 if (gLibOpenSlesLibraryHandle == nullptr && gFunction_slCreateEngine == nullptr) {
42 // Use RTLD_NOW to avoid the unpredictable behavior that RTLD_LAZY can cause.
43 // Also resolving all the links now will prevent a run-time penalty later.
44 gLibOpenSlesLibraryHandle = dlopen(LIB_OPENSLES_NAME, RTLD_NOW);
45 if (gLibOpenSlesLibraryHandle == nullptr) {
46 LOGE("linkOpenSLES() could not find " LIB_OPENSLES_NAME);
47 } else {
48 gFunction_slCreateEngine = (prototype_slCreateEngine) dlsym(
49 gLibOpenSlesLibraryHandle,
50 "slCreateEngine");
51 LOGD("linkOpenSLES(): dlsym(%s) returned %p", "slCreateEngine",
52 gFunction_slCreateEngine);
53 }
54 }
55 return gFunction_slCreateEngine != nullptr;
56 }
57
getInstance()58 EngineOpenSLES &EngineOpenSLES::getInstance() {
59 static EngineOpenSLES sInstance;
60 return sInstance;
61 }
62
open()63 SLresult EngineOpenSLES::open() {
64 std::lock_guard<std::mutex> lock(mLock);
65
66 SLresult result = SL_RESULT_SUCCESS;
67 if (mOpenCount++ == 0) {
68 // load the library and link to it
69 if (!linkOpenSLES()) {
70 result = SL_RESULT_FEATURE_UNSUPPORTED;
71 goto error;
72 };
73
74 // create engine
75 result = (*gFunction_slCreateEngine)(&mEngineObject, 0, NULL, 0, NULL, NULL);
76 if (SL_RESULT_SUCCESS != result) {
77 LOGE("EngineOpenSLES - slCreateEngine() result:%s", getSLErrStr(result));
78 goto error;
79 }
80
81 // realize the engine
82 result = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE);
83 if (SL_RESULT_SUCCESS != result) {
84 LOGE("EngineOpenSLES - Realize() engine result:%s", getSLErrStr(result));
85 goto error;
86 }
87
88 // get the engine interface, which is needed in order to create other objects
89 result = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface);
90 if (SL_RESULT_SUCCESS != result) {
91 LOGE("EngineOpenSLES - GetInterface() engine result:%s", getSLErrStr(result));
92 goto error;
93 }
94 }
95
96 return result;
97
98 error:
99 close();
100 return result;
101 }
102
close()103 void EngineOpenSLES::close() {
104 std::lock_guard<std::mutex> lock(mLock);
105 if (--mOpenCount == 0) {
106 if (mEngineObject != nullptr) {
107 (*mEngineObject)->Destroy(mEngineObject);
108 mEngineObject = nullptr;
109 mEngineInterface = nullptr;
110 }
111 }
112 }
113
createOutputMix(SLObjectItf * objectItf)114 SLresult EngineOpenSLES::createOutputMix(SLObjectItf *objectItf) {
115 return (*mEngineInterface)->CreateOutputMix(mEngineInterface, objectItf, 0, 0, 0);
116 }
117
createAudioPlayer(SLObjectItf * objectItf,SLDataSource * audioSource,SLDataSink * audioSink)118 SLresult EngineOpenSLES::createAudioPlayer(SLObjectItf *objectItf,
119 SLDataSource *audioSource,
120 SLDataSink *audioSink) {
121
122 const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
123 const SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
124
125 return (*mEngineInterface)->CreateAudioPlayer(mEngineInterface, objectItf, audioSource,
126 audioSink,
127 sizeof(ids) / sizeof(ids[0]), ids, reqs);
128 }
129
createAudioRecorder(SLObjectItf * objectItf,SLDataSource * audioSource,SLDataSink * audioSink)130 SLresult EngineOpenSLES::createAudioRecorder(SLObjectItf *objectItf,
131 SLDataSource *audioSource,
132 SLDataSink *audioSink) {
133
134 const SLInterfaceID ids[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
135 const SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
136
137 return (*mEngineInterface)->CreateAudioRecorder(mEngineInterface, objectItf, audioSource,
138 audioSink,
139 sizeof(ids) / sizeof(ids[0]), ids, reqs);
140 }
141
142