/* * 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 #include "IntelMetadataBuffer.h" #include #include #ifdef INTEL_VIDEO_XPROC_SHARING #include #include #include #include #include #include //#define TEST struct ShareMemMap { uint32_t sessionflag; intptr_t value; intptr_t value_backup; uint32_t type; sp membase; sp gbuffer; }; List 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 heap = interface_cast(data.readStrongBinder()); sp 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 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 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::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::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::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 GetIntelBufferSharingService() { sp sm = defaultServiceManager(); sp 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 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 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 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 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 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 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