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, &params);
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, &params);
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 *> &params,
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