/*
* Copyright (c) 2009-2011 Intel Corporation. All rights reserved.
*
* 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_NDEBUG 0
#define LOG_TAG "IntelMetadataBuffer"
#include <wrs_omxil_core/log.h>
#include "IntelMetadataBuffer.h"
#include <string.h>
#include <stdio.h>
#ifdef INTEL_VIDEO_XPROC_SHARING
#include <binder/IServiceManager.h>
#include <binder/MemoryBase.h>
#include <binder/Parcel.h>
#include <utils/List.h>
#include <utils/threads.h>
#include <ui/GraphicBuffer.h>
//#define TEST
struct ShareMemMap {
uint32_t sessionflag;
intptr_t value;
intptr_t value_backup;
uint32_t type;
sp<MemoryBase> membase;
sp<GraphicBuffer> gbuffer;
};
List <ShareMemMap *> gShareMemMapList;
Mutex gShareMemMapListLock;
enum {
SHARE_MEM = IBinder::FIRST_CALL_TRANSACTION,
GET_MEM,
CLEAR_MEM,
};
enum {
ST_MEMBASE = 0,
ST_GFX,
ST_MAX,
};
#define REMOTE_PROVIDER 0x80000000
#define REMOTE_CONSUMER 0x40000000
static ShareMemMap* ReadMemObjFromBinder(const Parcel& data, uint32_t sessionflag, intptr_t value) {
uint32_t type = data.readInt32();
if (type >= ST_MAX)
return NULL;
ShareMemMap* map = new ShareMemMap;
map->sessionflag = sessionflag;
map->type = type;
map->value_backup = value;
map->membase = NULL;
map->gbuffer= NULL;
// LOGI("ReadMemObjFromBinder");
if (type == ST_MEMBASE) /*offset, size, heap*/
{
ssize_t offset = data.readInt32();
size_t size = data.readInt32();
sp<IMemoryHeap> heap = interface_cast<IMemoryHeap>(data.readStrongBinder());
sp<MemoryBase> mem = new MemoryBase(heap, offset, size);
if (mem == NULL)
{
delete map;
return NULL;
}
map->value = (intptr_t)( mem->pointer() + 0x0FFF) & ~0x0FFF;
map->membase = mem;
#ifdef TEST
ALOGI("membase heapID:%d, pointer:%x data:%x, aligned value:%x", \
heap->getHeapID(), mem->pointer(), *((intptr_t *)(mem->pointer())), map->value);
#endif
}
else if (type == ST_GFX) /*graphicbuffer*/
{
sp<GraphicBuffer> buffer = new GraphicBuffer();
if (buffer == NULL)
{
delete map;
return NULL;
}
data.read(*buffer);
map->value = (intptr_t)buffer->handle;
map->gbuffer = buffer;
#ifdef TEST
void* usrptr[3];
buffer->lock(GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_READ_OFTEN, &usrptr[0]);
buffer->unlock();
ALOGI("gfx handle:%p data:%x", (intptr_t)buffer->handle, *((intptr_t *)usrptr[0]));
#endif
}
gShareMemMapListLock.lock();
gShareMemMapList.push_back(map);
gShareMemMapListLock.unlock();
return map;
}
static status_t WriteMemObjToBinder(Parcel& data, ShareMemMap* smem) {
if (smem->type >= ST_MAX)
return BAD_VALUE;
// LOGI("WriteMemObjToBinder");
data.writeInt32(smem->type);
if (smem->type == ST_MEMBASE) /*offset, size, heap*/
{
ssize_t offset;
size_t size;
sp<IMemoryHeap> heap = smem->membase->getMemory(&offset, &size);
data.writeInt32(offset);
data.writeInt32(size);
data.writeStrongBinder(IInterface::asBinder(heap));
#ifdef TEST
ALOGI("membase heapID:%d pointer:%x data:%x", \
heap->getHeapID(), smem->membase->pointer(), *((int *)(smem->membase->pointer())));
#endif
}
else if (smem->type == ST_GFX) /*graphicbuffer*/
data.write(*(smem->gbuffer));
return NO_ERROR;
}
static void ClearLocalMem(uint32_t sessionflag)
{
List<ShareMemMap *>::iterator node;
gShareMemMapListLock.lock();
for(node = gShareMemMapList.begin(); node != gShareMemMapList.end(); )
{
if ((*node)->sessionflag == sessionflag) //remove all buffers belong to this session
{
(*node)->membase = NULL;
(*node)->gbuffer = NULL;
delete (*node);
node = gShareMemMapList.erase(node);
}
else
node ++;
}
gShareMemMapListLock.unlock();
}
static ShareMemMap* FindShareMem(uint32_t sessionflag, intptr_t value, bool isBackup)
{
List<ShareMemMap *>::iterator node;
gShareMemMapListLock.lock();
for(node = gShareMemMapList.begin(); node != gShareMemMapList.end(); node++)
{
if (isBackup)
{
if ((*node)->sessionflag == sessionflag && (*node)->value_backup == value)
{
gShareMemMapListLock.unlock();
return (*node);
}
}
else if ((*node)->sessionflag == sessionflag && (*node)->value == value)
{
gShareMemMapListLock.unlock();
return (*node);
}
}
gShareMemMapListLock.unlock();
return NULL;
}
static ShareMemMap* PopShareMem(uint32_t sessionflag, intptr_t value)
{
List<ShareMemMap *>::iterator node;
gShareMemMapListLock.lock();
for(node = gShareMemMapList.begin(); node != gShareMemMapList.end(); node++)
{
if ((*node)->sessionflag == sessionflag && (*node)->value == value)
{
gShareMemMapList.erase(node);
gShareMemMapListLock.unlock();
return (*node);
}
}
gShareMemMapListLock.unlock();
return NULL;
}
static void PushShareMem(ShareMemMap* &smem)
{
gShareMemMapListLock.lock();
gShareMemMapList.push_back(smem);
gShareMemMapListLock.unlock();
}
static sp<IBinder> GetIntelBufferSharingService() {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->checkService(String16("media.IntelBufferSharing"));
if (binder == 0)
ALOGE("media.IntelBufferSharing service is not published");
return binder;
}
IntelBufferSharingService* IntelBufferSharingService::gBufferService = NULL;
status_t IntelBufferSharingService::instantiate(){
status_t ret = NO_ERROR;
if (gBufferService == NULL) {
gBufferService = new IntelBufferSharingService();
ret = defaultServiceManager()->addService(String16("media.IntelBufferSharing"), gBufferService);
LOGI("IntelBufferSharingService::instantiate() ret = %d\n", ret);
}
return ret;
}
status_t IntelBufferSharingService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
//TODO: if pid is int32?
pid_t pid = data.readInt32();
uint32_t sessionflag = data.readInt32();
switch(code)
{
case SHARE_MEM:
{
if (pid == getpid()) //in same process, should not use binder
{
ALOGE("onTransact in same process, wrong sessionflag?");
return UNKNOWN_ERROR;
}
intptr_t value = data.readIntPtr();
// LOGI("onTransact SHARE_MEM value=%x", value);
//different process
ShareMemMap* map = ReadMemObjFromBinder(data, sessionflag, value);
if (map == NULL)
return UNKNOWN_ERROR;
reply->writeIntPtr(map->value);
return NO_ERROR;
}
case CLEAR_MEM:
{
// LOGI("onTransact CLEAR_MEM sessionflag=%x", sessionflag);
if (pid == getpid()) //in same process, should not use binder
{
//same process, return same pointer in data
ALOGE("onTransact CLEAR_MEM in same process, wrong sessionflag?");
return UNKNOWN_ERROR;
}
ClearLocalMem(sessionflag);
return NO_ERROR;
}
case GET_MEM:
{
if (pid == getpid()) //in same process, should not use binder
{
ALOGE("onTransact GET_MEM in same process, wrong sessionflag?");
return UNKNOWN_ERROR;
}
intptr_t value = data.readIntPtr();
// LOGI("onTransact GET_MEM value=%x", value);
ShareMemMap* smem = FindShareMem(sessionflag, value, false);
if (smem && (NO_ERROR == WriteMemObjToBinder(*reply, smem)))
return NO_ERROR;
else
ALOGE("onTransact GET_MEM: Not find mem");
return UNKNOWN_ERROR;
}
default:
return BBinder::onTransact(code, data, reply, flags);
}
return NO_ERROR;
}
#endif
IntelMetadataBuffer::IntelMetadataBuffer()
{
mType = IntelMetadataBufferTypeCameraSource;
mValue = 0;
mInfo = NULL;
mExtraValues = NULL;
mExtraValues_Count = 0;
mBytes = NULL;
mSize = 0;
#ifdef INTEL_VIDEO_XPROC_SHARING
mSessionFlag = 0;
#endif
}
IntelMetadataBuffer::IntelMetadataBuffer(IntelMetadataBufferType type, intptr_t value)
{
mType = type;
mValue = value;
mInfo = NULL;
mExtraValues = NULL;
mExtraValues_Count = 0;
mBytes = NULL;
mSize = 0;
#ifdef INTEL_VIDEO_XPROC_SHARING
mSessionFlag = 0;
#endif
}
IntelMetadataBuffer::~IntelMetadataBuffer()
{
if (mInfo)
delete mInfo;
if (mExtraValues)
delete[] mExtraValues;
if (mBytes)
delete[] mBytes;
}
IntelMetadataBuffer::IntelMetadataBuffer(const IntelMetadataBuffer& imb)
:mType(imb.mType), mValue(imb.mValue), mInfo(NULL), mExtraValues(NULL),
mExtraValues_Count(imb.mExtraValues_Count), mBytes(NULL), mSize(imb.mSize)
#ifdef INTEL_VIDEO_XPROC_SHARING
,mSessionFlag(imb.mSessionFlag)
#endif
{
if (imb.mInfo)
mInfo = new ValueInfo(*imb.mInfo);
if (imb.mExtraValues)
{
mExtraValues = new intptr_t[mExtraValues_Count];
memcpy(mExtraValues, imb.mExtraValues, sizeof(mValue) * mExtraValues_Count);
}
if (imb.mBytes)
{
mBytes = new uint8_t[mSize];
memcpy(mBytes, imb.mBytes, mSize);
}
}
const IntelMetadataBuffer& IntelMetadataBuffer::operator=(const IntelMetadataBuffer& imb)
{
mType = imb.mType;
mValue = imb.mValue;
mInfo = NULL;
mExtraValues = NULL;
mExtraValues_Count = imb.mExtraValues_Count;
mBytes = NULL;
mSize = imb.mSize;
#ifdef INTEL_VIDEO_XPROC_SHARING
mSessionFlag = imb.mSessionFlag;
#endif
if (imb.mInfo)
mInfo = new ValueInfo(*imb.mInfo);
if (imb.mExtraValues)
{
mExtraValues = new intptr_t[mExtraValues_Count];
memcpy(mExtraValues, imb.mExtraValues, sizeof(mValue) * mExtraValues_Count);
}
if (imb.mBytes)
{
mBytes = new uint8_t[mSize];
memcpy(mBytes, imb.mBytes, mSize);
}
return *this;
}
IMB_Result IntelMetadataBuffer::GetType(IntelMetadataBufferType& type)
{
type = mType;
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::SetType(IntelMetadataBufferType type)
{
if (type < IntelMetadataBufferTypeLast)
mType = type;
else
return IMB_INVAL_PARAM;
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::GetValue(intptr_t& value)
{
value = mValue;
#ifndef INTEL_VIDEO_XPROC_SHARING
return IMB_SUCCESS;
#else
if ((mSessionFlag & REMOTE_CONSUMER) == 0) //no sharing or is local consumer
return IMB_SUCCESS;
//try to find if it is already cached.
ShareMemMap* smem = FindShareMem(mSessionFlag, mValue, true);
if(smem)
{
value = smem->value;
return IMB_SUCCESS;
}
//is remote provider and not find from cache, then pull from service
sp<IBinder> binder = GetIntelBufferSharingService();
if (binder == 0)
return IMB_NO_SERVICE;
//Detect IntelBufferSharingService, share mem to service
Parcel data, reply;
//send pid, sessionflag, and memtype
pid_t pid = getpid();
//TODO: if pid is int32?
data.writeInt32(pid);
data.writeInt32(mSessionFlag);
data.writeIntPtr(mValue);
//do transcation
if (binder->transact(GET_MEM, data, &reply) != NO_ERROR)
return IMB_SERVICE_FAIL;
//get type/Mem OBJ
smem = ReadMemObjFromBinder(reply, mSessionFlag, mValue);
if (smem)
value = smem->value;
else
return IMB_SERVICE_FAIL;
return IMB_SUCCESS;
#endif
}
IMB_Result IntelMetadataBuffer::SetValue(intptr_t value)
{
mValue = value;
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::GetValueInfo(ValueInfo* &info)
{
info = mInfo;
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::SetValueInfo(ValueInfo* info)
{
if (info)
{
if (mInfo == NULL)
mInfo = new ValueInfo;
memcpy(mInfo, info, sizeof(ValueInfo));
}
else
return IMB_INVAL_PARAM;
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::GetExtraValues(intptr_t* &values, uint32_t& num)
{
values = mExtraValues;
num = mExtraValues_Count;
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::SetExtraValues(intptr_t* values, uint32_t num)
{
if (values && num > 0)
{
if (mExtraValues && mExtraValues_Count != num)
{
delete[] mExtraValues;
mExtraValues = NULL;
}
if (mExtraValues == NULL)
mExtraValues = new intptr_t[num];
memcpy(mExtraValues, values, sizeof(intptr_t) * num);
mExtraValues_Count = num;
}
else
return IMB_INVAL_PARAM;
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::UnSerialize(uint8_t* data, uint32_t size)
{
if (!data || size == 0)
return IMB_INVAL_PARAM;
IntelMetadataBufferType type;
intptr_t value;
uint32_t extrasize = size - sizeof(type) - sizeof(value);
ValueInfo* info = NULL;
intptr_t* ExtraValues = NULL;
uint32_t ExtraValues_Count = 0;
memcpy(&type, data, sizeof(type));
data += sizeof(type);
memcpy(&value, data, sizeof(value));
data += sizeof(value);
switch (type)
{
case IntelMetadataBufferTypeCameraSource:
case IntelMetadataBufferTypeEncoder:
case IntelMetadataBufferTypeUser:
{
if (extrasize >0 && extrasize < sizeof(ValueInfo))
return IMB_INVAL_BUFFER;
if (extrasize > sizeof(ValueInfo)) //has extravalues
{
if ( (extrasize - sizeof(ValueInfo)) % sizeof(mValue) != 0 )
return IMB_INVAL_BUFFER;
ExtraValues_Count = (extrasize - sizeof(ValueInfo)) / sizeof(mValue);
}
if (extrasize > 0)
{
info = new ValueInfo;
memcpy(info, data, sizeof(ValueInfo));
data += sizeof(ValueInfo);
}
if (ExtraValues_Count > 0)
{
ExtraValues = new intptr_t[ExtraValues_Count];
memcpy(ExtraValues, data, ExtraValues_Count * sizeof(mValue));
}
break;
}
case IntelMetadataBufferTypeGrallocSource:
if (extrasize > 0)
return IMB_INVAL_BUFFER;
break;
default:
return IMB_INVAL_BUFFER;
}
//store data
mType = type;
mValue = value;
if (mInfo)
delete mInfo;
mInfo = info;
if (mExtraValues)
delete[] mExtraValues;
mExtraValues = ExtraValues;
mExtraValues_Count = ExtraValues_Count;
#ifdef INTEL_VIDEO_XPROC_SHARING
if (mInfo != NULL)
mSessionFlag = mInfo->sessionFlag;
#endif
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::Serialize(uint8_t* &data, uint32_t& size)
{
if (mBytes == NULL)
{
if (mType == IntelMetadataBufferTypeGrallocSource && mInfo)
return IMB_INVAL_PARAM;
//assemble bytes according members
mSize = sizeof(mType) + sizeof(mValue);
if (mInfo)
{
mSize += sizeof(ValueInfo);
if (mExtraValues)
mSize += sizeof(mValue) * mExtraValues_Count;
}
mBytes = new uint8_t[mSize];
uint8_t *ptr = mBytes;
memcpy(ptr, &mType, sizeof(mType));
ptr += sizeof(mType);
memcpy(ptr, &mValue, sizeof(mValue));
ptr += sizeof(mValue);
if (mInfo)
{
#ifdef INTEL_VIDEO_XPROC_SHARING
mInfo->sessionFlag = mSessionFlag;
#endif
memcpy(ptr, mInfo, sizeof(ValueInfo));
ptr += sizeof(ValueInfo);
if (mExtraValues)
memcpy(ptr, mExtraValues, mExtraValues_Count * sizeof(mValue));
}
}
data = mBytes;
size = mSize;
return IMB_SUCCESS;
}
uint32_t IntelMetadataBuffer::GetMaxBufferSize()
{
return 256;
}
#ifdef INTEL_VIDEO_XPROC_SHARING
IMB_Result IntelMetadataBuffer::GetSessionFlag(uint32_t& sessionflag)
{
sessionflag = mSessionFlag;
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::SetSessionFlag(uint32_t sessionflag)
{
mSessionFlag = sessionflag;
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::ShareValue(sp<MemoryBase> mem)
{
mValue = (intptr_t)((intptr_t) ( mem->pointer() + 0x0FFF) & ~0x0FFF);
if ( !(mSessionFlag & REMOTE_PROVIDER) && !(mSessionFlag & REMOTE_CONSUMER)) //no sharing
return IMB_SUCCESS;
if (mSessionFlag & REMOTE_PROVIDER) //is remote provider
{
sp<IBinder> binder = GetIntelBufferSharingService();
if (binder == 0)
return IMB_NO_SERVICE;
//Detect IntelBufferSharingService, share mem to service
Parcel data, reply;
//send pid, sessionflag, and value
pid_t pid = getpid();
//TODO: if pid is int32?
data.writeInt32(pid);
data.writeInt32(mSessionFlag);
data.writeIntPtr(mValue);
//send type/obj (offset/size/MemHeap)
ShareMemMap smem;
smem.membase = mem;
smem.type = ST_MEMBASE;
if (WriteMemObjToBinder(data, &smem) != NO_ERROR)
return IMB_SERVICE_FAIL;
//do transcation
if (binder->transact(SHARE_MEM, data, &reply) != NO_ERROR)
return IMB_SERVICE_FAIL;
//set new value gotten from peer
mValue = reply.readIntPtr();
// LOGI("ShareValue(membase) Get reply from sevice, new value:%x\n", mValue);
}
else //is local provider , direct access list
{
ShareMemMap* smem = new ShareMemMap;
smem->sessionflag = mSessionFlag;
smem->value = mValue;
smem->value_backup = mValue;
smem->type = ST_MEMBASE;
smem->membase = mem;
smem->gbuffer = NULL;
PushShareMem(smem);
}
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::ShareValue(sp<GraphicBuffer> gbuffer)
{
mValue = (intptr_t)gbuffer->handle;
if ( !(mSessionFlag & REMOTE_PROVIDER) && !(mSessionFlag & REMOTE_CONSUMER)) //no sharing
return IMB_SUCCESS;
if (mSessionFlag & REMOTE_PROVIDER == 0) //is remote provider
{
sp<IBinder> binder = GetIntelBufferSharingService();
if (binder == 0)
return IMB_NO_SERVICE;
Parcel data, reply;
//send pid, sessionflag, and memtype
pid_t pid = getpid();
//TODO: if pid is int32 ?
data.writeInt32(pid);
data.writeInt32(mSessionFlag);
data.writeIntPtr(mValue);
//send value/graphicbuffer obj
ShareMemMap smem;
smem.gbuffer = gbuffer;
smem.type = ST_GFX;
if (WriteMemObjToBinder(data, &smem) != NO_ERROR)
return IMB_SERVICE_FAIL;
//do transcation
if (binder->transact(SHARE_MEM, data, &reply) != NO_ERROR)
return IMB_SERVICE_FAIL;
//set new value gotten from peer
mValue = reply.readIntPtr();
// LOGI("ShareValue(gfx) Get reply from sevice, new value:%x\n", mValue);
}
else //is local provider, direct access list
{
ShareMemMap* smem = new ShareMemMap;
smem->sessionflag = mSessionFlag;
smem->value = mValue;
smem->value_backup = mValue;
smem->type = ST_GFX;
smem->membase = NULL;
smem->gbuffer = gbuffer;
PushShareMem(smem);
}
return IMB_SUCCESS;
}
IMB_Result IntelMetadataBuffer::ClearContext(uint32_t sessionflag, bool isProvider)
{
if ( !(sessionflag & REMOTE_PROVIDER) && !(sessionflag & REMOTE_CONSUMER)) //no sharing
return IMB_SUCCESS;
//clear local firstly
ClearLocalMem(sessionflag);
//clear mem on service if it is remote user
if ((isProvider && (sessionflag & REMOTE_PROVIDER)) || (!isProvider && (sessionflag & REMOTE_CONSUMER)))
{
// LOGI("CLEAR_MEM sessionflag=%x", sessionflag);
sp<IBinder> binder = GetIntelBufferSharingService();
if (binder == 0)
return IMB_NO_SERVICE;
//Detect IntelBufferSharingService, unshare mem from service
Parcel data, reply;
//send pid and sessionflag
pid_t pid = getpid();
//TODO: if pid is int32?
data.writeInt32(pid);
data.writeInt32(sessionflag);
if (binder->transact(CLEAR_MEM, data, &reply) != NO_ERROR)
return IMB_SERVICE_FAIL;
}
return IMB_SUCCESS;
}
uint32_t IntelMetadataBuffer::MakeSessionFlag(bool romoteProvider, bool remoteConsumer, uint16_t sindex)
{
uint32_t sessionflag = 0;
if (romoteProvider)
sessionflag |= REMOTE_PROVIDER;
if (remoteConsumer)
sessionflag |= REMOTE_CONSUMER;
return sessionflag + sindex;
}
#endif