You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1238 lines
47 KiB

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "C2Store"
#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <C2AllocatorBlob.h>
#include <C2AllocatorGralloc.h>
#include <C2AllocatorIon.h>
#include <C2DmaBufAllocator.h>
#include <C2BufferPriv.h>
#include <C2BqBufferPriv.h>
#include <C2Component.h>
#include <C2Config.h>
#include <C2PlatformStorePluginLoader.h>
#include <C2PlatformSupport.h>
#include <cutils/properties.h>
#include <util/C2InterfaceHelper.h>
#include <dlfcn.h>
#include <unistd.h> // getpagesize
#include <map>
#include <memory>
#include <mutex>
#ifdef __ANDROID_APEX__
#include <android-base/properties.h>
#endif
namespace android {
/**
* Returns the preferred component store in this process to access its interface.
*/
std::shared_ptr<C2ComponentStore> GetPreferredCodec2ComponentStore();
/**
* The platform allocator store provides basic allocator-types for the framework based on ion and
* gralloc. Allocators are not meant to be updatable.
*
* \todo Provide allocator based on ashmem
* \todo Move ion allocation into its HIDL or provide some mapping from memory usage to ion flags
* \todo Make this allocator store extendable
*/
class C2PlatformAllocatorStoreImpl : public C2PlatformAllocatorStore {
public:
C2PlatformAllocatorStoreImpl();
virtual c2_status_t fetchAllocator(
id_t id, std::shared_ptr<C2Allocator> *const allocator) override;
virtual std::vector<std::shared_ptr<const C2Allocator::Traits>> listAllocators_nb()
const override {
return std::vector<std::shared_ptr<const C2Allocator::Traits>>(); /// \todo
}
virtual C2String getName() const override {
return "android.allocator-store";
}
void setComponentStore(std::shared_ptr<C2ComponentStore> store);
~C2PlatformAllocatorStoreImpl() override = default;
private:
/// returns a shared-singleton blob allocator (gralloc-backed)
std::shared_ptr<C2Allocator> fetchBlobAllocator();
/// returns a shared-singleton ion allocator
std::shared_ptr<C2Allocator> fetchIonAllocator();
std::shared_ptr<C2Allocator> fetchDmaBufAllocator();
/// returns a shared-singleton gralloc allocator
std::shared_ptr<C2Allocator> fetchGrallocAllocator();
/// returns a shared-singleton bufferqueue supporting gralloc allocator
std::shared_ptr<C2Allocator> fetchBufferQueueAllocator();
/// component store to use
std::mutex _mComponentStoreSetLock; // protects the entire updating _mComponentStore and its
// dependencies
std::mutex _mComponentStoreReadLock; // must protect only read/write of _mComponentStore
std::shared_ptr<C2ComponentStore> _mComponentStore;
};
C2PlatformAllocatorStoreImpl::C2PlatformAllocatorStoreImpl() {
}
static bool using_ion(void) {
static int cached_result = []()->int {
struct stat buffer;
int ret = (stat("/dev/ion", &buffer) == 0);
if (property_get_int32("debug.c2.use_dmabufheaps", 0)) {
/*
* Double check that the system heap is present so we
* can gracefully fail back to ION if we cannot satisfy
* the override
*/
ret = (stat("/dev/dma_heap/system", &buffer) != 0);
if (ret)
ALOGE("debug.c2.use_dmabufheaps set, but no system heap. Ignoring override!");
else
ALOGD("debug.c2.use_dmabufheaps set, forcing DMABUF Heaps");
}
if (ret)
ALOGD("Using ION\n");
else
ALOGD("Using DMABUF Heaps\n");
return ret;
}();
return (cached_result == 1);
}
c2_status_t C2PlatformAllocatorStoreImpl::fetchAllocator(
id_t id, std::shared_ptr<C2Allocator> *const allocator) {
allocator->reset();
if (id == C2AllocatorStore::DEFAULT_LINEAR) {
id = GetPreferredLinearAllocatorId(GetCodec2PoolMask());
}
switch (id) {
// TODO: should we implement a generic registry for all, and use that?
case C2PlatformAllocatorStore::ION: /* also ::DMABUFHEAP */
if (using_ion())
*allocator = fetchIonAllocator();
else
*allocator = fetchDmaBufAllocator();
break;
case C2PlatformAllocatorStore::GRALLOC:
case C2AllocatorStore::DEFAULT_GRAPHIC:
*allocator = fetchGrallocAllocator();
break;
case C2PlatformAllocatorStore::BUFFERQUEUE:
*allocator = fetchBufferQueueAllocator();
break;
case C2PlatformAllocatorStore::BLOB:
*allocator = fetchBlobAllocator();
break;
default:
// Try to create allocator from platform store plugins.
c2_status_t res =
C2PlatformStorePluginLoader::GetInstance()->createAllocator(id, allocator);
if (res != C2_OK) {
return res;
}
break;
}
if (*allocator == nullptr) {
return C2_NO_MEMORY;
}
return C2_OK;
}
namespace {
std::mutex gIonAllocatorMutex;
std::mutex gDmaBufAllocatorMutex;
std::weak_ptr<C2AllocatorIon> gIonAllocator;
std::weak_ptr<C2DmaBufAllocator> gDmaBufAllocator;
void UseComponentStoreForIonAllocator(
const std::shared_ptr<C2AllocatorIon> allocator,
std::shared_ptr<C2ComponentStore> store) {
C2AllocatorIon::UsageMapperFn mapper;
uint64_t minUsage = 0;
uint64_t maxUsage = C2MemoryUsage(C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE).expected;
size_t blockSize = getpagesize();
// query min and max usage as well as block size via supported values
C2StoreIonUsageInfo usageInfo;
std::vector<C2FieldSupportedValuesQuery> query = {
C2FieldSupportedValuesQuery::Possible(C2ParamField::Make(usageInfo, usageInfo.usage)),
C2FieldSupportedValuesQuery::Possible(C2ParamField::Make(usageInfo, usageInfo.capacity)),
};
c2_status_t res = store->querySupportedValues_sm(query);
if (res == C2_OK) {
if (query[0].status == C2_OK) {
const C2FieldSupportedValues &fsv = query[0].values;
if (fsv.type == C2FieldSupportedValues::FLAGS && !fsv.values.empty()) {
minUsage = fsv.values[0].u64;
maxUsage = 0;
for (C2Value::Primitive v : fsv.values) {
maxUsage |= v.u64;
}
}
}
if (query[1].status == C2_OK) {
const C2FieldSupportedValues &fsv = query[1].values;
if (fsv.type == C2FieldSupportedValues::RANGE && fsv.range.step.u32 > 0) {
blockSize = fsv.range.step.u32;
}
}
mapper = [store](C2MemoryUsage usage, size_t capacity,
size_t *align, unsigned *heapMask, unsigned *flags) -> c2_status_t {
if (capacity > UINT32_MAX) {
return C2_BAD_VALUE;
}
C2StoreIonUsageInfo usageInfo = { usage.expected, capacity };
std::vector<std::unique_ptr<C2SettingResult>> failures; // TODO: remove
c2_status_t res = store->config_sm({&usageInfo}, &failures);
if (res == C2_OK) {
*align = usageInfo.minAlignment;
*heapMask = usageInfo.heapMask;
*flags = usageInfo.allocFlags;
}
return res;
};
}
allocator->setUsageMapper(mapper, minUsage, maxUsage, blockSize);
}
void UseComponentStoreForDmaBufAllocator(const std::shared_ptr<C2DmaBufAllocator> allocator,
std::shared_ptr<C2ComponentStore> store) {
C2DmaBufAllocator::UsageMapperFn mapper;
const size_t maxHeapNameLen = 128;
uint64_t minUsage = 0;
uint64_t maxUsage = C2MemoryUsage(C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE).expected;
size_t blockSize = getpagesize();
// query min and max usage as well as block size via supported values
std::unique_ptr<C2StoreDmaBufUsageInfo> usageInfo;
usageInfo = C2StoreDmaBufUsageInfo::AllocUnique(maxHeapNameLen);
std::vector<C2FieldSupportedValuesQuery> query = {
C2FieldSupportedValuesQuery::Possible(C2ParamField::Make(*usageInfo, usageInfo->m.usage)),
C2FieldSupportedValuesQuery::Possible(
C2ParamField::Make(*usageInfo, usageInfo->m.capacity)),
};
c2_status_t res = store->querySupportedValues_sm(query);
if (res == C2_OK) {
if (query[0].status == C2_OK) {
const C2FieldSupportedValues& fsv = query[0].values;
if (fsv.type == C2FieldSupportedValues::FLAGS && !fsv.values.empty()) {
minUsage = fsv.values[0].u64;
maxUsage = 0;
for (C2Value::Primitive v : fsv.values) {
maxUsage |= v.u64;
}
}
}
if (query[1].status == C2_OK) {
const C2FieldSupportedValues& fsv = query[1].values;
if (fsv.type == C2FieldSupportedValues::RANGE && fsv.range.step.u32 > 0) {
blockSize = fsv.range.step.u32;
}
}
mapper = [store](C2MemoryUsage usage, size_t capacity, C2String* heapName,
unsigned* flags) -> c2_status_t {
if (capacity > UINT32_MAX) {
return C2_BAD_VALUE;
}
std::unique_ptr<C2StoreDmaBufUsageInfo> usageInfo;
usageInfo = C2StoreDmaBufUsageInfo::AllocUnique(maxHeapNameLen, usage.expected, capacity);
std::vector<std::unique_ptr<C2SettingResult>> failures; // TODO: remove
c2_status_t res = store->config_sm({&*usageInfo}, &failures);
if (res == C2_OK) {
*heapName = C2String(usageInfo->m.heapName);
*flags = usageInfo->m.allocFlags;
}
return res;
};
}
allocator->setUsageMapper(mapper, minUsage, maxUsage, blockSize);
}
}
void C2PlatformAllocatorStoreImpl::setComponentStore(std::shared_ptr<C2ComponentStore> store) {
// technically this set lock is not needed, but is here for safety in case we add more
// getter orders
std::lock_guard<std::mutex> lock(_mComponentStoreSetLock);
{
std::lock_guard<std::mutex> lock(_mComponentStoreReadLock);
_mComponentStore = store;
}
std::shared_ptr<C2AllocatorIon> allocator;
{
std::lock_guard<std::mutex> lock(gIonAllocatorMutex);
allocator = gIonAllocator.lock();
}
if (allocator) {
UseComponentStoreForIonAllocator(allocator, store);
}
}
std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchIonAllocator() {
std::lock_guard<std::mutex> lock(gIonAllocatorMutex);
std::shared_ptr<C2AllocatorIon> allocator = gIonAllocator.lock();
if (allocator == nullptr) {
std::shared_ptr<C2ComponentStore> componentStore;
{
std::lock_guard<std::mutex> lock(_mComponentStoreReadLock);
componentStore = _mComponentStore;
}
allocator = std::make_shared<C2AllocatorIon>(C2PlatformAllocatorStore::ION);
UseComponentStoreForIonAllocator(allocator, componentStore);
gIonAllocator = allocator;
}
return allocator;
}
std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchDmaBufAllocator() {
std::lock_guard<std::mutex> lock(gDmaBufAllocatorMutex);
std::shared_ptr<C2DmaBufAllocator> allocator = gDmaBufAllocator.lock();
if (allocator == nullptr) {
std::shared_ptr<C2ComponentStore> componentStore;
{
std::lock_guard<std::mutex> lock(_mComponentStoreReadLock);
componentStore = _mComponentStore;
}
allocator = std::make_shared<C2DmaBufAllocator>(C2PlatformAllocatorStore::DMABUFHEAP);
UseComponentStoreForDmaBufAllocator(allocator, componentStore);
gDmaBufAllocator = allocator;
}
return allocator;
}
std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchBlobAllocator() {
static std::mutex mutex;
static std::weak_ptr<C2Allocator> blobAllocator;
std::lock_guard<std::mutex> lock(mutex);
std::shared_ptr<C2Allocator> allocator = blobAllocator.lock();
if (allocator == nullptr) {
allocator = std::make_shared<C2AllocatorBlob>(C2PlatformAllocatorStore::BLOB);
blobAllocator = allocator;
}
return allocator;
}
std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchGrallocAllocator() {
static std::mutex mutex;
static std::weak_ptr<C2Allocator> grallocAllocator;
std::lock_guard<std::mutex> lock(mutex);
std::shared_ptr<C2Allocator> allocator = grallocAllocator.lock();
if (allocator == nullptr) {
allocator = std::make_shared<C2AllocatorGralloc>(C2PlatformAllocatorStore::GRALLOC);
grallocAllocator = allocator;
}
return allocator;
}
std::shared_ptr<C2Allocator> C2PlatformAllocatorStoreImpl::fetchBufferQueueAllocator() {
static std::mutex mutex;
static std::weak_ptr<C2Allocator> grallocAllocator;
std::lock_guard<std::mutex> lock(mutex);
std::shared_ptr<C2Allocator> allocator = grallocAllocator.lock();
if (allocator == nullptr) {
allocator = std::make_shared<C2AllocatorGralloc>(
C2PlatformAllocatorStore::BUFFERQUEUE, true);
grallocAllocator = allocator;
}
return allocator;
}
namespace {
std::mutex gPreferredComponentStoreMutex;
std::shared_ptr<C2ComponentStore> gPreferredComponentStore;
std::mutex gPlatformAllocatorStoreMutex;
std::weak_ptr<C2PlatformAllocatorStoreImpl> gPlatformAllocatorStore;
}
std::shared_ptr<C2AllocatorStore> GetCodec2PlatformAllocatorStore() {
std::lock_guard<std::mutex> lock(gPlatformAllocatorStoreMutex);
std::shared_ptr<C2PlatformAllocatorStoreImpl> store = gPlatformAllocatorStore.lock();
if (store == nullptr) {
store = std::make_shared<C2PlatformAllocatorStoreImpl>();
store->setComponentStore(GetPreferredCodec2ComponentStore());
gPlatformAllocatorStore = store;
}
return store;
}
void SetPreferredCodec2ComponentStore(std::shared_ptr<C2ComponentStore> componentStore) {
static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex); // don't interleve set-s
// update preferred store
{
std::lock_guard<std::mutex> lock(gPreferredComponentStoreMutex);
gPreferredComponentStore = componentStore;
}
// update platform allocator's store as well if it is alive
std::shared_ptr<C2PlatformAllocatorStoreImpl> allocatorStore;
{
std::lock_guard<std::mutex> lock(gPlatformAllocatorStoreMutex);
allocatorStore = gPlatformAllocatorStore.lock();
}
if (allocatorStore) {
allocatorStore->setComponentStore(componentStore);
}
}
std::shared_ptr<C2ComponentStore> GetPreferredCodec2ComponentStore() {
std::lock_guard<std::mutex> lock(gPreferredComponentStoreMutex);
return gPreferredComponentStore ? gPreferredComponentStore : GetCodec2PlatformComponentStore();
}
int GetCodec2PoolMask() {
return property_get_int32(
"debug.stagefright.c2-poolmask",
1 << C2PlatformAllocatorStore::ION |
1 << C2PlatformAllocatorStore::BUFFERQUEUE);
}
C2PlatformAllocatorStore::id_t GetPreferredLinearAllocatorId(int poolMask) {
return ((poolMask >> C2PlatformAllocatorStore::BLOB) & 1) ? C2PlatformAllocatorStore::BLOB
: C2PlatformAllocatorStore::ION;
}
namespace {
class _C2BlockPoolCache {
public:
_C2BlockPoolCache() : mBlockPoolSeqId(C2BlockPool::PLATFORM_START + 1) {}
c2_status_t _createBlockPool(
C2PlatformAllocatorStore::id_t allocatorId,
std::vector<std::shared_ptr<const C2Component>> components,
C2BlockPool::local_id_t poolId,
std::shared_ptr<C2BlockPool> *pool) {
std::shared_ptr<C2AllocatorStore> allocatorStore =
GetCodec2PlatformAllocatorStore();
std::shared_ptr<C2Allocator> allocator;
c2_status_t res = C2_NOT_FOUND;
if (allocatorId == C2AllocatorStore::DEFAULT_LINEAR) {
allocatorId = GetPreferredLinearAllocatorId(GetCodec2PoolMask());
}
switch(allocatorId) {
case C2PlatformAllocatorStore::ION: /* also ::DMABUFHEAP */
res = allocatorStore->fetchAllocator(
C2PlatformAllocatorStore::ION, &allocator);
if (res == C2_OK) {
std::shared_ptr<C2BlockPool> ptr =
std::make_shared<C2PooledBlockPool>(
allocator, poolId);
*pool = ptr;
mBlockPools[poolId] = ptr;
mComponents[poolId].insert(
mComponents[poolId].end(),
components.begin(), components.end());
}
break;
case C2PlatformAllocatorStore::BLOB:
res = allocatorStore->fetchAllocator(
C2PlatformAllocatorStore::BLOB, &allocator);
if (res == C2_OK) {
std::shared_ptr<C2BlockPool> ptr =
std::make_shared<C2PooledBlockPool>(
allocator, poolId);
*pool = ptr;
mBlockPools[poolId] = ptr;
mComponents[poolId].insert(
mComponents[poolId].end(),
components.begin(), components.end());
}
break;
case C2PlatformAllocatorStore::GRALLOC:
case C2AllocatorStore::DEFAULT_GRAPHIC:
res = allocatorStore->fetchAllocator(
C2AllocatorStore::DEFAULT_GRAPHIC, &allocator);
if (res == C2_OK) {
std::shared_ptr<C2BlockPool> ptr =
std::make_shared<C2PooledBlockPool>(allocator, poolId);
*pool = ptr;
mBlockPools[poolId] = ptr;
mComponents[poolId].insert(
mComponents[poolId].end(),
components.begin(), components.end());
}
break;
case C2PlatformAllocatorStore::BUFFERQUEUE:
res = allocatorStore->fetchAllocator(
C2PlatformAllocatorStore::BUFFERQUEUE, &allocator);
if (res == C2_OK) {
std::shared_ptr<C2BlockPool> ptr =
std::make_shared<C2BufferQueueBlockPool>(
allocator, poolId);
*pool = ptr;
mBlockPools[poolId] = ptr;
mComponents[poolId].insert(
mComponents[poolId].end(),
components.begin(), components.end());
}
break;
default:
// Try to create block pool from platform store plugins.
std::shared_ptr<C2BlockPool> ptr;
res = C2PlatformStorePluginLoader::GetInstance()->createBlockPool(
allocatorId, poolId, &ptr);
if (res == C2_OK) {
*pool = ptr;
mBlockPools[poolId] = ptr;
mComponents[poolId].insert(
mComponents[poolId].end(),
components.begin(), components.end());
}
break;
}
return res;
}
c2_status_t createBlockPool(
C2PlatformAllocatorStore::id_t allocatorId,
std::vector<std::shared_ptr<const C2Component>> components,
std::shared_ptr<C2BlockPool> *pool) {
return _createBlockPool(allocatorId, components, mBlockPoolSeqId++, pool);
}
bool getBlockPool(
C2BlockPool::local_id_t blockPoolId,
std::shared_ptr<const C2Component> component,
std::shared_ptr<C2BlockPool> *pool) {
// TODO: use one iterator for multiple blockpool type scalability.
std::shared_ptr<C2BlockPool> ptr;
auto it = mBlockPools.find(blockPoolId);
if (it != mBlockPools.end()) {
ptr = it->second.lock();
if (!ptr) {
mBlockPools.erase(it);
mComponents.erase(blockPoolId);
} else {
auto found = std::find_if(
mComponents[blockPoolId].begin(),
mComponents[blockPoolId].end(),
[component](const std::weak_ptr<const C2Component> &ptr) {
return component == ptr.lock();
});
if (found != mComponents[blockPoolId].end()) {
*pool = ptr;
return true;
}
}
}
return false;
}
private:
C2BlockPool::local_id_t mBlockPoolSeqId;
std::map<C2BlockPool::local_id_t, std::weak_ptr<C2BlockPool>> mBlockPools;
std::map<C2BlockPool::local_id_t, std::vector<std::weak_ptr<const C2Component>>> mComponents;
};
static std::unique_ptr<_C2BlockPoolCache> sBlockPoolCache =
std::make_unique<_C2BlockPoolCache>();
static std::mutex sBlockPoolCacheMutex;
} // anynymous namespace
c2_status_t GetCodec2BlockPool(
C2BlockPool::local_id_t id, std::shared_ptr<const C2Component> component,
std::shared_ptr<C2BlockPool> *pool) {
pool->reset();
std::lock_guard<std::mutex> lock(sBlockPoolCacheMutex);
std::shared_ptr<C2AllocatorStore> allocatorStore = GetCodec2PlatformAllocatorStore();
std::shared_ptr<C2Allocator> allocator;
c2_status_t res = C2_NOT_FOUND;
if (id >= C2BlockPool::PLATFORM_START) {
if (sBlockPoolCache->getBlockPool(id, component, pool)) {
return C2_OK;
}
}
switch (id) {
case C2BlockPool::BASIC_LINEAR:
res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &allocator);
if (res == C2_OK) {
*pool = std::make_shared<C2BasicLinearBlockPool>(allocator);
}
break;
case C2BlockPool::BASIC_GRAPHIC:
res = allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_GRAPHIC, &allocator);
if (res == C2_OK) {
*pool = std::make_shared<C2BasicGraphicBlockPool>(allocator);
}
break;
// TODO: remove this. this is temporary
case C2BlockPool::PLATFORM_START:
res = sBlockPoolCache->_createBlockPool(
C2PlatformAllocatorStore::BUFFERQUEUE, {component}, id, pool);
break;
default:
break;
}
return res;
}
c2_status_t CreateCodec2BlockPool(
C2PlatformAllocatorStore::id_t allocatorId,
const std::vector<std::shared_ptr<const C2Component>> &components,
std::shared_ptr<C2BlockPool> *pool) {
pool->reset();
std::lock_guard<std::mutex> lock(sBlockPoolCacheMutex);
return sBlockPoolCache->createBlockPool(allocatorId, components, pool);
}
c2_status_t CreateCodec2BlockPool(
C2PlatformAllocatorStore::id_t allocatorId,
std::shared_ptr<const C2Component> component,
std::shared_ptr<C2BlockPool> *pool) {
pool->reset();
std::lock_guard<std::mutex> lock(sBlockPoolCacheMutex);
return sBlockPoolCache->createBlockPool(allocatorId, {component}, pool);
}
class C2PlatformComponentStore : public C2ComponentStore {
public:
virtual std::vector<std::shared_ptr<const C2Component::Traits>> listComponents() override;
virtual std::shared_ptr<C2ParamReflector> getParamReflector() const override;
virtual C2String getName() const override;
virtual c2_status_t querySupportedValues_sm(
std::vector<C2FieldSupportedValuesQuery> &fields) const override;
virtual c2_status_t querySupportedParams_nb(
std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const override;
virtual c2_status_t query_sm(
const std::vector<C2Param*> &stackParams,
const std::vector<C2Param::Index> &heapParamIndices,
std::vector<std::unique_ptr<C2Param>> *const heapParams) const override;
virtual c2_status_t createInterface(
C2String name, std::shared_ptr<C2ComponentInterface> *const interface) override;
virtual c2_status_t createComponent(
C2String name, std::shared_ptr<C2Component> *const component) override;
virtual c2_status_t copyBuffer(
std::shared_ptr<C2GraphicBuffer> src, std::shared_ptr<C2GraphicBuffer> dst) override;
virtual c2_status_t config_sm(
const std::vector<C2Param*> &params,
std::vector<std::unique_ptr<C2SettingResult>> *const failures) override;
C2PlatformComponentStore();
// For testing only
C2PlatformComponentStore(
std::vector<std::tuple<C2String,
C2ComponentFactory::CreateCodec2FactoryFunc,
C2ComponentFactory::DestroyCodec2FactoryFunc>>);
virtual ~C2PlatformComponentStore() override = default;
private:
/**
* An object encapsulating a loaded component module.
*
* \todo provide a way to add traits to known components here to avoid loading the .so-s
* for listComponents
*/
struct ComponentModule : public C2ComponentFactory,
public std::enable_shared_from_this<ComponentModule> {
virtual c2_status_t createComponent(
c2_node_id_t id, std::shared_ptr<C2Component> *component,
ComponentDeleter deleter = std::default_delete<C2Component>()) override;
virtual c2_status_t createInterface(
c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *interface,
InterfaceDeleter deleter = std::default_delete<C2ComponentInterface>()) override;
/**
* \returns the traits of the component in this module.
*/
std::shared_ptr<const C2Component::Traits> getTraits();
/**
* Creates an uninitialized component module.
*
* \param name[in] component name.
*
* \note Only used by ComponentLoader.
*/
ComponentModule()
: mInit(C2_NO_INIT),
mLibHandle(nullptr),
createFactory(nullptr),
destroyFactory(nullptr),
mComponentFactory(nullptr) {
}
/**
* Creates an uninitialized component module.
* NOTE: For testing only
*
* \param name[in] component name.
*
* \note Only used by ComponentLoader.
*/
ComponentModule(
C2ComponentFactory::CreateCodec2FactoryFunc createFactory,
C2ComponentFactory::DestroyCodec2FactoryFunc destroyFactory)
: mInit(C2_NO_INIT),
mLibHandle(nullptr),
createFactory(createFactory),
destroyFactory(destroyFactory),
mComponentFactory(nullptr) {
}
/**
* Initializes a component module with a given library path. Must be called exactly once.
*
* \note Only used by ComponentLoader.
*
* \param libPath[in] library path
*
* \retval C2_OK the component module has been successfully loaded
* \retval C2_NO_MEMORY not enough memory to loading the component module
* \retval C2_NOT_FOUND could not locate the component module
* \retval C2_CORRUPTED the component module could not be loaded (unexpected)
* \retval C2_REFUSED permission denied to load the component module (unexpected)
* \retval C2_TIMED_OUT could not load the module within the time limit (unexpected)
*/
c2_status_t init(std::string libPath);
virtual ~ComponentModule() override;
protected:
std::recursive_mutex mLock; ///< lock protecting mTraits
std::shared_ptr<C2Component::Traits> mTraits; ///< cached component traits
c2_status_t mInit; ///< initialization result
void *mLibHandle; ///< loaded library handle
C2ComponentFactory::CreateCodec2FactoryFunc createFactory; ///< loaded create function
C2ComponentFactory::DestroyCodec2FactoryFunc destroyFactory; ///< loaded destroy function
C2ComponentFactory *mComponentFactory; ///< loaded/created component factory
};
/**
* An object encapsulating a loadable component module.
*
* \todo make this also work for enumerations
*/
struct ComponentLoader {
/**
* Load the component module.
*
* This method simply returns the component module if it is already currently loaded, or
* attempts to load it if it is not.
*
* \param module[out] pointer to the shared pointer where the loaded module shall be stored.
* This will be nullptr on error.
*
* \retval C2_OK the component module has been successfully loaded
* \retval C2_NO_MEMORY not enough memory to loading the component module
* \retval C2_NOT_FOUND could not locate the component module
* \retval C2_CORRUPTED the component module could not be loaded
* \retval C2_REFUSED permission denied to load the component module
*/
c2_status_t fetchModule(std::shared_ptr<ComponentModule> *module) {
c2_status_t res = C2_OK;
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<ComponentModule> localModule = mModule.lock();
if (localModule == nullptr) {
if(mCreateFactory) {
// For testing only
localModule = std::make_shared<ComponentModule>(mCreateFactory,
mDestroyFactory);
} else {
localModule = std::make_shared<ComponentModule>();
}
res = localModule->init(mLibPath);
if (res == C2_OK) {
mModule = localModule;
}
}
*module = localModule;
return res;
}
/**
* Creates a component loader for a specific library path (or name).
*/
ComponentLoader(std::string libPath)
: mLibPath(libPath) {}
// For testing only
ComponentLoader(std::tuple<C2String,
C2ComponentFactory::CreateCodec2FactoryFunc,
C2ComponentFactory::DestroyCodec2FactoryFunc> func)
: mLibPath(std::get<0>(func)),
mCreateFactory(std::get<1>(func)),
mDestroyFactory(std::get<2>(func)) {}
private:
std::mutex mMutex; ///< mutex guarding the module
std::weak_ptr<ComponentModule> mModule; ///< weak reference to the loaded module
std::string mLibPath; ///< library path
// For testing only
C2ComponentFactory::CreateCodec2FactoryFunc mCreateFactory = nullptr;
C2ComponentFactory::DestroyCodec2FactoryFunc mDestroyFactory = nullptr;
};
struct Interface : public C2InterfaceHelper {
std::shared_ptr<C2StoreIonUsageInfo> mIonUsageInfo;
std::shared_ptr<C2StoreDmaBufUsageInfo> mDmaBufUsageInfo;
Interface(std::shared_ptr<C2ReflectorHelper> reflector)
: C2InterfaceHelper(reflector) {
setDerivedInstance(this);
struct Setter {
static C2R setIonUsage(bool /* mayBlock */, C2P<C2StoreIonUsageInfo> &me) {
#ifdef __ANDROID_APEX__
static int32_t defaultHeapMask = [] {
int32_t heapmask = base::GetIntProperty(
"ro.com.android.media.swcodec.ion.heapmask", int32_t(0xFFFFFFFF));
ALOGD("Default ION heapmask = %d", heapmask);
return heapmask;
}();
static int32_t defaultFlags = [] {
int32_t flags = base::GetIntProperty(
"ro.com.android.media.swcodec.ion.flags", 0);
ALOGD("Default ION flags = %d", flags);
return flags;
}();
static uint32_t defaultAlign = [] {
uint32_t align = base::GetUintProperty(
"ro.com.android.media.swcodec.ion.align", 0u);
ALOGD("Default ION align = %d", align);
return align;
}();
me.set().heapMask = defaultHeapMask;
me.set().allocFlags = defaultFlags;
me.set().minAlignment = defaultAlign;
#else
me.set().heapMask = ~0;
me.set().allocFlags = 0;
me.set().minAlignment = 0;
#endif
return C2R::Ok();
};
static C2R setDmaBufUsage(bool /* mayBlock */, C2P<C2StoreDmaBufUsageInfo> &me) {
long long usage = (long long)me.get().m.usage;
if (C2DmaBufAllocator::system_uncached_supported() &&
!(usage & (C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE))) {
strncpy(me.set().m.heapName, "system-uncached", me.v.flexCount());
} else {
strncpy(me.set().m.heapName, "system", me.v.flexCount());
}
me.set().m.allocFlags = 0;
return C2R::Ok();
};
};
addParameter(
DefineParam(mIonUsageInfo, "ion-usage")
.withDefault(new C2StoreIonUsageInfo())
.withFields({
C2F(mIonUsageInfo, usage).flags({C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE}),
C2F(mIonUsageInfo, capacity).inRange(0, UINT32_MAX, 1024),
C2F(mIonUsageInfo, heapMask).any(),
C2F(mIonUsageInfo, allocFlags).flags({}),
C2F(mIonUsageInfo, minAlignment).equalTo(0)
})
.withSetter(Setter::setIonUsage)
.build());
addParameter(
DefineParam(mDmaBufUsageInfo, "dmabuf-usage")
.withDefault(C2StoreDmaBufUsageInfo::AllocShared(0))
.withFields({
C2F(mDmaBufUsageInfo, m.usage).flags({C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE}),
C2F(mDmaBufUsageInfo, m.capacity).inRange(0, UINT32_MAX, 1024),
C2F(mDmaBufUsageInfo, m.allocFlags).flags({}),
C2F(mDmaBufUsageInfo, m.heapName).any(),
})
.withSetter(Setter::setDmaBufUsage)
.build());
}
};
/**
* Retrieves the component module for a component.
*
* \param module pointer to a shared_pointer where the component module will be stored on
* success.
*
* \retval C2_OK the component loader has been successfully retrieved
* \retval C2_NO_MEMORY not enough memory to locate the component loader
* \retval C2_NOT_FOUND could not locate the component to be loaded
* \retval C2_CORRUPTED the component loader could not be identified due to some modules being
* corrupted (this can happen if the name does not refer to an already
* identified component but some components could not be loaded due to
* bad library)
* \retval C2_REFUSED permission denied to find the component loader for the named component
* (this can happen if the name does not refer to an already identified
* component but some components could not be loaded due to lack of
* permissions)
*/
c2_status_t findComponent(C2String name, std::shared_ptr<ComponentModule> *module);
/**
* Loads each component module and discover its contents.
*/
void visitComponents();
std::mutex mMutex; ///< mutex guarding the component lists during construction
bool mVisited; ///< component modules visited
std::map<C2String, ComponentLoader> mComponents; ///< path -> component module
std::map<C2String, C2String> mComponentNameToPath; ///< name -> path
std::vector<std::shared_ptr<const C2Component::Traits>> mComponentList;
std::shared_ptr<C2ReflectorHelper> mReflector;
Interface mInterface;
// For testing only
std::vector<std::tuple<C2String,
C2ComponentFactory::CreateCodec2FactoryFunc,
C2ComponentFactory::DestroyCodec2FactoryFunc>> mCodec2FactoryFuncs;
};
c2_status_t C2PlatformComponentStore::ComponentModule::init(
std::string libPath) {
ALOGV("in %s", __func__);
ALOGV("loading dll");
if(!createFactory) {
mLibHandle = dlopen(libPath.c_str(), RTLD_NOW|RTLD_NODELETE);
LOG_ALWAYS_FATAL_IF(mLibHandle == nullptr,
"could not dlopen %s: %s", libPath.c_str(), dlerror());
createFactory =
(C2ComponentFactory::CreateCodec2FactoryFunc)dlsym(mLibHandle, "CreateCodec2Factory");
LOG_ALWAYS_FATAL_IF(createFactory == nullptr,
"createFactory is null in %s", libPath.c_str());
destroyFactory =
(C2ComponentFactory::DestroyCodec2FactoryFunc)dlsym(mLibHandle, "DestroyCodec2Factory");
LOG_ALWAYS_FATAL_IF(destroyFactory == nullptr,
"destroyFactory is null in %s", libPath.c_str());
}
mComponentFactory = createFactory();
if (mComponentFactory == nullptr) {
ALOGD("could not create factory in %s", libPath.c_str());
mInit = C2_NO_MEMORY;
} else {
mInit = C2_OK;
}
if (mInit != C2_OK) {
return mInit;
}
std::shared_ptr<C2ComponentInterface> intf;
c2_status_t res = createInterface(0, &intf);
if (res != C2_OK) {
ALOGD("failed to create interface: %d", res);
return mInit;
}
std::shared_ptr<C2Component::Traits> traits(new (std::nothrow) C2Component::Traits);
if (traits) {
if (!C2InterfaceUtils::FillTraitsFromInterface(traits.get(), intf)) {
ALOGD("Failed to fill traits from interface");
return mInit;
}
// TODO: get this properly from the store during emplace
switch (traits->domain) {
case C2Component::DOMAIN_AUDIO:
traits->rank = 8;
break;
default:
traits->rank = 512;
}
}
mTraits = traits;
return mInit;
}
C2PlatformComponentStore::ComponentModule::~ComponentModule() {
ALOGV("in %s", __func__);
if (destroyFactory && mComponentFactory) {
destroyFactory(mComponentFactory);
}
if (mLibHandle) {
ALOGV("unloading dll");
dlclose(mLibHandle);
}
}
c2_status_t C2PlatformComponentStore::ComponentModule::createInterface(
c2_node_id_t id, std::shared_ptr<C2ComponentInterface> *interface,
std::function<void(::C2ComponentInterface*)> deleter) {
interface->reset();
if (mInit != C2_OK) {
return mInit;
}
std::shared_ptr<ComponentModule> module = shared_from_this();
c2_status_t res = mComponentFactory->createInterface(
id, interface, [module, deleter](C2ComponentInterface *p) mutable {
// capture module so that we ensure we still have it while deleting interface
deleter(p); // delete interface first
module.reset(); // remove module ref (not technically needed)
});
return res;
}
c2_status_t C2PlatformComponentStore::ComponentModule::createComponent(
c2_node_id_t id, std::shared_ptr<C2Component> *component,
std::function<void(::C2Component*)> deleter) {
component->reset();
if (mInit != C2_OK) {
return mInit;
}
std::shared_ptr<ComponentModule> module = shared_from_this();
c2_status_t res = mComponentFactory->createComponent(
id, component, [module, deleter](C2Component *p) mutable {
// capture module so that we ensure we still have it while deleting component
deleter(p); // delete component first
module.reset(); // remove module ref (not technically needed)
});
return res;
}
std::shared_ptr<const C2Component::Traits> C2PlatformComponentStore::ComponentModule::getTraits() {
std::unique_lock<std::recursive_mutex> lock(mLock);
return mTraits;
}
C2PlatformComponentStore::C2PlatformComponentStore()
: mVisited(false),
mReflector(std::make_shared<C2ReflectorHelper>()),
mInterface(mReflector) {
auto emplace = [this](const char *libPath) {
mComponents.emplace(libPath, libPath);
};
// TODO: move this also into a .so so it can be updated
emplace("libcodec2_soft_aacdec.so");
emplace("libcodec2_soft_aacenc.so");
emplace("libcodec2_soft_amrnbdec.so");
emplace("libcodec2_soft_amrnbenc.so");
emplace("libcodec2_soft_amrwbdec.so");
emplace("libcodec2_soft_amrwbenc.so");
//emplace("libcodec2_soft_av1dec_aom.so"); // deprecated for the gav1 implementation
emplace("libcodec2_soft_av1dec_gav1.so");
emplace("libcodec2_soft_avcdec.so");
emplace("libcodec2_soft_avcenc.so");
emplace("libcodec2_soft_flacdec.so");
emplace("libcodec2_soft_flacenc.so");
emplace("libcodec2_soft_g711alawdec.so");
emplace("libcodec2_soft_g711mlawdec.so");
emplace("libcodec2_soft_gsmdec.so");
emplace("libcodec2_soft_h263dec.so");
emplace("libcodec2_soft_h263enc.so");
emplace("libcodec2_soft_hevcdec.so");
emplace("libcodec2_soft_hevcenc.so");
emplace("libcodec2_soft_mp3dec.so");
emplace("libcodec2_soft_mpeg2dec.so");
emplace("libcodec2_soft_mpeg4dec.so");
emplace("libcodec2_soft_mpeg4enc.so");
emplace("libcodec2_soft_opusdec.so");
emplace("libcodec2_soft_opusenc.so");
emplace("libcodec2_soft_rawdec.so");
emplace("libcodec2_soft_vorbisdec.so");
emplace("libcodec2_soft_vp8dec.so");
emplace("libcodec2_soft_vp8enc.so");
emplace("libcodec2_soft_vp9dec.so");
emplace("libcodec2_soft_vp9enc.so");
}
// For testing only
C2PlatformComponentStore::C2PlatformComponentStore(
std::vector<std::tuple<C2String,
C2ComponentFactory::CreateCodec2FactoryFunc,
C2ComponentFactory::DestroyCodec2FactoryFunc>> funcs)
: mVisited(false),
mReflector(std::make_shared<C2ReflectorHelper>()),
mInterface(mReflector),
mCodec2FactoryFuncs(funcs) {
for(auto const& func: mCodec2FactoryFuncs) {
mComponents.emplace(std::get<0>(func), func);
}
}
c2_status_t C2PlatformComponentStore::copyBuffer(
std::shared_ptr<C2GraphicBuffer> src, std::shared_ptr<C2GraphicBuffer> dst) {
(void)src;
(void)dst;
return C2_OMITTED;
}
c2_status_t C2PlatformComponentStore::query_sm(
const std::vector<C2Param*> &stackParams,
const std::vector<C2Param::Index> &heapParamIndices,
std::vector<std::unique_ptr<C2Param>> *const heapParams) const {
return mInterface.query(stackParams, heapParamIndices, C2_MAY_BLOCK, heapParams);
}
c2_status_t C2PlatformComponentStore::config_sm(
const std::vector<C2Param*> &params,
std::vector<std::unique_ptr<C2SettingResult>> *const failures) {
return mInterface.config(params, C2_MAY_BLOCK, failures);
}
void C2PlatformComponentStore::visitComponents() {
std::lock_guard<std::mutex> lock(mMutex);
if (mVisited) {
return;
}
for (auto &pathAndLoader : mComponents) {
const C2String &path = pathAndLoader.first;
ComponentLoader &loader = pathAndLoader.second;
std::shared_ptr<ComponentModule> module;
if (loader.fetchModule(&module) == C2_OK) {
std::shared_ptr<const C2Component::Traits> traits = module->getTraits();
if (traits) {
mComponentList.push_back(traits);
mComponentNameToPath.emplace(traits->name, path);
for (const C2String &alias : traits->aliases) {
mComponentNameToPath.emplace(alias, path);
}
}
}
}
mVisited = true;
}
std::vector<std::shared_ptr<const C2Component::Traits>> C2PlatformComponentStore::listComponents() {
// This method SHALL return within 500ms.
visitComponents();
return mComponentList;
}
c2_status_t C2PlatformComponentStore::findComponent(
C2String name, std::shared_ptr<ComponentModule> *module) {
(*module).reset();
visitComponents();
auto pos = mComponentNameToPath.find(name);
if (pos != mComponentNameToPath.end()) {
return mComponents.at(pos->second).fetchModule(module);
}
return C2_NOT_FOUND;
}
c2_status_t C2PlatformComponentStore::createComponent(
C2String name, std::shared_ptr<C2Component> *const component) {
// This method SHALL return within 100ms.
component->reset();
std::shared_ptr<ComponentModule> module;
c2_status_t res = findComponent(name, &module);
if (res == C2_OK) {
// TODO: get a unique node ID
res = module->createComponent(0, component);
}
return res;
}
c2_status_t C2PlatformComponentStore::createInterface(
C2String name, std::shared_ptr<C2ComponentInterface> *const interface) {
// This method SHALL return within 100ms.
interface->reset();
std::shared_ptr<ComponentModule> module;
c2_status_t res = findComponent(name, &module);
if (res == C2_OK) {
// TODO: get a unique node ID
res = module->createInterface(0, interface);
}
return res;
}
c2_status_t C2PlatformComponentStore::querySupportedParams_nb(
std::vector<std::shared_ptr<C2ParamDescriptor>> *const params) const {
return mInterface.querySupportedParams(params);
}
c2_status_t C2PlatformComponentStore::querySupportedValues_sm(
std::vector<C2FieldSupportedValuesQuery> &fields) const {
return mInterface.querySupportedValues(fields, C2_MAY_BLOCK);
}
C2String C2PlatformComponentStore::getName() const {
return "android.componentStore.platform";
}
std::shared_ptr<C2ParamReflector> C2PlatformComponentStore::getParamReflector() const {
return mReflector;
}
std::shared_ptr<C2ComponentStore> GetCodec2PlatformComponentStore() {
static std::mutex mutex;
static std::weak_ptr<C2ComponentStore> platformStore;
std::lock_guard<std::mutex> lock(mutex);
std::shared_ptr<C2ComponentStore> store = platformStore.lock();
if (store == nullptr) {
store = std::make_shared<C2PlatformComponentStore>();
platformStore = store;
}
return store;
}
// For testing only
std::shared_ptr<C2ComponentStore> GetTestComponentStore(
std::vector<std::tuple<C2String,
C2ComponentFactory::CreateCodec2FactoryFunc,
C2ComponentFactory::DestroyCodec2FactoryFunc>> funcs) {
return std::shared_ptr<C2ComponentStore>(new C2PlatformComponentStore(funcs));
}
} // namespace android