Android4.4 Camera 資料流分析
開門見山:
這裡給出rk 在cameraHAL層的camera資料結構:
typedef struct FramInfo
{
int phy_addr;
int vir_addr;
int frame_width;
int frame_height;
int frame_index;
int frame_fmt;
int zoom_value;
int used_flag;
int frame_size;
void* res;
}FramInfo_s;
主要是第二個成員,虛擬地址,也就是儲存buffer的地址。
camera資料是在介面getFrame中填充的:
int CameraAdapter::getFrame(FramInfo_s** tmpFrame){}
看一下具體的填充過程:
// fill frame info:w,h,phy,vir
mPreviewFrameInfos[cfilledbuffer1.index].frame_fmt= mCamDriverPreviewFmt;
mPreviewFrameInfos[cfilledbuffer1.index].frame_height = mCamDrvHeight;
mPreviewFrameInfos[cfilledbuffer1.index ].frame_width = mCamDrvWidth;
mPreviewFrameInfos[cfilledbuffer1.index].frame_index = cfilledbuffer1.index;
if(mCamDriverV4l2MemType == V4L2_MEMORY_OVERLAY){
#if (defined(TARGET_RK312x) && (IOMMU_ENABLED == 1))
mPreviewFrameInfos[cfilledbuffer1.index].phy_addr = mPreviewBufProvider->getBufShareFd(cfilledbuffer1.index );
#else
mPreviewFrameInfos[cfilledbuffer1.index].phy_addr = mPreviewBufProvider->getBufPhyAddr(cfilledbuffer1.index);
#endif
}else
mPreviewFrameInfos[cfilledbuffer1.index].phy_addr = 0;
mPreviewFrameInfos[cfilledbuffer1.index].vir_addr = (int)mCamDriverV4l2Buffer[cfilledbuffer1.index];
//get zoom_value
mPreviewFrameInfos[cfilledbuffer1.index].zoom_value = mZoomVal;
mPreviewFrameInfos[cfilledbuffer1.index].used_flag = 0;
mPreviewFrameInfos[cfilledbuffer1.index].frame_size = cfilledbuffer1.bytesused;
mPreviewFrameInfos[cfilledbuffer1.index].res = NULL;
*tmpFrame = &(mPreviewFrameInfos[cfilledbuffer1.index]);
比較關鍵的就是:
mPreviewFrameInfos[cfilledbuffer1.index].vir_addr = (int)mCamDriverV4l2Buffer[cfilledbuffer1.index];
把之前初始化mmap到使用者地址空間的buffer地址賦值給了vir_addr。最後把整個結構體的地址傳給了tmpFrame
然後通過:
notifyNewPreviewCbFrame(tmpFrame);
把這個frame封裝成msg:
void AppMsgNotifier::notifyNewPreviewCbFrame(FramInfo_s* frame)
{
//send to app msg thread
Message msg;
Mutex::Autolock lock(mDataCbLock);
if(mRunningState & STA_RECEIVE_PREVIEWCB_FRAME){
msg.command = CameraAppMsgThread::CMD_EVENT_PREVIEW_DATA_CB;
msg.arg2 = (void*)(frame);
msg.arg3 = (void*)(frame->used_flag);
eventThreadCommandQ.put(&msg);
}else
mFrameProvider->returnFrame(frame->frame_index,frame->used_flag);
}
封裝到msg的第二個引數 arg2中。
在eventThread中獲取這個訊息,然後處理:
frame = (FramInfo_s*)msg.arg2;
processPreviewDataCb(frame);
下面看看processPreviewDataCb
int AppMsgNotifier::processPreviewDataCb(FramInfo_s* frame){
int ret = 0;
mDataCbLock.lock();
if ((mMsgTypeEnabled & CAMERA_MSG_PREVIEW_FRAME) && mDataCb) {
//compute request mem size
int tempMemSize = 0;
//request bufer
camera_memory_t* tmpPreviewMemory = NULL;
if (strcmp(mPreviewDataFmt,android::CameraParameters::PIXEL_FORMAT_RGB565) == 0) {
tempMemSize = mPreviewDataW*mPreviewDataH*2;
} else if (strcmp(mPreviewDataFmt,android::CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) {
tempMemSize = mPreviewDataW*mPreviewDataH*3/2;
} else if (strcmp(mPreviewDataFmt,android::CameraParameters::PIXEL_FORMAT_YUV422SP) == 0) {
tempMemSize = mPreviewDataW*mPreviewDataH*2;
} else if(strcmp(mPreviewDataFmt,android::CameraParameters::PIXEL_FORMAT_YUV420P) == 0){
tempMemSize = ((mPreviewDataW+15)&0xfffffff0)*mPreviewDataH
+((mPreviewDataW/2+15)&0xfffffff0)*mPreviewDataH;
}else {
LOGE("%s(%d): pixel format %s is unknow!",__FUNCTION__,__LINE__,mPreviewDataFmt);
}
mDataCbLock.unlock();
tmpPreviewMemory = mRequestMemory(-1, tempMemSize, 1, NULL);
if (tmpPreviewMemory) {
//fill the tmpPreviewMemory
if (strcmp(mPreviewDataFmt,android::CameraParameters::PIXEL_FORMAT_YUV420P) == 0) {
cameraFormatConvert(V4L2_PIX_FMT_NV12,0,mPreviewDataFmt,
(char*)frame->vir_addr,(char*)tmpPreviewMemory->data,0,0,tempMemSize,
frame->frame_width, frame->frame_height,frame->frame_width,
//frame->frame_width,frame->frame_height,frame->frame_width,false);
mPreviewDataW,mPreviewDataH,mPreviewDataW,mDataCbFrontMirror);
}else {
#if 0
//QQ voip need NV21
arm_camera_yuv420_scale_arm(V4L2_PIX_FMT_NV12, V4L2_PIX_FMT_NV21, (char*)(frame->vir_addr),
(char*)tmpPreviewMemory->data,frame->frame_width, frame->frame_height,mPreviewDataW, mPreviewDataH,mDataCbFrontMirror,frame->zoom_value);
#else
rga_nv12_scale_crop(frame->frame_width, frame->frame_height,
(char*)(frame->vir_addr), (short int *)(tmpPreviewMemory->data),
mPreviewDataW,mPreviewDataW,mPreviewDataH,frame->zoom_value,mDataCbFrontMirror,true,true);
#endif
//arm_yuyv_to_nv12(frame->frame_width, frame->frame_height,(char*)(frame->vir_addr), (char*)buf_vir);
}
if(mDataCbFrontFlip) {
LOG1("----------------need flip -------------------");
YuvData_Mirror_Flip(V4L2_PIX_FMT_NV12, (char*) tmpPreviewMemory->data,
(char*)frame->vir_addr,mPreviewDataW, mPreviewDataH);
}
//callback
mDataCb(CAMERA_MSG_PREVIEW_FRAME, tmpPreviewMemory, 0,NULL,mCallbackCookie);
//release buffer
tmpPreviewMemory->release(tmpPreviewMemory);
} else {
LOGE("%s(%d): mPreviewMemory create failed",__FUNCTION__,__LINE__);
}
} else {
mDataCbLock.unlock();
LOG1("%s(%d): needn't to send preview datacb",__FUNCTION__,__LINE__);
}
return ret;
}
比較冗長,關注其跟資料記憶體操作有關的部分即可:
首先先聲明瞭一個物件例項:
camera_memory_t* tmpPreviewMemory = NULL;
這個結構體具體如下:
typedef struct camera_memory {
void *data;
size_t size;
void *handle;
camera_release_memory release;
} camera_memory_t;
四個成員分別表示data存放,data大小,handle可以代表這個結構體的控制代碼,release是釋放記憶體的函式指標。後面會具體講這個結構體是如何填充的。
接著看:
tmpPreviewMemory = mRequestMemory(-1, tempMemSize, 1, NULL);
這是一個很複雜的呼叫操作,簡單講他的作用是獲取一塊匿名共享記憶體的地址,而實現部分都被封裝在這個裡面。仔細看mRequestMemory;
這個函式的實現發現是一個函式指標,這個就扯到了callback了,看看這個callback實在哪裡註冊的:
void AppMsgNotifier::setCallbacks(camera_notify_callback notify_cb,
camera_data_callback data_cb,
camera_data_timestamp_callback data_cb_timestamp,
camera_request_memory get_memory,
void *user)
{
LOG_FUNCTION_NAME
mNotifyCb = notify_cb;
mDataCb = data_cb;
mDataCbTimestamp = data_cb_timestamp;
mRequestMemory = get_memory;
mCallbackCookie = user;
LOG_FUNCTION_NAME_EXIT
}
mRequestMemory = get_memory;這裡就是callback的註冊,關注前一篇文章,在interface中有註冊實現這個callback。
看看這個callback的實現:
static camera_memory_t* __get_memory(int fd, size_t buf_size, uint_t num_bufs,
void *user __attribute__((unused)))
{
CameraHeapMemory *mem;
if (fd < 0)
mem = new CameraHeapMemory(buf_size, num_bufs);
else
mem = new CameraHeapMemory(fd, buf_size, num_bufs);
mem->incStrong(mem);
return &mem->handle;
}
簡單理解是返回了一個camera_memory_t的地址。
仔細跟蹤一下,我們傳進去的fd是-1,所以會呼叫到:
mem = new CameraHeapMemory(buf_size, num_bufs);
CameraHeapMemory這個類也是在interface中實現的看看它對應的建構函式:
CameraHeapMemory(size_t buf_size, uint_t num_buffers = 1) :
mBufSize(buf_size),
mNumBufs(num_buffers)
{
mHeap = new MemoryHeapBase(buf_size * num_buffers);
commonInitialization();
}
繼續看MemoryHeapBase:
MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
: mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
mDevice(0), mNeedUnmap(false), mOffset(0)
{
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
if (fd >= 0) {
if (mapfd(fd, size) == NO_ERROR) {
if (flags & READ_ONLY) {
ashmem_set_prot_region(fd, PROT_READ);
}
}
}
}
這裡可以看出是用了匿名共享的機制了,看看這個介面就知道了:
int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
int ashmem_create_region(const char *name, size_t size)
{
int fd, ret;
fd = open(ASHMEM_DEVICE, O_RDWR);
if (fd < 0)
return fd;
if (name) {
char buf[ASHMEM_NAME_LEN];
strlcpy(buf, name, sizeof(buf));
ret = ioctl(fd, ASHMEM_SET_NAME, buf);
if (ret < 0)
goto error;
}
ret = ioctl(fd, ASHMEM_SET_SIZE, size);
if (ret < 0)
goto error;
return fd;
error:
close(fd);
return ret;
}
以上是開啟一個ashem裝置,接下來開始對映:
status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
{
if (size == 0) {
// try to figure out the size automatically
#ifdef HAVE_ANDROID_OS
// first try the PMEM ioctl
pmem_region reg;
int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®);
if (err == 0)
size = reg.len;
#endif
if (size == 0) { // try fstat
struct stat sb;
if (fstat(fd, &sb) == 0)
size = sb.st_size;
}
// if it didn't work, let mmap() fail.
}
if ((mFlags & DONT_MAP_LOCALLY) == 0) {
void* base = (uint8_t*)mmap(0, size,
PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
if (base == MAP_FAILED) {
ALOGE("mmap(fd=%d, size=%u) failed (%s)",
fd, uint32_t(size), strerror(errno));
close(fd);
return -errno;
}
//ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size);
mBase = base;
mNeedUnmap = true;
} else {
mBase = 0; // not MAP_FAILED
mNeedUnmap = false;
}
mFD = fd;
mSize = size;
mOffset = offset;
return NO_ERROR;
}
留意下,把映射出來的記憶體地址儲存到成員變數mBase中,而獲取這個地址只需要呼叫成員函式getBase即可:
void* MemoryHeapBase::getBase() const {
return mBase;
}
回到之前的建構函式
CameraHeapMemory(size_t buf_size, uint_t num_buffers = 1) :
mBufSize(buf_size),
mNumBufs(num_buffers)
{
mHeap = new MemoryHeapBase(buf_size * num_buffers);
commonInitialization();
}
接下來執行:
commonInitialization();
void commonInitialization()
{
handle.data = mHeap->base();
handle.size = mBufSize * mNumBufs;
handle.handle = this;
mBuffers = new sp<MemoryBase>[mNumBufs];
for (uint_t i = 0; i < mNumBufs; i++)
mBuffers[i] = new MemoryBase(mHeap,
i * mBufSize,
mBufSize);
handle.release = __put_memory;
}
handle.data = mHeap->base();這一操作就是把剛才申請到的匿名共享記憶體地址複製給camera資料結構體中data, handle.handle = this;這裡是給handle賦值控制代碼,相當於這個結構體的本地this指標。
mBuffers[i] = new MemoryBase(mHeap,
i * mBufSize,
mBufSize);
就是在分配好可用的記憶體塊裡面申請一塊記憶體,返回地址IMemory類。
就這樣到了cameraclient層,根據上一篇章的回撥過程,這個memory也會不斷變化,不過簡單點來說就是MemoryBase和MamoryHeapBase兩個型別拆了又封裝,重複的操作。針對跨程序的記憶體共享,android也使用了binder機制一套框架MemoryBase和MamoryHeapBase來控制操作camera的記憶體。整個記憶體的操作過程可以用下面一張圖來解釋一下:
圖片也就表達了一下意思,具體過程還是需要程式碼仔細跟一下,不過由於沒有什麼特別的操作,跟著上一篇的回撥過程,分析資料變化,很快就能清楚了。直到JNI層,才會把資料拆成位元組流傳到java上面,之前的一些列都把data資料流封裝的很嚴實。