1 /* Copyright (C) 2020 The Android Open Source Project
2 **
3 ** This software is licensed under the terms of the GNU General Public
4 ** License version 2, as published by the Free Software Foundation, and
5 ** may be copied, distributed, and modified under those terms.
6 **
7 ** This program is distributed in the hope that it will be useful,
8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 ** GNU General Public License for more details.
11 */
12
13 #define LOG_TAG "GoldfishComponentStore"
14
15 #include <goldfish_codec2/store/GoldfishComponentStore.h>
16
17 #include <dlfcn.h>
18 #include <stdint.h>
19
20 #include <memory>
21 #include <mutex>
22
23 #include <C2.h>
24 #include <C2Config.h>
25 #include <cutils/properties.h>
26 #include <log/log.h>
27
28 namespace android {
29
30 // static
Create()31 std::shared_ptr<C2ComponentStore> GoldfishComponentStore::Create() {
32 ALOGI("%s()", __func__);
33
34 static std::mutex mutex;
35 static std::weak_ptr<C2ComponentStore> platformStore;
36
37 std::lock_guard<std::mutex> lock(mutex);
38 std::shared_ptr<C2ComponentStore> store = platformStore.lock();
39 if (store != nullptr)
40 return store;
41
42 store = std::shared_ptr<C2ComponentStore>(new GoldfishComponentStore());
43 platformStore = store;
44 return store;
45 }
46
getName() const47 C2String GoldfishComponentStore::getName() const {
48 return "android.componentStore.goldfish";
49 }
50
init(std::string libPath)51 c2_status_t GoldfishComponentStore::ComponentModule::init(std::string libPath) {
52 ALOGI("in %s", __func__);
53 ALOGI("loading dll of path %s", libPath.c_str());
54 mLibHandle = dlopen(libPath.c_str(), RTLD_NOW | RTLD_NODELETE);
55 LOG_ALWAYS_FATAL_IF(mLibHandle == nullptr, "could not dlopen %s: %s",
56 libPath.c_str(), dlerror());
57
58 createFactory = (C2ComponentFactory::CreateCodec2FactoryFunc)dlsym(
59 mLibHandle, "CreateCodec2Factory");
60 LOG_ALWAYS_FATAL_IF(createFactory == nullptr, "createFactory is null in %s",
61 libPath.c_str());
62
63 destroyFactory = (C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym(
64 mLibHandle, "DestroyCodec2Factory");
65 LOG_ALWAYS_FATAL_IF(destroyFactory == nullptr,
66 "destroyFactory is null in %s", libPath.c_str());
67
68 mComponentFactory = createFactory();
69 if (mComponentFactory == nullptr) {
70 ALOGD("could not create factory in %s", libPath.c_str());
71 mInit = C2_NO_MEMORY;
72 } else {
73 mInit = C2_OK;
74 }
75
76 if (mInit != C2_OK) {
77 return mInit;
78 }
79
80 std::shared_ptr<C2ComponentInterface> intf;
81 c2_status_t res = createInterface(0, &intf);
82 if (res != C2_OK) {
83 ALOGD("failed to create interface: %d", res);
84 return mInit;
85 }
86
87 std::shared_ptr<C2Component::Traits> traits(new (std::nothrow)
88 C2Component::Traits);
89 if (traits) {
90 traits->name = intf->getName();
91
92 C2ComponentKindSetting kind;
93 C2ComponentDomainSetting domain;
94 res = intf->query_vb({&kind, &domain}, {}, C2_MAY_BLOCK, nullptr);
95 bool fixDomain = res != C2_OK;
96 if (res == C2_OK) {
97 traits->kind = kind.value;
98 traits->domain = domain.value;
99 } else {
100 // TODO: remove this fall-back
101 ALOGD("failed to query interface for kind and domain: %d", res);
102
103 traits->kind = (traits->name.find("encoder") != std::string::npos)
104 ? C2Component::KIND_ENCODER
105 : (traits->name.find("decoder") != std::string::npos)
106 ? C2Component::KIND_DECODER
107 : C2Component::KIND_OTHER;
108 }
109
110 uint32_t mediaTypeIndex =
111 traits->kind == C2Component::KIND_ENCODER
112 ? C2PortMediaTypeSetting::output::PARAM_TYPE
113 : C2PortMediaTypeSetting::input::PARAM_TYPE;
114 std::vector<std::unique_ptr<C2Param>> params;
115 res = intf->query_vb({}, {mediaTypeIndex}, C2_MAY_BLOCK, ¶ms);
116 if (res != C2_OK) {
117 ALOGD("failed to query interface: %d", res);
118 return mInit;
119 }
120 if (params.size() != 1u) {
121 ALOGD("failed to query interface: unexpected vector size: %zu",
122 params.size());
123 return mInit;
124 }
125 C2PortMediaTypeSetting *mediaTypeConfig =
126 C2PortMediaTypeSetting::From(params[0].get());
127 if (mediaTypeConfig == nullptr) {
128 ALOGD("failed to query media type");
129 return mInit;
130 }
131 traits->mediaType = std::string(
132 mediaTypeConfig->m.value,
133 strnlen(mediaTypeConfig->m.value, mediaTypeConfig->flexCount()));
134
135 if (fixDomain) {
136 if (strncmp(traits->mediaType.c_str(), "audio/", 6) == 0) {
137 traits->domain = C2Component::DOMAIN_AUDIO;
138 } else if (strncmp(traits->mediaType.c_str(), "video/", 6) == 0) {
139 traits->domain = C2Component::DOMAIN_VIDEO;
140 } else if (strncmp(traits->mediaType.c_str(), "image/", 6) == 0) {
141 traits->domain = C2Component::DOMAIN_IMAGE;
142 } else {
143 traits->domain = C2Component::DOMAIN_OTHER;
144 }
145 }
146
147 // TODO: get this properly from the store during emplace
148 switch (traits->domain) {
149 case C2Component::DOMAIN_AUDIO:
150 traits->rank = 8;
151 break;
152 default:
153 traits->rank = 512;
154 }
155
156 params.clear();
157 res = intf->query_vb({}, {C2ComponentAliasesSetting::PARAM_TYPE},
158 C2_MAY_BLOCK, ¶ms);
159 if (res == C2_OK && params.size() == 1u) {
160 C2ComponentAliasesSetting *aliasesSetting =
161 C2ComponentAliasesSetting::From(params[0].get());
162 if (aliasesSetting) {
163 // Split aliases on ','
164 // This looks simpler in plain C and even std::string would
165 // still make a copy.
166 char *aliases = ::strndup(aliasesSetting->m.value,
167 aliasesSetting->flexCount());
168 ALOGD("'%s' has aliases: '%s'", intf->getName().c_str(),
169 aliases);
170
171 for (char *tok, *ptr, *str = aliases;
172 (tok = ::strtok_r(str, ",", &ptr)); str = nullptr) {
173 traits->aliases.push_back(tok);
174 ALOGD("adding alias: '%s'", tok);
175 }
176 free(aliases);
177 }
178 }
179 }
180 mTraits = traits;
181
182 return mInit;
183 }
184
~ComponentModule()185 GoldfishComponentStore::ComponentModule::~ComponentModule() {
186 ALOGI("in %s", __func__);
187 if (destroyFactory && mComponentFactory) {
188 destroyFactory(mComponentFactory);
189 }
190 if (mLibHandle) {
191 ALOGI("unloading dll");
192 dlclose(mLibHandle);
193 }
194 }
195
createInterface(c2_node_id_t id,std::shared_ptr<C2ComponentInterface> * interface,std::function<void (::C2ComponentInterface *)> deleter)196 c2_status_t GoldfishComponentStore::ComponentModule::createInterface(
197 c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *interface,
198 std::function<void(::C2ComponentInterface *)> deleter) {
199 interface->reset();
200 if (mInit != C2_OK) {
201 return mInit;
202 }
203 std::shared_ptr<ComponentModule> module = shared_from_this();
204 c2_status_t res = mComponentFactory->createInterface(
205 id, interface, [module, deleter](C2ComponentInterface *p) mutable {
206 // capture module so that we ensure we still have it while deleting
207 // interface
208 deleter(p); // delete interface first
209 module.reset(); // remove module ref (not technically needed)
210 });
211 ALOGI("created interface");
212 return res;
213 }
214
createComponent(c2_node_id_t id,std::shared_ptr<C2Component> * component,std::function<void (::C2Component *)> deleter)215 c2_status_t GoldfishComponentStore::ComponentModule::createComponent(
216 c2_node_id_t id, std::shared_ptr<C2Component> *component,
217 std::function<void(::C2Component *)> deleter) {
218 component->reset();
219 if (mInit != C2_OK) {
220 return mInit;
221 }
222 std::shared_ptr<ComponentModule> module = shared_from_this();
223 c2_status_t res = mComponentFactory->createComponent(
224 id, component, [module, deleter](C2Component *p) mutable {
225 // capture module so that we ensure we still have it while deleting
226 // component
227 deleter(p); // delete component first
228 module.reset(); // remove module ref (not technically needed)
229 });
230 ALOGI("created component");
231 return res;
232 }
233
234 std::shared_ptr<const C2Component::Traits>
getTraits()235 GoldfishComponentStore::ComponentModule::getTraits() {
236 std::unique_lock<std::recursive_mutex> lock(mLock);
237 return mTraits;
238 }
239
240 // We have a property set indicating whether to use the host side codec
241 // or not (ro.boot.qemu.hwcodec.<mLibNameSuffix>).
BuildHWCodecPropName(const char * libname)242 static std::string BuildHWCodecPropName(const char *libname) {
243 using namespace std::literals::string_literals;
244 return "ro.boot.qemu.hwcodec."s + libname;
245 }
246
useAndroidGoldfishComponentInstance(const char * libname)247 static bool useAndroidGoldfishComponentInstance(const char *libname) {
248 const std::string propName = BuildHWCodecPropName(libname);
249 char propValue[PROP_VALUE_MAX];
250 bool myret = property_get(propName.c_str(), propValue, "") > 0 &&
251 strcmp("2", propValue) == 0;
252 if (myret) {
253 ALOGD("%s %d found prop %s val %s", __func__, __LINE__, propName.c_str(),
254 propValue);
255 }
256 return myret;
257 }
258
GoldfishComponentStore()259 GoldfishComponentStore::GoldfishComponentStore()
260 : mVisited(false), mReflector(std::make_shared<C2ReflectorHelper>()) {
261
262 ALOGW("created goldfish store %p reflector of param %p", this,
263 mReflector.get());
264 auto emplace = [this](const char *libPath) {
265 mComponents.emplace(libPath, libPath);
266 };
267
268 if (useAndroidGoldfishComponentInstance("vpxdec")) {
269 emplace("libcodec2_goldfish_vp8dec.so");
270 emplace("libcodec2_goldfish_vp9dec.so");
271 }
272 if (useAndroidGoldfishComponentInstance("avcdec")) {
273 emplace("libcodec2_goldfish_avcdec.so");
274 }
275 if (useAndroidGoldfishComponentInstance("hevcdec")) {
276 emplace("libcodec2_goldfish_hevcdec.so");
277 }
278 }
279
280 c2_status_t
copyBuffer(std::shared_ptr<C2GraphicBuffer> src,std::shared_ptr<C2GraphicBuffer> dst)281 GoldfishComponentStore::copyBuffer(std::shared_ptr<C2GraphicBuffer> src,
282 std::shared_ptr<C2GraphicBuffer> dst) {
283 (void)src;
284 (void)dst;
285 return C2_OMITTED;
286 }
287
query_sm(const std::vector<C2Param * > & stackParams,const std::vector<C2Param::Index> & heapParamIndices,std::vector<std::unique_ptr<C2Param>> * const heapParams) const288 c2_status_t GoldfishComponentStore::query_sm(
289 const std::vector<C2Param *> &stackParams,
290 const std::vector<C2Param::Index> &heapParamIndices,
291 std::vector<std::unique_ptr<C2Param>> *const heapParams) const {
292 (void)heapParams;
293 return stackParams.empty() && heapParamIndices.empty() ? C2_OK
294 : C2_BAD_INDEX;
295 }
296
config_sm(const std::vector<C2Param * > & params,std::vector<std::unique_ptr<C2SettingResult>> * const failures)297 c2_status_t GoldfishComponentStore::config_sm(
298 const std::vector<C2Param *> ¶ms,
299 std::vector<std::unique_ptr<C2SettingResult>> *const failures) {
300 (void)failures;
301 return params.empty() ? C2_OK : C2_BAD_INDEX;
302 }
303
visitComponents()304 void GoldfishComponentStore::visitComponents() {
305 std::lock_guard<std::mutex> lock(mMutex);
306 if (mVisited) {
307 return;
308 }
309 for (auto &pathAndLoader : mComponents) {
310 const C2String &path = pathAndLoader.first;
311 ComponentLoader &loader = pathAndLoader.second;
312 std::shared_ptr<ComponentModule> module;
313 if (loader.fetchModule(&module) == C2_OK) {
314 std::shared_ptr<const C2Component::Traits> traits =
315 module->getTraits();
316 if (traits) {
317 mComponentList.push_back(traits);
318 mComponentNameToPath.emplace(traits->name, path);
319 for (const C2String &alias : traits->aliases) {
320 mComponentNameToPath.emplace(alias, path);
321 }
322 }
323 }
324 }
325 mVisited = true;
326 }
327
328 std::vector<std::shared_ptr<const C2Component::Traits>>
listComponents()329 GoldfishComponentStore::listComponents() {
330 // This method SHALL return within 500ms.
331 visitComponents();
332 return mComponentList;
333 }
334
findComponent(C2String name,std::shared_ptr<ComponentModule> * module)335 c2_status_t GoldfishComponentStore::findComponent(
336 C2String name, std::shared_ptr<ComponentModule> *module) {
337 (*module).reset();
338 visitComponents();
339
340 auto pos = mComponentNameToPath.find(name);
341 if (pos != mComponentNameToPath.end()) {
342 return mComponents.at(pos->second).fetchModule(module);
343 }
344 return C2_NOT_FOUND;
345 }
346
createComponent(C2String name,std::shared_ptr<C2Component> * const component)347 c2_status_t GoldfishComponentStore::createComponent(
348 C2String name, std::shared_ptr<C2Component> *const component) {
349 // This method SHALL return within 100ms.
350 component->reset();
351 std::shared_ptr<ComponentModule> module;
352 c2_status_t res = findComponent(name, &module);
353 if (res == C2_OK) {
354 // TODO: get a unique node ID
355 res = module->createComponent(0, component);
356 }
357 return res;
358 }
359
createInterface(C2String name,std::shared_ptr<C2ComponentInterface> * const interface)360 c2_status_t GoldfishComponentStore::createInterface(
361 C2String name, std::shared_ptr<C2ComponentInterface> *const interface) {
362 // This method SHALL return within 100ms.
363 interface->reset();
364 std::shared_ptr<ComponentModule> module;
365 c2_status_t res = findComponent(name, &module);
366 if (res == C2_OK) {
367 // TODO: get a unique node ID
368 res = module->createInterface(0, interface);
369 }
370 return res;
371 }
372
querySupportedParams_nb(std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const373 c2_status_t GoldfishComponentStore::querySupportedParams_nb(
374 std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
375 (void)params;
376 return C2_OK;
377 }
378
querySupportedValues_sm(std::vector<C2FieldSupportedValuesQuery> & fields) const379 c2_status_t GoldfishComponentStore::querySupportedValues_sm(
380 std::vector<C2FieldSupportedValuesQuery> &fields) const {
381 return fields.empty() ? C2_OK : C2_BAD_INDEX;
382 }
383
384 std::shared_ptr<C2ParamReflector>
getParamReflector() const385 GoldfishComponentStore::getParamReflector() const {
386 return mReflector;
387 }
388
389 } // namespace android
390