Merge "media.bufferpool@1.0 implementation"

gugelfrei
TreeHugger Robot 6 years ago committed by Android (Google) Code Review
commit 29cf87f9a7

@ -1,6 +1,9 @@
cc_library_shared {
name: "libstagefright_codec2",
vendor_available: true,
vndk: {
enabled: true,
},
tags: [
"optional",

@ -54,3 +54,7 @@ cc_library_shared {
"-std=c++14",
],
}
subdirs = [
"bufferpool",
]

@ -0,0 +1,100 @@
/*
* Copyright (C) 2018 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.
*/
#include "Accessor.h"
#include "AccessorImpl.h"
#include "Connection.h"
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
// Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
Return<void> Accessor::connect(connect_cb _hidl_cb) {
sp<Connection> connection;
ConnectionId connectionId;
const QueueDescriptor* fmqDesc;
ResultStatus status = connect(&connection, &connectionId, &fmqDesc);
if (status == ResultStatus::OK) {
_hidl_cb(status, connection, connectionId, *fmqDesc);
} else {
_hidl_cb(status, nullptr, -1LL,
android::hardware::MQDescriptorSync<BufferStatusMessage>(
std::vector<android::hardware::GrantorDescriptor>(),
nullptr /* nhandle */, 0 /* size */));
}
return Void();
}
Accessor::Accessor(const std::shared_ptr<C2Allocator> &allocator, bool linear)
: mImpl(new Impl(allocator, linear)) {}
Accessor::~Accessor() {
}
bool Accessor::isValid() {
return (bool)mImpl;
}
ResultStatus Accessor::allocate(
ConnectionId connectionId,
const std::vector<uint8_t> &params,
BufferId *bufferId, const native_handle_t** handle) {
if (mImpl) {
return mImpl->allocate(connectionId, params, bufferId, handle);
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus Accessor::fetch(
ConnectionId connectionId, TransactionId transactionId,
BufferId bufferId, const native_handle_t** handle) {
if (mImpl) {
return mImpl->fetch(connectionId, transactionId, bufferId, handle);
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus Accessor::connect(
sp<Connection> *connection, ConnectionId *pConnectionId,
const QueueDescriptor** fmqDescPtr) {
if (mImpl) {
return mImpl->connect(this, connection, pConnectionId, fmqDescPtr);
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus Accessor::close(ConnectionId connectionId) {
if (mImpl) {
return mImpl->close(connectionId);
}
return ResultStatus::CRITICAL_ERROR;
}
//IAccessor* HIDL_FETCH_IAccessor(const char* /* name */) {
// return new Accessor();
//}
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android

@ -0,0 +1,144 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H
#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <C2Buffer.h>
#include <BufferPoolTypes.h>
#include "BufferStatus.h"
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
struct Connection;
/**
* A buffer pool accessor which enables a buffer pool to communicate with buffer
* pool clients. 1:1 correspondense holds between a buffer pool and an accessor.
*/
struct Accessor : public IAccessor {
// Methods from ::android::hardware::media::bufferpool::V1_0::IAccessor follow.
Return<void> connect(connect_cb _hidl_cb) override;
/**
* Creates a buffer pool accessor which uses the specified allocator.
*
* @param allocator buffer allocator.
* @param linear whether the allocator is linear or not.
*/
Accessor(const std::shared_ptr<C2Allocator> &allocator, bool linear);
/** Destructs a buffer pool accessor. */
~Accessor();
/** Returns whether the accessor is valid. */
bool isValid();
/** Allocates a buffer form a buffer pool.
*
* @param connectionId the connection id of the client.
* @param params the allocation parameters.
* @param bufferId the id of the allocated buffer.
* @param handle the native handle of the allocated buffer.
*
* @return OK when a buffer is successfully allocated.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus allocate(
ConnectionId connectionId,
const std::vector<uint8_t>& params,
BufferId *bufferId,
const native_handle_t** handle);
/**
* Fetches a buffer for the specified transaction.
*
* @param connectionId the id of receiving connection(client).
* @param transactionId the id of the transfer transaction.
* @param bufferId the id of the buffer to be fetched.
* @param handle the native handle of the fetched buffer.
*
* @return OK when a buffer is successfully fetched.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus fetch(
ConnectionId connectionId,
TransactionId transactionId,
BufferId bufferId,
const native_handle_t** handle);
/**
* Makes a connection to the buffer pool. The buffer pool client uses the
* created connection in order to communicate with the buffer pool. An
* FMQ for buffer status message is also created for the client.
*
* @param connection created connection
* @param pConnectionId the id of the created connection
* @param fmqDescPtr FMQ descriptor for shared buffer status message
* queue between a buffer pool and the client.
*
* @return OK when a connection is successfully made.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus connect(
sp<Connection> *connection, ConnectionId *pConnectionId,
const QueueDescriptor** fmqDescPtr);
/**
* Closes the specified connection to the client.
*
* @param connectionId the id of the connection.
*
* @return OK when the connection is closed.
* CRITICAL_ERROR otherwise.
*/
ResultStatus close(ConnectionId connectionId);
private:
class Impl;
std::unique_ptr<Impl> mImpl;
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" IAccessor* HIDL_FETCH_IAccessor(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSOR_H

@ -0,0 +1,542 @@
/*
* Copyright (C) 2018 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 "bufferpool"
//#define LOG_NDEBUG 0
#include <inttypes.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <utils/Log.h>
#include <C2Buffer.h>
#include "AccessorImpl.h"
#include "Connection.h"
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
// Buffer structure in bufferpool process
struct InternalBuffer {
BufferId mId;
size_t mOwnerCount;
size_t mTransactionCount;
const bool mLinear;
const std::shared_ptr<C2LinearAllocation> mLinearAllocation;
const std::shared_ptr<C2GraphicAllocation> mGraphicAllocation;
const std::vector<uint8_t> mConfig;
InternalBuffer(
BufferId id,
const std::shared_ptr<C2LinearAllocation> &alloc,
const std::vector<uint8_t> &allocConfig)
: mId(id), mOwnerCount(0), mTransactionCount(0),
mLinear(true), mLinearAllocation(alloc),
mGraphicAllocation(nullptr),
mConfig(allocConfig) {}
InternalBuffer(
BufferId id,
const std::shared_ptr<C2GraphicAllocation> &alloc,
const std::vector<uint8_t> &allocConfig)
: mId(id), mOwnerCount(0), mTransactionCount(0),
mLinear(false), mLinearAllocation(nullptr),
mGraphicAllocation(alloc),
mConfig(allocConfig) {}
const native_handle_t *handle() {
if (mLinear) {
return mLinearAllocation->handle();
} else {
return mGraphicAllocation->handle();
}
}
// TODO : support non exact matching. e.g) capacity
bool isRecyclable(const std::vector<uint8_t> &config) {
if (mConfig.size() == config.size()) {
for (size_t i = 0; i < config.size(); ++i) {
if (mConfig[i] != config[i]) {
return false;
}
}
return true;
}
return false;
}
};
struct TransactionStatus {
TransactionId mId;
BufferId mBufferId;
ConnectionId mSender;
ConnectionId mReceiver;
BufferStatus mStatus;
int64_t mTimestampUs;
bool mSenderValidated;
TransactionStatus(const BufferStatusMessage &message, int64_t timestampUs) {
mId = message.transactionId;
mBufferId = message.bufferId;
mStatus = message.newStatus;
mTimestampUs = timestampUs;
if (mStatus == BufferStatus::TRANSFER_TO) {
mSender = message.connectionId;
mReceiver = message.targetConnectionId;
mSenderValidated = true;
} else {
mSender = -1LL;
mReceiver = message.connectionId;
mSenderValidated = false;
}
}
};
// Helper template methods for handling map of set.
template<class T, class U>
bool insert(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
auto iter = mapOfSet->find(key);
if (iter == mapOfSet->end()) {
std::set<U> valueSet{value};
mapOfSet->insert(std::make_pair(key, valueSet));
return true;
} else if (iter->second.find(value) == iter->second.end()) {
iter->second.insert(value);
return true;
}
return false;
}
template<class T, class U>
bool erase(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
bool ret = false;
auto iter = mapOfSet->find(key);
if (iter != mapOfSet->end()) {
if (iter->second.erase(value) > 0) {
ret = true;
}
if (iter->second.size() == 0) {
mapOfSet->erase(iter);
}
}
return ret;
}
template<class T, class U>
bool contains(std::map<T, std::set<U>> *mapOfSet, T key, U value) {
auto iter = mapOfSet->find(key);
if (iter != mapOfSet->end()) {
auto setIter = iter->second.find(value);
return setIter != iter->second.end();
}
return false;
}
int32_t Accessor::Impl::sPid = getpid();
uint32_t Accessor::Impl::sSeqId = time(NULL);
Accessor::Impl::Impl(
const std::shared_ptr<C2Allocator> &allocator, bool linear)
: mAllocator(allocator), mLinear(linear) {}
Accessor::Impl::~Impl() {
}
ResultStatus Accessor::Impl::connect(
const sp<Accessor> &accessor, sp<Connection> *connection,
ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr) {
sp<Connection> newConnection = new Connection();
ResultStatus status = ResultStatus::CRITICAL_ERROR;
if (newConnection) {
std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
ConnectionId id = (int64_t)sPid << 32 | sSeqId;
status = mBufferPool.mObserver.open(id, fmqDescPtr);
if (status == ResultStatus::OK) {
newConnection->initialize(accessor, id);
*connection = newConnection;
*pConnectionId = id;
++sSeqId;
}
}
return status;
}
ResultStatus Accessor::Impl::close(ConnectionId connectionId) {
std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
mBufferPool.processStatusMessages();
mBufferPool.handleClose(connectionId);
mBufferPool.mObserver.close(connectionId);
return ResultStatus::OK;
}
ResultStatus Accessor::Impl::allocate(
ConnectionId connectionId, const std::vector<uint8_t>& params,
BufferId *bufferId, const native_handle_t** handle) {
std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
mBufferPool.processStatusMessages();
ResultStatus status = ResultStatus::OK;
if (!mBufferPool.getFreeBuffer(params, bufferId, handle)) {
if (mLinear) {
status = mBufferPool.getNewLinearBuffer(
mAllocator, params, bufferId, handle);
} else {
status = mBufferPool.getNewGraphicBuffer(
mAllocator, params, bufferId, handle);
}
ALOGV("create a buffer %d : %u %p",
status == ResultStatus::OK, *bufferId, *handle);
}
if (status == ResultStatus::OK) {
// TODO: handle ownBuffer failure
mBufferPool.handleOwnBuffer(connectionId, *bufferId);
}
return status;
}
ResultStatus Accessor::Impl::fetch(
ConnectionId connectionId, TransactionId transactionId,
BufferId bufferId, const native_handle_t** handle) {
std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
mBufferPool.processStatusMessages();
auto found = mBufferPool.mTransactions.find(transactionId);
if (found != mBufferPool.mTransactions.end() &&
contains(&mBufferPool.mPendingTransactions,
connectionId, transactionId)) {
if (found->second->mSenderValidated &&
found->second->mStatus == BufferStatus::TRANSFER_FROM &&
found->second->mBufferId == bufferId) {
found->second->mStatus = BufferStatus::TRANSFER_FETCH;
auto bufferIt = mBufferPool.mBuffers.find(bufferId);
if (bufferIt != mBufferPool.mBuffers.end()) {
*handle = bufferIt->second->handle();
return ResultStatus::OK;
}
}
}
return ResultStatus::CRITICAL_ERROR;
}
void Accessor::Impl::sync() {
// TODO: periodic jobs
// transaction timeout, buffer cacheing TTL handling
std::lock_guard<std::mutex> lock(mBufferPool.mMutex);
mBufferPool.processStatusMessages();
}
Accessor::Impl::Impl::BufferPool::BufferPool()
: mTimestampUs(getTimestampNow()), mSeq(0) {}
bool Accessor::Impl::BufferPool::handleOwnBuffer(
ConnectionId connectionId, BufferId bufferId) {
bool added = insert(&mUsingBuffers, connectionId, bufferId);
if (added) {
auto iter = mBuffers.find(bufferId);
iter->second->mOwnerCount++;
}
insert(&mUsingConnections, bufferId, connectionId);
return added;
}
bool Accessor::Impl::BufferPool::handleReleaseBuffer(
ConnectionId connectionId, BufferId bufferId) {
bool deleted = erase(&mUsingBuffers, connectionId, bufferId);
if (deleted) {
auto iter = mBuffers.find(bufferId);
iter->second->mOwnerCount--;
if (iter->second->mOwnerCount == 0 &&
iter->second->mTransactionCount == 0) {
mFreeBuffers.insert(bufferId);
}
}
erase(&mUsingConnections, bufferId, connectionId);
ALOGV("release buffer %u : %d", bufferId, deleted);
return deleted;
}
bool Accessor::Impl::BufferPool::handleTransferTo(const BufferStatusMessage &message) {
auto completed = mCompletedTransactions.find(
message.transactionId);
if (completed != mCompletedTransactions.end()) {
// already completed
mCompletedTransactions.erase(completed);
return true;
}
// the buffer should exist and be owned.
auto bufferIter = mBuffers.find(message.bufferId);
if (bufferIter == mBuffers.end() ||
!contains(&mUsingBuffers, message.connectionId, message.bufferId)) {
return false;
}
auto found = mTransactions.find(message.transactionId);
if (found != mTransactions.end()) {
// transfer_from was received earlier.
found->second->mSender = message.connectionId;
found->second->mSenderValidated = true;
return true;
}
// TODO: verify there is target connection Id
mTransactions.insert(std::make_pair(
message.transactionId,
std::make_unique<TransactionStatus>(message, mTimestampUs)));
insert(&mPendingTransactions, message.targetConnectionId,
message.transactionId);
bufferIter->second->mTransactionCount++;
return true;
}
bool Accessor::Impl::BufferPool::handleTransferFrom(const BufferStatusMessage &message) {
auto found = mTransactions.find(message.transactionId);
if (found == mTransactions.end()) {
// TODO: is it feasible to check ownership here?
mTransactions.insert(std::make_pair(
message.transactionId,
std::make_unique<TransactionStatus>(message, mTimestampUs)));
insert(&mPendingTransactions, message.connectionId,
message.transactionId);
auto bufferIter = mBuffers.find(message.bufferId);
bufferIter->second->mTransactionCount++;
} else {
if (message.connectionId == found->second->mReceiver) {
found->second->mStatus = BufferStatus::TRANSFER_FROM;
}
}
return true;
}
bool Accessor::Impl::BufferPool::handleTransferResult(const BufferStatusMessage &message) {
auto found = mTransactions.find(message.transactionId);
if (found != mTransactions.end()) {
bool deleted = erase(&mPendingTransactions, message.connectionId,
message.transactionId);
if (deleted) {
if (!found->second->mSenderValidated) {
mCompletedTransactions.insert(message.transactionId);
}
auto bufferIter = mBuffers.find(message.bufferId);
if (message.newStatus == BufferStatus::TRANSFER_OK) {
handleOwnBuffer(message.connectionId, message.bufferId);
}
bufferIter->second->mTransactionCount--;
if (bufferIter->second->mOwnerCount == 0
&& bufferIter->second->mTransactionCount == 0) {
mFreeBuffers.insert(message.bufferId);
}
}
ALOGV("transfer finished %" PRIu64 " %u - %d", message.transactionId,
message.bufferId, deleted);
return deleted;
}
ALOGV("transfer not found %" PRIu64 " %u", message.transactionId,
message.bufferId);
return false;
}
void Accessor::Impl::BufferPool::processStatusMessages() {
std::vector<BufferStatusMessage> messages;
mObserver.getBufferStatusChanges(messages);
mTimestampUs = getTimestampNow();
for (BufferStatusMessage& message: messages) {
bool ret = false;
switch (message.newStatus) {
case BufferStatus::NOT_USED:
ret = handleReleaseBuffer(
message.connectionId, message.bufferId);
break;
case BufferStatus::USED:
// not happening
break;
case BufferStatus::TRANSFER_TO:
ret = handleTransferTo(message);
break;
case BufferStatus::TRANSFER_FROM:
ret = handleTransferFrom(message);
break;
case BufferStatus::TRANSFER_TIMEOUT:
// TODO
break;
case BufferStatus::TRANSFER_LOST:
// TODO
break;
case BufferStatus::TRANSFER_FETCH:
// not happening
break;
case BufferStatus::TRANSFER_OK:
case BufferStatus::TRANSFER_ERROR:
ret = handleTransferResult(message);
break;
}
if (ret == false) {
ALOGW("buffer status message processing failure - message : %d "
"connection : %" PRId64,
message.newStatus, message.connectionId);
}
}
messages.clear();
}
bool Accessor::Impl::BufferPool::handleClose(ConnectionId connectionId) {
// Cleaning buffers
auto buffers = mUsingBuffers.find(connectionId);
if (buffers != mUsingBuffers.end()) {
for (const BufferId& bufferId : buffers->second) {
bool deleted = erase(&mUsingConnections, bufferId, connectionId);
if (deleted) {
auto bufferIter = mBuffers.find(bufferId);
bufferIter->second->mOwnerCount--;
if (bufferIter->second->mOwnerCount == 0 &&
bufferIter->second->mTransactionCount == 0) {
// TODO: handle freebuffer insert fail
mFreeBuffers.insert(bufferId);
}
}
}
mUsingBuffers.erase(buffers);
}
// Cleaning transactions
auto pending = mPendingTransactions.find(connectionId);
if (pending != mPendingTransactions.end()) {
for (const TransactionId& transactionId : pending->second) {
auto iter = mTransactions.find(transactionId);
if (iter != mTransactions.end()) {
if (!iter->second->mSenderValidated) {
mCompletedTransactions.insert(transactionId);
}
BufferId bufferId = iter->second->mBufferId;
auto bufferIter = mBuffers.find(bufferId);
bufferIter->second->mTransactionCount--;
if (bufferIter->second->mOwnerCount == 0 &&
bufferIter->second->mTransactionCount == 0) {
// TODO: handle freebuffer insert fail
mFreeBuffers.insert(bufferId);
}
mTransactions.erase(iter);
}
}
}
return true;
}
bool Accessor::Impl::BufferPool::getFreeBuffer(
const std::vector<uint8_t> &params, BufferId *pId,
const native_handle_t** handle) {
auto bufferIt = mFreeBuffers.begin();
for (;bufferIt != mFreeBuffers.end(); ++bufferIt) {
BufferId bufferId = *bufferIt;
if (mBuffers[bufferId]->isRecyclable(params)) {
break;
}
}
if (bufferIt != mFreeBuffers.end()) {
BufferId id = *bufferIt;
mFreeBuffers.erase(bufferIt);
*handle = mBuffers[id]->handle();
*pId = id;
ALOGV("recycle a buffer %u %p", id, *handle);
return true;
}
return false;
}
ResultStatus Accessor::Impl::BufferPool::getNewLinearBuffer(
const std::shared_ptr<C2Allocator> &allocator,
const std::vector<uint8_t> &params, BufferId *pId,
const native_handle_t** handle) {
union LinearParam {
struct {
uint32_t capacity;
C2MemoryUsage usage;
} data;
uint8_t array[0];
LinearParam() : data{0, {0, 0}} {}
} linearParam;
memcpy(&linearParam, params.data(),
std::min(sizeof(linearParam), params.size()));
std::shared_ptr<C2LinearAllocation> linearAlloc;
c2_status_t status = allocator->newLinearAllocation(
linearParam.data.capacity, linearParam.data.usage, &linearAlloc);
if (status == C2_OK) {
BufferId bufferId = mSeq++;
std::unique_ptr<InternalBuffer> buffer =
std::make_unique<InternalBuffer>(
bufferId, linearAlloc, params);
if (buffer) {
auto res = mBuffers.insert(std::make_pair(
bufferId, std::move(buffer)));
if (res.second) {
*handle = linearAlloc->handle();
*pId = bufferId;
return ResultStatus::OK;
}
}
return ResultStatus::NO_MEMORY;
}
// TODO: map C2 error code
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus Accessor::Impl::BufferPool::getNewGraphicBuffer(
const std::shared_ptr<C2Allocator> &allocator,
const std::vector<uint8_t> &params, BufferId *pId,
const native_handle_t** handle) {
union GraphicParam {
struct {
uint32_t width;
uint32_t height;
uint32_t format;
C2MemoryUsage usage;
} data;
uint8_t array[0];
GraphicParam() : data{0, 0, 0, {0, 0}} {}
} graphicParam;
memcpy(&graphicParam, params.data(),
std::min(sizeof(graphicParam), params.size()));
std::shared_ptr<C2GraphicAllocation> graphicAlloc;
c2_status_t status = allocator->newGraphicAllocation(
graphicParam.data.width, graphicParam.data.height,
graphicParam.data.format, graphicParam.data.usage, &graphicAlloc);
if (status == C2_OK) {
BufferId bufferId = mSeq;
std::unique_ptr<InternalBuffer> buffer =
std::make_unique<InternalBuffer>(
bufferId, graphicAlloc, params);
if (buffer) {
auto res = mBuffers.insert(std::make_pair(
bufferId, std::move(buffer)));
if (res.second) {
*handle = graphicAlloc->handle();
*pId = bufferId;
return ResultStatus::OK;
}
}
return ResultStatus::NO_MEMORY;
}
// TODO: map C2 error code
return ResultStatus::CRITICAL_ERROR;
}
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android

@ -0,0 +1,230 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H
#include <map>
#include <set>
#include "Accessor.h"
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
struct InternalBuffer;
struct TransactionStatus;
/**
* An implementation of a buffer pool accessor(or a buffer pool implementation.) */
class Accessor::Impl {
public:
Impl(const std::shared_ptr<C2Allocator> &allocator, bool linear);
~Impl();
ResultStatus connect(
const sp<Accessor> &accessor, sp<Connection> *connection,
ConnectionId *pConnectionId, const QueueDescriptor** fmqDescPtr);
ResultStatus close(ConnectionId connectionId);
ResultStatus allocate(ConnectionId connectionId,
const std::vector<uint8_t>& params,
BufferId *bufferId,
const native_handle_t** handle);
ResultStatus fetch(ConnectionId connectionId,
TransactionId transactionId,
BufferId bufferId,
const native_handle_t** handle);
/** Processes pending buffer status messages */
void sync();
private:
// ConnectionId = pid : (timestamp_created + seqId)
// in order to guarantee uniqueness for each connection
static uint32_t sSeqId;
static int32_t sPid;
const std::shared_ptr<C2Allocator> mAllocator;
bool mLinear;
/**
* Buffer pool implementation.
*
* Handles buffer status messages. Handles buffer allocation/recycling.
* Handles buffer transfer between buffer pool clients.
*/
struct BufferPool {
private:
std::mutex mMutex;
int64_t mTimestampUs;
BufferId mSeq;
BufferStatusObserver mObserver;
std::map<ConnectionId, std::set<BufferId>> mUsingBuffers;
std::map<BufferId, std::set<ConnectionId>> mUsingConnections;
std::map<ConnectionId, std::set<TransactionId>> mPendingTransactions;
// Transactions completed before TRANSFER_TO message arrival.
// Fetch does not occur for the transactions.
// Only transaction id is kept for the transactions in short duration.
std::set<TransactionId> mCompletedTransactions;
// Currently active(pending) transations' status & information.
std::map<TransactionId, std::unique_ptr<TransactionStatus>>
mTransactions;
std::map<BufferId, std::unique_ptr<InternalBuffer>> mBuffers;
std::set<BufferId> mFreeBuffers;
public:
/** Creates a buffer pool. */
BufferPool();
/**
* Processes all pending buffer status messages, and returns the result.
* Each status message is handled by methods with 'handle' prefix.
*/
void processStatusMessages();
/**
* Handles a buffer being owned by a connection.
*
* @param connectionId the id of the buffer owning connection.
* @param bufferId the id of the buffer.
*
* @return {@code true} when the buffer is owned,
* {@code false} otherwise.
*/
bool handleOwnBuffer(ConnectionId connectionId, BufferId bufferId);
/**
* Handles a buffer being released by a connection.
*
* @param connectionId the id of the buffer owning connection.
* @param bufferId the id of the buffer.
*
* @return {@code true} when the buffer ownership is released,
* {@code false} otherwise.
*/
bool handleReleaseBuffer(ConnectionId connectionId, BufferId bufferId);
/**
* Handles a transfer transaction start message from the sender.
*
* @param message a buffer status message for the transaction.
*
* @result {@code true} when transfer_to message is acknowledged,
* {@code false} otherwise.
*/
bool handleTransferTo(const BufferStatusMessage &message);
/**
* Handles a transfer transaction being acked by the receiver.
*
* @param message a buffer status message for the transaction.
*
* @result {@code true} when transfer_from message is acknowledged,
* {@code false} otherwise.
*/
bool handleTransferFrom(const BufferStatusMessage &message);
/**
* Handles a transfer transaction result message from the receiver.
*
* @param message a buffer status message for the transaction.
*
* @result {@code true} when the exisitng transaction is finished,
* {@code false} otherwise.
*/
bool handleTransferResult(const BufferStatusMessage &message);
/**
* Handles a connection being closed, and returns the result. All the
* buffers and transactions owned by the connection will be cleaned up.
* The related FMQ will be cleaned up too.
*
* @param connectionId the id of the connection.
*
* @result {@code true} when the connection existed,
* {@code false} otherwise.
*/
bool handleClose(ConnectionId connectionId);
/**
* Recycles a existing free buffer if it is possible.
*
* @param params the allocation parameters.
* @param pId the id of the recycled buffer.
* @param handle the native handle of the recycled buffer.
*
* @return {@code true} when a buffer is recycled, {@code false}
* otherwise.
*/
bool getFreeBuffer(const std::vector<uint8_t> &params, BufferId *pId,
const native_handle_t **handle);
/**
* Creates a new linear buffer.
*
* @param allocator the linear buffer allocator
* @param params the allocator parameters
* @param pId the buffer id for the newly allocated buffer.
* @param handle the native handle for the newly allocated buffer.
*
* @return OK when a linear allocation is successfully allocated.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus getNewLinearBuffer(
const std::shared_ptr<C2Allocator> &allocator,
const std::vector<uint8_t> &params, BufferId *pId,
const native_handle_t **handle);
/**
* Creates a new graphic buffer.
*
* @param allocator the graphic buffer allocator
* @param params the allocator parameters
* @param pId the buffer id for the newly allocated buffer.
* @param handle the native handle for the newly allocated buffer.
*
* @return OK when a graphic allocation is successfully allocated.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus getNewGraphicBuffer(
const std::shared_ptr<C2Allocator> &allocator,
const std::vector<uint8_t> &params, BufferId *pId,
const native_handle_t **handle);
friend class Accessor::Impl;
} mBufferPool;
};
} // namespace implementation
} // namespace V1_0
} // namespace ufferpool
} // namespace media
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_ACCESSORIMPL_H

@ -0,0 +1,31 @@
cc_library_shared {
name: "android.hardware.media.bufferpool@1.0-impl",
vendor_available: true,
vndk: {
enabled: true,
},
srcs: [
"Accessor.cpp",
"AccessorImpl.cpp",
"BufferPoolClient.cpp",
"BufferStatus.cpp",
"ClientManager.cpp",
"Connection.cpp",
],
export_include_dirs: [
"include",
],
include_dirs: [
"frameworks/av/media/libstagefright/codec2/vndk/bufferpool/include",
],
shared_libs: [
"libcutils",
"libfmq",
"libhidlbase",
"libhidltransport",
"liblog",
"libstagefright_codec2",
"libutils",
"android.hardware.media.bufferpool@1.0",
],
}

@ -0,0 +1,566 @@
/*
* Copyright (C) 2018 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 "bufferpool"
//#define LOG_NDEBUG 0
#include <inttypes.h>
#include <thread>
#include <utils/Log.h>
#include "BufferPoolClient.h"
#include "Connection.h"
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
static constexpr int64_t kReceiveTimeoutUs = 5000; // 5ms
static constexpr int kPostMaxRetry = 3;
static constexpr int kCacheTtlUs = 500000; // TODO: tune
class BufferPoolClient::Impl
: public std::enable_shared_from_this<BufferPoolClient::Impl> {
public:
explicit Impl(const sp<Accessor> &accessor);
explicit Impl(const sp<IAccessor> &accessor);
bool isValid() {
return mValid;
}
ConnectionId getConnectionId() {
return mConnectionId;
}
sp<IAccessor> &getAccessor() {
return mAccessor;
}
ResultStatus allocate(const std::vector<uint8_t> &params,
std::shared_ptr<_C2BlockPoolData> *buffer);
ResultStatus receive(
TransactionId transactionId, BufferId bufferId,
int64_t timestampUs, std::shared_ptr<_C2BlockPoolData> *buffer);
void postBufferRelease(BufferId bufferId);
bool postSend(
BufferId bufferId, ConnectionId receiver,
TransactionId *transactionId, int64_t *timestampUs);
private:
bool postReceive(
BufferId bufferId, TransactionId transactionId,
int64_t timestampUs);
bool postReceiveResult(
BufferId bufferId, TransactionId transactionId, bool result);
bool syncReleased();
ResultStatus allocateBufferHandle(
const std::vector<uint8_t>& params, BufferId *bufferId,
native_handle_t **handle);
ResultStatus fetchBufferHandle(
TransactionId transactionId, BufferId bufferId,
native_handle_t **handle);
struct BlockPoolDataDtor;
struct ClientBuffer;
bool mLocal;
bool mValid;
sp<IAccessor> mAccessor;
sp<Connection> mLocalConnection;
sp<IConnection> mRemoteConnection;
uint32_t mSeqId;
ConnectionId mConnectionId;
// CachedBuffers
struct {
std::mutex mLock;
bool creating;
std::condition_variable mCreateCv;
std::map<BufferId, std::unique_ptr<ClientBuffer>> mBuffers;
} mCache;
// FMQ - release notifier
struct {
std::mutex mLock;
// TODO: use only one list?(using one list may dealy sending messages?)
std::list<BufferId> mReleasingIds;
std::list<BufferId> mReleasedIds;
std::unique_ptr<BufferStatusChannel> mStatusChannel;
} mReleasing;
};
struct BufferPoolClient::Impl::BlockPoolDataDtor {
BlockPoolDataDtor(const std::shared_ptr<BufferPoolClient::Impl> &impl)
: mImpl(impl) {}
void operator()(_C2BlockPoolData *buffer) {
BufferId id = buffer->mId;
delete buffer;
auto impl = mImpl.lock();
if (impl && impl->isValid()) {
impl->postBufferRelease(id);
}
}
const std::weak_ptr<BufferPoolClient::Impl> mImpl;
};
struct BufferPoolClient::Impl::ClientBuffer {
private:
bool mInvalidated; // TODO: implement
int64_t mExpireUs;
bool mHasCache;
_C2BlockPoolData mBuffer;
std::weak_ptr<_C2BlockPoolData> mCache;
void updateExpire() {
mExpireUs = getTimestampNow() + kCacheTtlUs;
}
public:
ClientBuffer(BufferId id, native_handle_t *handle)
: mInvalidated(false), mHasCache(false), mBuffer(id, handle) {
(void)mInvalidated;
mExpireUs = getTimestampNow() + kCacheTtlUs;
}
bool expire() const {
int64_t now = getTimestampNow();
return now >= mExpireUs;
}
bool hasCache() const {
return mHasCache;
}
std::shared_ptr<_C2BlockPoolData> fetchCache() {
if (mHasCache) {
std::shared_ptr<_C2BlockPoolData> cache = mCache.lock();
if (cache) {
updateExpire();
}
return cache;
}
return nullptr;
}
std::shared_ptr<_C2BlockPoolData> createCache(
const std::shared_ptr<BufferPoolClient::Impl> &impl) {
if (!mHasCache) {
// Allocates a raw ptr in order to avoid sending #postBufferRelease
// from deleter, in case of native_handle_clone failure.
_C2BlockPoolData *ptr = new _C2BlockPoolData(
mBuffer.mId, native_handle_clone(mBuffer.mHandle));
if (ptr && ptr->mHandle != NULL) {
std::shared_ptr<_C2BlockPoolData>
cache(ptr, BlockPoolDataDtor(impl));
if (cache) {
mCache = cache;
mHasCache = true;
updateExpire();
return cache;
}
}
if (ptr) {
delete ptr;
}
}
return nullptr;
}
bool onCacheRelease() {
if (mHasCache) {
// TODO: verify mCache is not valid;
mHasCache = false;
return true;
}
return false;
}
};
BufferPoolClient::Impl::Impl(const sp<Accessor> &accessor)
: mLocal(true), mAccessor(accessor), mSeqId(0) {
mValid = false;
const QueueDescriptor *fmqDesc;
ResultStatus status = accessor->connect(
&mLocalConnection, &mConnectionId, &fmqDesc);
if (status == ResultStatus::OK) {
mReleasing.mStatusChannel =
std::make_unique<BufferStatusChannel>(*fmqDesc);
mValid = mReleasing.mStatusChannel &&
mReleasing.mStatusChannel->isValid();
}
}
BufferPoolClient::Impl::Impl(const sp<IAccessor> &accessor)
: mLocal(false), mAccessor(accessor), mSeqId(0) {
mValid = false;
bool& valid = mValid;
sp<IConnection>& outConnection = mRemoteConnection;
ConnectionId& id = mConnectionId;
std::unique_ptr<BufferStatusChannel>& outChannel =
mReleasing.mStatusChannel;
accessor->connect(
[&valid, &outConnection, &id, &outChannel]
(ResultStatus status, sp<IConnection> connection,
ConnectionId connectionId, const QueueDescriptor& desc) {
if (status == ResultStatus::OK) {
outConnection = connection;
id = connectionId;
outChannel = std::make_unique<BufferStatusChannel>(desc);
if (outChannel && outChannel->isValid()) {
valid = true;
}
}
});
}
ResultStatus BufferPoolClient::Impl::allocate(
const std::vector<uint8_t> &params,
std::shared_ptr<_C2BlockPoolData> *buffer) {
if (!mLocal || !mLocalConnection || !mValid) {
return ResultStatus::CRITICAL_ERROR;
}
BufferId bufferId;
native_handle_t *handle = NULL;
buffer->reset();
ResultStatus status = allocateBufferHandle(params, &bufferId, &handle);
if (status == ResultStatus::OK) {
if (handle) {
std::unique_lock<std::mutex> lock(mCache.mLock);
syncReleased();
auto cacheIt = mCache.mBuffers.find(bufferId);
if (cacheIt != mCache.mBuffers.end()) {
// TODO: verify it is recycled. (not having active ref)
mCache.mBuffers.erase(cacheIt);
}
auto clientBuffer = std::make_unique<ClientBuffer>(
bufferId, handle);
if (clientBuffer) {
auto result = mCache.mBuffers.insert(std::make_pair(
bufferId, std::move(clientBuffer)));
if (result.second) {
*buffer = result.first->second->createCache(
shared_from_this());
}
}
}
if (!*buffer) {
ALOGV("client cache creation failure %d: %" PRId64,
handle != NULL, mConnectionId);
status = ResultStatus::NO_MEMORY;
postBufferRelease(bufferId);
}
}
return status;
}
ResultStatus BufferPoolClient::Impl::receive(
TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
std::shared_ptr<_C2BlockPoolData> *buffer) {
if (!mValid) {
return ResultStatus::CRITICAL_ERROR;
}
if (timestampUs != 0) {
timestampUs += kReceiveTimeoutUs;
}
if (!postReceive(bufferId, transactionId, timestampUs)) {
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus status = ResultStatus::CRITICAL_ERROR;
buffer->reset();
while(1) {
std::unique_lock<std::mutex> lock(mCache.mLock);
syncReleased();
auto cacheIt = mCache.mBuffers.find(bufferId);
if (cacheIt != mCache.mBuffers.end()) {
if (cacheIt->second->hasCache()) {
*buffer = cacheIt->second->fetchCache();
if (!*buffer) {
// check transfer time_out
lock.unlock();
std::this_thread::yield();
continue;
}
ALOGV("client receive from reference %" PRId64, mConnectionId);
break;
} else {
*buffer = cacheIt->second->createCache(shared_from_this());
ALOGV("client receive from cache %" PRId64, mConnectionId);
break;
}
} else {
if (!mCache.creating) {
mCache.creating = true;
lock.unlock();
native_handle_t* handle = NULL;
status = fetchBufferHandle(transactionId, bufferId, &handle);
lock.lock();
if (status == ResultStatus::OK) {
if (handle) {
auto clientBuffer = std::make_unique<ClientBuffer>(
bufferId, handle);
if (clientBuffer) {
auto result = mCache.mBuffers.insert(
std::make_pair(bufferId, std::move(
clientBuffer)));
if (result.second) {
*buffer = result.first->second->createCache(
shared_from_this());
}
}
}
if (!*buffer) {
status = ResultStatus::NO_MEMORY;
}
}
mCache.creating = false;
lock.unlock();
mCache.mCreateCv.notify_all();
break;
}
mCache.mCreateCv.wait(lock);
}
}
bool posted = postReceiveResult(bufferId, transactionId,
*buffer ? true : false);
ALOGV("client receive %" PRId64 " - %u : %s (%d)", mConnectionId, bufferId,
*buffer ? "ok" : "fail", posted);
if (*buffer) {
if (!posted) {
buffer->reset();
return ResultStatus::CRITICAL_ERROR;
}
return ResultStatus::OK;
}
return status;
}
void BufferPoolClient::Impl::postBufferRelease(BufferId bufferId) {
std::lock_guard<std::mutex> lock(mReleasing.mLock);
mReleasing.mReleasingIds.push_back(bufferId);
mReleasing.mStatusChannel->postBufferRelease(
mConnectionId, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
}
// TODO: revise ad-hoc posting data structure
bool BufferPoolClient::Impl::postSend(
BufferId bufferId, ConnectionId receiver,
TransactionId *transactionId, int64_t *timestampUs) {
std::lock_guard<std::mutex> lock(mReleasing.mLock);
*timestampUs = getTimestampNow();
*transactionId = (mConnectionId << 32) | mSeqId++;
// TODO: retry, add timeout, target?
return mReleasing.mStatusChannel->postBufferStatusMessage(
*transactionId, bufferId, BufferStatus::TRANSFER_TO, mConnectionId,
receiver, mReleasing.mReleasingIds, mReleasing.mReleasedIds);
}
bool BufferPoolClient::Impl::postReceive(
BufferId bufferId, TransactionId transactionId, int64_t timestampUs) {
for (int i = 0; i < kPostMaxRetry; ++i) {
std::unique_lock<std::mutex> lock(mReleasing.mLock);
int64_t now = getTimestampNow();
if (timestampUs == 0 || now < timestampUs) {
bool result = mReleasing.mStatusChannel->postBufferStatusMessage(
transactionId, bufferId, BufferStatus::TRANSFER_FROM,
mConnectionId, -1, mReleasing.mReleasingIds,
mReleasing.mReleasedIds);
if (result) {
return true;
}
lock.unlock();
std::this_thread::yield();
} else {
mReleasing.mStatusChannel->postBufferStatusMessage(
transactionId, bufferId, BufferStatus::TRANSFER_TIMEOUT,
mConnectionId, -1, mReleasing.mReleasingIds,
mReleasing.mReleasedIds);
return false;
}
}
return false;
}
bool BufferPoolClient::Impl::postReceiveResult(
BufferId bufferId, TransactionId transactionId, bool result) {
std::lock_guard<std::mutex> lock(mReleasing.mLock);
// TODO: retry, add timeout
return mReleasing.mStatusChannel->postBufferStatusMessage(
transactionId, bufferId,
result ? BufferStatus::TRANSFER_OK : BufferStatus::TRANSFER_ERROR,
mConnectionId, -1, mReleasing.mReleasingIds,
mReleasing.mReleasedIds);
}
// should have mCache.mLock
bool BufferPoolClient::Impl::syncReleased() {
std::lock_guard<std::mutex> lock(mReleasing.mLock);
if (mReleasing.mReleasingIds.size() > 0) {
mReleasing.mStatusChannel->postBufferRelease(
mConnectionId, mReleasing.mReleasingIds,
mReleasing.mReleasedIds);
}
if (mReleasing.mReleasedIds.size() > 0) {
for (BufferId& id: mReleasing.mReleasedIds) {
ALOGV("client release buffer %" PRId64 " - %u", mConnectionId, id);
auto found = mCache.mBuffers.find(id);
if (found != mCache.mBuffers.end()) {
if (!found->second->onCacheRelease()) {
// should not happen!
ALOGW("client %" PRId64 "cache release status inconsitent!",
mConnectionId);
}
if (found->second->expire()) {
ALOGV("client evict buffer from cache %" PRId64 " - %u",
mConnectionId, id);
mCache.mBuffers.erase(found);
}
} else {
// should not happen!
ALOGW("client %" PRId64 "cache status inconsitent!",
mConnectionId);
}
}
mReleasing.mReleasedIds.clear();
return true;
}
return false;
}
ResultStatus BufferPoolClient::Impl::allocateBufferHandle(
const std::vector<uint8_t>& params, BufferId *bufferId,
native_handle_t** handle) {
if (mLocalConnection) {
const native_handle_t* allocHandle = NULL;
ResultStatus status = mLocalConnection->allocate(
params, bufferId, &allocHandle);
if (status == ResultStatus::OK) {
*handle = native_handle_clone(allocHandle);
}
ALOGV("client allocate result %" PRId64 "%d : %u clone %p",
mConnectionId, status == ResultStatus::OK,
*handle ? *bufferId : 0 , *handle);
return status;
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus BufferPoolClient::Impl::fetchBufferHandle(
TransactionId transactionId, BufferId bufferId,
native_handle_t **handle) {
sp<IConnection> connection;
if (mLocal) {
connection = mLocalConnection;
} else {
connection = mRemoteConnection;
}
ResultStatus status;
connection->fetch(
transactionId, bufferId,
[&status, &handle]
(ResultStatus outStatus, Buffer outBuffer) {
status = outStatus;
if (status == ResultStatus::OK) {
*handle = native_handle_clone(
outBuffer.buffer.getNativeHandle());
}
});
return status;
}
BufferPoolClient::BufferPoolClient(const sp<Accessor> &accessor) {
mImpl = std::make_shared<Impl>(accessor);
}
BufferPoolClient::BufferPoolClient(const sp<IAccessor> &accessor) {
mImpl = std::make_shared<Impl>(accessor);
}
BufferPoolClient::~BufferPoolClient() {
// TODO: how to handle orphaned buffers?
}
bool BufferPoolClient::isValid() {
return mImpl && mImpl->isValid();
}
ConnectionId BufferPoolClient::getConnectionId() {
if (isValid()) {
return mImpl->getConnectionId();
}
return -1;
}
ResultStatus BufferPoolClient::getAccessor(sp<IAccessor> *accessor) {
if (isValid()) {
*accessor = mImpl->getAccessor();
return ResultStatus::OK;
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus BufferPoolClient::allocate(
const std::vector<uint8_t> &params,
std::shared_ptr<_C2BlockPoolData> *buffer) {
if (isValid()) {
return mImpl->allocate(params, buffer);
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus BufferPoolClient::receive(
TransactionId transactionId, BufferId bufferId, int64_t timestampUs,
std::shared_ptr<_C2BlockPoolData> *buffer) {
if (isValid()) {
return mImpl->receive(transactionId, bufferId, timestampUs, buffer);
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus BufferPoolClient::postSend(
ConnectionId receiverId,
const std::shared_ptr<_C2BlockPoolData> &buffer,
TransactionId *transactionId,
int64_t *timestampUs) {
if (isValid()) {
bool result = mImpl->postSend(
buffer->mId, receiverId, transactionId, timestampUs);
return result ? ResultStatus::OK : ResultStatus::CRITICAL_ERROR;
}
return ResultStatus::CRITICAL_ERROR;
}
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android

@ -0,0 +1,96 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H
#include <memory>
#include <cutils/native_handle.h>
#include <android/hardware/media/bufferpool/1.0/IAccessor.h>
#include <android/hardware/media/bufferpool/1.0/IConnection.h>
#include <BufferPoolTypes.h>
#include "Accessor.h"
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
using ::android::hardware::media::bufferpool::V1_0::IAccessor;
using ::android::hardware::media::bufferpool::V1_0::IConnection;
using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
using ::android::sp;
/**
* A buffer pool client for a buffer pool. For a specific buffer pool, at most
* one buffer pool client exists per process. This class will not be exposed
* outside. A buffer pool client will be used via ClientManager.
*/
class BufferPoolClient {
public:
/**
* Creates a buffer pool client from a local buffer pool
* (via ClientManager#create).
*/
explicit BufferPoolClient(const sp<Accessor> &accessor);
/**
* Creates a buffer pool client from a remote buffer pool
* (via ClientManager#registerSender).
* Note: A buffer pool client created with remote buffer pool cannot
* allocate a buffer.
*/
explicit BufferPoolClient(const sp<IAccessor> &accessor);
/** Destructs a buffer pool client. */
~BufferPoolClient();
private:
bool isValid();
ConnectionId getConnectionId();
ResultStatus getAccessor(sp<IAccessor> *accessor);
ResultStatus allocate(const std::vector<uint8_t> &params,
std::shared_ptr<_C2BlockPoolData> *buffer);
ResultStatus receive(TransactionId transactionId,
BufferId bufferId,
int64_t timestampUs,
std::shared_ptr<_C2BlockPoolData> *buffer);
ResultStatus postSend(ConnectionId receiver,
const std::shared_ptr<_C2BlockPoolData> &buffer,
TransactionId *transactionId,
int64_t *timestampUs);
class Impl;
std::shared_ptr<Impl> mImpl;
friend struct ClientManager;
};
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLCLIENT_H

@ -0,0 +1,184 @@
/*
* Copyright (C) 2018 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 "bufferpool"
//#define LOG_NDEBUG 0
#include <inttypes.h>
#include <time.h>
#include "BufferStatus.h"
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
int64_t getTimestampNow() {
int64_t stamp;
struct timespec ts;
// TODO: CLOCK_MONOTONIC_COARSE?
clock_gettime(CLOCK_MONOTONIC, &ts);
stamp = ts.tv_nsec / 1000;
stamp += (ts.tv_sec * 1000000LL);
return stamp;
}
static constexpr int kNumElementsInQueue = 1024*16;
ResultStatus BufferStatusObserver::open(
ConnectionId id, const QueueDescriptor** fmqDescPtr) {
if (mBufferStatusQueues.find(id) != mBufferStatusQueues.end()) {
// TODO: id collision log?
return ResultStatus::CRITICAL_ERROR;
}
std::unique_ptr<BufferStatusQueue> queue =
std::make_unique<BufferStatusQueue>(kNumElementsInQueue);
if (!queue || queue->isValid() == false) {
*fmqDescPtr = NULL;
return ResultStatus::NO_MEMORY;
} else {
*fmqDescPtr = queue->getDesc();
}
auto result = mBufferStatusQueues.insert(
std::make_pair(id, std::move(queue)));
if (!result.second) {
*fmqDescPtr = NULL;
return ResultStatus::NO_MEMORY;
}
return ResultStatus::OK;
}
ResultStatus BufferStatusObserver::close(ConnectionId id) {
if (mBufferStatusQueues.find(id) == mBufferStatusQueues.end()) {
return ResultStatus::CRITICAL_ERROR;
}
mBufferStatusQueues.erase(id);
return ResultStatus::OK;
}
void BufferStatusObserver::getBufferStatusChanges(std::vector<BufferStatusMessage> &messages) {
for (auto it = mBufferStatusQueues.begin(); it != mBufferStatusQueues.end(); ++it) {
BufferStatusMessage message;
size_t avail = it->second->availableToRead();
while (avail > 0) {
if (!it->second->read(&message, 1)) {
// Since avaliable # of reads are already confiremd,
// this should not happen.
// TODO: error handling (supurious client?)
ALOGW("FMQ message cannot be read from %" PRId64, it->first);
return;
}
message.connectionId = it->first;
messages.push_back(message);
--avail;
}
}
}
BufferStatusChannel::BufferStatusChannel(
const QueueDescriptor &fmqDesc) {
std::unique_ptr<BufferStatusQueue> queue =
std::make_unique<BufferStatusQueue>(fmqDesc);
if (!queue || queue->isValid() == false) {
mValid = false;
return;
}
mValid = true;
mBufferStatusQueue = std::move(queue);
}
bool BufferStatusChannel::isValid() {
return mValid;
}
void BufferStatusChannel::postBufferRelease(
ConnectionId connectionId,
std::list<BufferId> &pending, std::list<BufferId> &posted) {
if (mValid && pending.size() > 0) {
size_t avail = mBufferStatusQueue->availableToWrite();
avail = std::min(avail, pending.size());
BufferStatusMessage message;
for (size_t i = 0 ; i < avail; ++i) {
BufferId id = pending.front();
message.newStatus = BufferStatus::NOT_USED;
message.bufferId = id;
message.connectionId = connectionId;
if (!mBufferStatusQueue->write(&message, 1)) {
// Since avaliable # of writes are already confiremd,
// this should not happen.
// TODO: error handing?
ALOGW("FMQ message cannot be sent from %" PRId64, connectionId);
return;
}
pending.pop_front();
posted.push_back(id);
}
}
}
bool BufferStatusChannel::postBufferStatusMessage(
TransactionId transactionId, BufferId bufferId,
BufferStatus status, ConnectionId connectionId, ConnectionId targetId,
std::list<BufferId> &pending, std::list<BufferId> &posted) {
if (mValid) {
size_t avail = mBufferStatusQueue->availableToWrite();
size_t numPending = pending.size();
if (avail >= numPending + 1) {
BufferStatusMessage release, message;
for (size_t i = 0; i < numPending; ++i) {
BufferId id = pending.front();
release.newStatus = BufferStatus::NOT_USED;
release.bufferId = id;
release.connectionId = connectionId;
if (!mBufferStatusQueue->write(&release, 1)) {
// Since avaliable # of writes are already confiremd,
// this should not happen.
// TODO: error handling?
ALOGW("FMQ message cannot be sent from %" PRId64,
connectionId);
return false;
}
pending.pop_front();
posted.push_back(id);
}
message.transactionId = transactionId;
message.bufferId = bufferId;
message.newStatus = status;
message.connectionId = connectionId;
message.targetConnectionId = targetId;
// TODO : timesatamp
message.timestampUs = 0;
if (!mBufferStatusQueue->write(&message, 1)) {
// Since avaliable # of writes are already confiremd,
// this should not happen.
ALOGW("FMQ message cannot be sent from %" PRId64, connectionId);
return false;
}
return true;
}
}
return false;
}
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android

@ -0,0 +1,140 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H
#include <android/hardware/media/bufferpool/1.0/types.h>
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <memory>
#include <mutex>
#include <vector>
#include <list>
#include <BufferPoolTypes.h>
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
/** Returns monotonic timestamp in Us since fixed point in time. */
int64_t getTimestampNow();
/**
* A collection of FMQ for a buffer pool. buffer ownership/status change
* messages are sent via the FMQs from the clients.
*/
class BufferStatusObserver {
private:
std::map<ConnectionId, std::unique_ptr<BufferStatusQueue>>
mBufferStatusQueues;
public:
/** Creates an FMQ for the specified connection(client).
*
* @param connectionId connection Id of the specified client.
* @param fmqDescPtr double ptr of created FMQ's descriptor.
*
* @return OK if FMQ is created successfully.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus open(ConnectionId id, const QueueDescriptor** fmqDescPtr);
/** Closes an FMQ for the specified connection(client).
*
* @param connectionId connection Id of the specified client.
*
* @return OK if the specified connection is closed successfully.
* CRITICAL_ERROR otherwise.
*/
ResultStatus close(ConnectionId id);
/** Retrieves all pending FMQ buffer status messages from clients.
*
* @param messages retrieved pending messages.
*/
void getBufferStatusChanges(std::vector<BufferStatusMessage> &messages);
};
/**
* An FMQ for a buffer pool client. buffer ownership/status change messages
* are sent via the fmq to the buffer pool.
*/
class BufferStatusChannel {
private:
bool mValid;
std::unique_ptr<BufferStatusQueue> mBufferStatusQueue;
public:
/**
* Connects to an FMQ from a descriptor of the created FMQ.
*
* @param fmqDesc Descriptor of the created FMQ.
*/
BufferStatusChannel(const QueueDescriptor &fmqDesc);
/** Returns whether the FMQ is connected successfully. */
bool isValid();
/**
* Posts a buffer release message to the buffer pool.
*
* @param connectionId connection Id of the client.
* @param pending currently pending buffer release messages.
* @param posted posted buffer release messages.
*/
void postBufferRelease(
ConnectionId connectionId,
std::list<BufferId> &pending, std::list<BufferId> &posted);
/**
* Posts a buffer status message regarding the specified buffer
* transfer transaction.
*
* @param transactionId Id of the specified transaction.
* @param bufferId buffer Id of the specified transaction.
* @param status new status of the buffer.
* @param connectionId connection Id of the client.
* @param targetId connection Id of the receiver(only when the sender
* posts a status message).
* @param pending currently pending buffer release messages.
* @param posted posted buffer release messages.
*
* @return {@code true} when the specified message is posted,
* {@code false} otherwise.
*/
bool postBufferStatusMessage(
TransactionId transactionId,
BufferId bufferId,
BufferStatus status,
ConnectionId connectionId,
ConnectionId targetId,
std::list<BufferId> &pending, std::list<BufferId> &posted);
};
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERSTATUS_H

@ -0,0 +1,340 @@
/*
* Copyright (C) 2018 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.
*/
#include <ClientManager.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "BufferPoolClient.h"
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
static constexpr int64_t kRegisterTimeoutUs = 500000; // 0.5 sec
class ClientManager::Impl {
public:
Impl();
ResultStatus registerSender(const sp<IAccessor> &accessor,
ConnectionId *pConnectionId);
ResultStatus create(const std::shared_ptr<C2Allocator> &allocator,
bool linear,
ConnectionId *pConnectionId);
ResultStatus close(ConnectionId connectionId);
ResultStatus allocate(ConnectionId connectionId,
const std::vector<uint8_t> &params,
std::shared_ptr<_C2BlockPoolData> *buffer);
ResultStatus receive(ConnectionId connectionId,
TransactionId transactionId,
BufferId bufferId,
int64_t timestampUs,
std::shared_ptr<_C2BlockPoolData> *buffer);
ResultStatus postSend(ConnectionId connectionId,
ConnectionId receiverId,
const std::shared_ptr<_C2BlockPoolData> &buffer,
TransactionId *transactionId,
int64_t *timestampUs);
ResultStatus getAccessor(ConnectionId connectionId,
sp<IAccessor> *accessor);
private:
// In order to prevent deadlock between multiple locks,
// Always lock ClientCache.lock before locking ActiveClients.lock.
struct ClientCache {
// This lock is held for brief duration.
// Blocking operation is not performed holding the lock.
std::mutex mMutex;
std::map<const wp<IAccessor>, const std::weak_ptr<BufferPoolClient>>
mClients;
std::condition_variable mConnectCv;
bool mConnecting;
ClientCache() : mConnecting(false) {}
} mCache;
// Active clients which can be retrieved via ConnectionId
struct ActiveClients {
// This lock is held for brief duration.
// Blocking operation is not performed holding the lock.
std::mutex mMutex;
std::map<ConnectionId, const std::shared_ptr<BufferPoolClient>>
mClients;
} mActive;
};
ClientManager::Impl::Impl() {}
ResultStatus ClientManager::Impl::registerSender(
const sp<IAccessor> &accessor, ConnectionId *pConnectionId) {
int64_t timeoutUs = getTimestampNow() + kRegisterTimeoutUs;
do {
std::unique_lock<std::mutex> lock(mCache.mMutex);
auto it = mCache.mClients.find(accessor);
if (it != mCache.mClients.end()) {
const std::shared_ptr<BufferPoolClient> client = it->second.lock();
if (client) {
*pConnectionId = client->getConnectionId();
return ResultStatus::ALREADY_EXISTS;
}
mCache.mClients.erase(it);
}
if (!mCache.mConnecting) {
mCache.mConnecting = true;
lock.unlock();
ResultStatus result = ResultStatus::OK;
const std::shared_ptr<BufferPoolClient> client =
std::make_shared<BufferPoolClient>(accessor);
lock.lock();
if (!client) {
result = ResultStatus::NO_MEMORY;
} else if (!client->isValid()) {
result = ResultStatus::CRITICAL_ERROR;
}
if (result == ResultStatus::OK) {
// TODO: handle insert fail. (malloc fail)
const std::weak_ptr<BufferPoolClient> wclient = client;
mCache.mClients.insert(std::make_pair(accessor, wclient));
ConnectionId conId = client->getConnectionId();
{
std::lock_guard<std::mutex> lock(mActive.mMutex);
mActive.mClients.insert(std::make_pair(conId, client));
}
*pConnectionId = conId;
}
mCache.mConnecting = false;
lock.unlock();
mCache.mConnectCv.notify_all();
return result;
}
mCache.mConnectCv.wait_for(
lock, std::chrono::microseconds(kRegisterTimeoutUs));
} while (getTimestampNow() < timeoutUs);
// TODO: return timeout error
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus ClientManager::Impl::create(
const std::shared_ptr<C2Allocator> &allocator,
bool linear,
ConnectionId *pConnectionId) {
const sp<Accessor> accessor = new Accessor(allocator, linear);
if (!accessor || !accessor->isValid()) {
return ResultStatus::CRITICAL_ERROR;
}
std::shared_ptr<BufferPoolClient> client =
std::make_shared<BufferPoolClient>(accessor);
if (!client || !client->isValid()) {
return ResultStatus::CRITICAL_ERROR;
}
{
// TODO: handle insert fail. (malloc fail)
std::lock_guard<std::mutex> lock(mCache.mMutex);
const wp<Accessor> waccessor = accessor;
const std::weak_ptr<BufferPoolClient> wclient = client;
mCache.mClients.insert(std::make_pair(waccessor, wclient));
ConnectionId conId = client->getConnectionId();
{
std::lock_guard<std::mutex> lock(mActive.mMutex);
mActive.mClients.insert(std::make_pair(conId, client));
}
*pConnectionId = conId;
}
return ResultStatus::OK;
}
ResultStatus ClientManager::Impl::close(ConnectionId connectionId) {
std::lock_guard<std::mutex> lock1(mCache.mMutex);
std::lock_guard<std::mutex> lock2(mActive.mMutex);
auto it = mActive.mClients.find(connectionId);
if (it != mActive.mClients.end()) {
sp<IAccessor> accessor;
if (it->second->getAccessor(&accessor) == ResultStatus::OK) {
mCache.mClients.erase(accessor);
}
mActive.mClients.erase(connectionId);
return ResultStatus::OK;
}
return ResultStatus::NOT_FOUND;
}
ResultStatus ClientManager::Impl::allocate(
ConnectionId connectionId, const std::vector<uint8_t> &params,
std::shared_ptr<_C2BlockPoolData> *buffer) {
std::shared_ptr<BufferPoolClient> client;
{
std::lock_guard<std::mutex> lock(mActive.mMutex);
auto it = mActive.mClients.find(connectionId);
if (it == mActive.mClients.end()) {
return ResultStatus::NOT_FOUND;
}
client = it->second;
}
return client->allocate(params, buffer);
}
ResultStatus ClientManager::Impl::receive(
ConnectionId connectionId, TransactionId transactionId,
BufferId bufferId, int64_t timestampUs,
std::shared_ptr<_C2BlockPoolData> *buffer) {
std::shared_ptr<BufferPoolClient> client;
{
std::lock_guard<std::mutex> lock(mActive.mMutex);
auto it = mActive.mClients.find(connectionId);
if (it == mActive.mClients.end()) {
return ResultStatus::NOT_FOUND;
}
client = it->second;
}
return client->receive(transactionId, bufferId, timestampUs, buffer);
}
ResultStatus ClientManager::Impl::postSend(
ConnectionId connectionId, ConnectionId receiverId,
const std::shared_ptr<_C2BlockPoolData> &buffer,
TransactionId *transactionId, int64_t *timestampUs) {
std::shared_ptr<BufferPoolClient> client;
{
std::lock_guard<std::mutex> lock(mActive.mMutex);
auto it = mActive.mClients.find(connectionId);
if (it == mActive.mClients.end()) {
return ResultStatus::NOT_FOUND;
}
client = it->second;
}
return client->postSend(receiverId, buffer, transactionId, timestampUs);
}
ResultStatus ClientManager::Impl::getAccessor(
ConnectionId connectionId, sp<IAccessor> *accessor) {
std::shared_ptr<BufferPoolClient> client;
{
std::lock_guard<std::mutex> lock(mActive.mMutex);
auto it = mActive.mClients.find(connectionId);
if (it == mActive.mClients.end()) {
return ResultStatus::NOT_FOUND;
}
client = it->second;
}
return client->getAccessor(accessor);
}
// Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
Return<void> ClientManager::registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) {
if (mImpl) {
ConnectionId connectionId = -1;
ResultStatus status = mImpl->registerSender(bufferPool, &connectionId);
_hidl_cb(status, connectionId);
} else {
_hidl_cb(ResultStatus::CRITICAL_ERROR, -1);
}
return Void();
}
// Methods for local use.
sp<ClientManager> ClientManager::sInstance;
std::mutex ClientManager::sInstanceLock;
sp<ClientManager> ClientManager::getInstance() {
std::lock_guard<std::mutex> lock(sInstanceLock);
if (!sInstance) {
sInstance = new ClientManager();
}
return sInstance;
}
ClientManager::ClientManager() : mImpl(new Impl()) {}
ClientManager::~ClientManager() {
}
ResultStatus ClientManager::create(
const std::shared_ptr<C2Allocator> &allocator,
bool linear,
ConnectionId *pConnectionId) {
if (mImpl) {
return mImpl->create(allocator, linear, pConnectionId);
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus ClientManager::close(ConnectionId connectionId) {
if (mImpl) {
return mImpl->close(connectionId);
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus ClientManager::allocate(
ConnectionId connectionId, const std::vector<uint8_t> &params,
std::shared_ptr<_C2BlockPoolData> *buffer) {
if (mImpl) {
return mImpl->allocate(connectionId, params, buffer);
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus ClientManager::receive(
ConnectionId connectionId, TransactionId transactionId,
BufferId bufferId, int64_t timestampUs,
std::shared_ptr<_C2BlockPoolData> *buffer) {
if (mImpl) {
return mImpl->receive(connectionId, transactionId, bufferId,
timestampUs, buffer);
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus ClientManager::postSend(
ConnectionId connectionId, ConnectionId receiverId,
const std::shared_ptr<_C2BlockPoolData> &buffer,
TransactionId *transactionId, int64_t* timestampUs) {
if (mImpl) {
return mImpl->postSend(connectionId, receiverId, buffer,
transactionId, timestampUs);
}
return ResultStatus::CRITICAL_ERROR;
}
ResultStatus ClientManager::getAccessor(
ConnectionId connectionId, sp<IAccessor> *accessor) {
if (mImpl) {
return mImpl->getAccessor(connectionId, accessor);
}
return ResultStatus::CRITICAL_ERROR;
}
//IClientManager* HIDL_FETCH_IClientManager(const char* /* name */) {
// return new ClientManager();
//}
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android

@ -0,0 +1,80 @@
/*
* Copyright (C) 2018 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.
*/
#include "Connection.h"
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
// Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
Return<void> Connection::fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) {
ResultStatus status = ResultStatus::CRITICAL_ERROR;
if (mInitialized && mAccessor) {
const native_handle_t *handle = NULL;
status = mAccessor->fetch(
mConnectionId, transactionId, bufferId, &handle);
if (status == ResultStatus::OK) {
_hidl_cb(status, Buffer{bufferId, handle});
return Void();
}
}
_hidl_cb(status, Buffer{0, nullptr});
return Void();
}
Connection::Connection() : mInitialized(false), mConnectionId(-1LL) {}
Connection::~Connection() {
if (mInitialized && mAccessor) {
mAccessor->close(mConnectionId);
}
}
void Connection::initialize(
const sp<Accessor>& accessor, ConnectionId connectionId) {
if (!mInitialized) {
mAccessor = accessor;
mConnectionId = connectionId;
mInitialized = true;
}
}
ResultStatus Connection::allocate(
const std::vector<uint8_t> &params, BufferId *bufferId,
const native_handle_t **handle) {
if (mInitialized && mAccessor) {
return mAccessor->allocate(mConnectionId, params, bufferId, handle);
}
return ResultStatus::CRITICAL_ERROR;
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//IConnection* HIDL_FETCH_IConnection(const char* /* name */) {
// return new Connection();
//}
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android

@ -0,0 +1,93 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H
#include <android/hardware/media/bufferpool/1.0/IConnection.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <BufferPoolTypes.h>
#include "Accessor.h"
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::media::bufferpool::V1_0::implementation::Accessor;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
struct Connection : public IConnection {
// Methods from ::android::hardware::media::bufferpool::V1_0::IConnection follow.
Return<void> fetch(uint64_t transactionId, uint32_t bufferId, fetch_cb _hidl_cb) override;
/**
* Allocates a buffer using the specified parameters. Recycles a buffer if
* it is possible. The returned buffer can be transferred to other remote
* clients(Connection).
*
* @param params allocation parameters.
* @param bufferId Id of the allocated buffer.
* @param handle native handle of the allocated buffer.
*
* @return OK if a buffer is successfully allocated.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus allocate(const std::vector<uint8_t> &params,
BufferId *bufferId, const native_handle_t **handle);
/** Destructs a connection. */
~Connection();
/** Creates a connection. */
Connection();
/**
* Initializes with the specified buffer pool and the connection id.
* The connection id should be unique in the whole system.
*
* @param accessor the specified buffer pool.
* @param connectionId Id.
*/
void initialize(const sp<Accessor> &accessor, ConnectionId connectionId);
private:
bool mInitialized;
sp<Accessor> mAccessor;
ConnectionId mConnectionId;
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" IConnection* HIDL_FETCH_IConnection(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CONNECTION_H

@ -0,0 +1,67 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H
#include <android/hardware/media/bufferpool/1.0/types.h>
#include <cutils/native_handle.h>
#include <fmq/MessageQueue.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <C2Buffer.h>
struct C2_HIDE _C2BlockPoolData {
uint32_t mId; //BufferId
native_handle_t *mHandle;
_C2BlockPoolData() : mId(0), mHandle(NULL) {}
_C2BlockPoolData(uint32_t id, native_handle_t *handle)
: mId(id), mHandle(handle) {}
~_C2BlockPoolData() {
if (mHandle != NULL) {
native_handle_close(mHandle);
native_handle_delete(mHandle);
}
}
};
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
using ::android::hardware::kSynchronizedReadWrite;
typedef uint32_t BufferId;
typedef uint64_t TransactionId;
typedef int64_t ConnectionId;
typedef android::hardware::MessageQueue<BufferStatusMessage, kSynchronizedReadWrite> BufferStatusQueue;
typedef BufferStatusQueue::Descriptor QueueDescriptor;
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_BUFFERPOOLTYPES_H

@ -0,0 +1,169 @@
/*
* Copyright (C) 2018 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.
*/
#ifndef ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
#define ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
#include <android/hardware/media/bufferpool/1.0/IClientManager.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <C2Buffer.h>
#include <memory>
#include <BufferPoolTypes.h>
namespace android {
namespace hardware {
namespace media {
namespace bufferpool {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::media::bufferpool::V1_0::IAccessor;
using ::android::hardware::media::bufferpool::V1_0::ResultStatus;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
struct ClientManager : public IClientManager {
// Methods from ::android::hardware::media::bufferpool::V1_0::IClientManager follow.
Return<void> registerSender(const sp<::android::hardware::media::bufferpool::V1_0::IAccessor>& bufferPool, registerSender_cb _hidl_cb) override;
/** Gets an instance. */
static sp<ClientManager> getInstance();
/**
* Creates a local connection with a newly created buffer pool.
*
* @param allocator for new buffer allocation.
* @param linear whether the allocator is linear or not.
* @param pConnectionId Id of the created connection. This is
* system-wide unique.
*
* @return OK when a buffer pool and a local connection is successfully
* created.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus create(const std::shared_ptr<C2Allocator> &allocator,
bool linear,
ConnectionId *pConnectionId);
/**
* Closes the specified connection.
*
* @param connectionId The id of the connection.
*
* @return OK when the connection is closed.
* NOT_FOUND when the specified connection was not found.
* CRITICAL_ERROR otherwise.
*/
ResultStatus close(ConnectionId connectionId);
/**
* Allocates a buffer from the specified connection.
*
* @param connectionId The id of the connection.
* @param params The allocation parameters.
* @param buffer The allocated buffer.
*
* @return OK when a buffer was allocated successfully.
* NOT_FOUND when the specified connection was not found.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus allocate(ConnectionId connectionId,
const std::vector<uint8_t> &params,
std::shared_ptr<_C2BlockPoolData> *buffer);
/**
* Receives a buffer for the transaction.
*
* @param connectionId The id of the receiving connection.
* @param transactionId The id for the transaction.
* @param bufferId The id for the buffer.
* @param timestampUs The timestamp of the buffer is being sent.
* @param buffer The received buffer.
*
* @return OK when a buffer was received successfully.
* NOT_FOUND when the specified connection was not found.
* NO_MEMORY when there is no memory.
* CRITICAL_ERROR otherwise.
*/
ResultStatus receive(ConnectionId connectionId,
TransactionId transactionId,
BufferId bufferId,
int64_t timestampUs,
std::shared_ptr<_C2BlockPoolData> *buffer);
/**
* Posts a buffer transfer transaction to the buffer pool. Sends a buffer
* to other remote clients(connection) after this call has been succeeded.
*
* @param connectionId The id of the sending connection.
* @param receiverId The id of the receiving connection.
* @param buffer to transfer
* @param transactionId Id of the transfer transaction.
* @param timestampUs The timestamp of the buffer transaction is being
* posted.
*
* @return OK when a buffer transaction was posted successfully.
* NOT_FOUND when the sending connection was not found.
* CRITICAL_ERROR otherwise.
*/
ResultStatus postSend(ConnectionId connectionId,
ConnectionId receiverId,
const std::shared_ptr<_C2BlockPoolData> &buffer,
TransactionId *transactionId,
int64_t *timestampUs);
/** Gets a buffer pool for the specified connection.
*
* @param connectionId The id of the connection.
* @param accessor The buffer pool for the specified connection.
* @return OK when a buffer pool was found for the connection.
* NOT_FOUND when the specified connection was not found.
* CRITICAL_ERROR otherwise.
*/
ResultStatus getAccessor(ConnectionId connectionId,
sp<IAccessor> *accessor);
/** Destructs the manager of buffer pool clients. */
~ClientManager();
private:
static sp<ClientManager> sInstance;
static std::mutex sInstanceLock;
class Impl;
const std::unique_ptr<Impl> mImpl;
ClientManager();
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" IClientManager* HIDL_FETCH_IClientManager(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace bufferpool
} // namespace media
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_MEDIA_BUFFERPOOL_V1_0_CLIENTMANAGER_H
Loading…
Cancel
Save