編輯:關於Android編程
開門見山:
這裡給出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[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數據流封裝的很嚴實。
什麼是RecyclerView關於RecyclerView,是一個主要用於展示和回收View的有一個控件,在官用了一句話來概括RecyclerView 是一種通過提供有限
很多朋友在啟動應用或者玩游戲的時候都出現Google Play服務已停止運行,或者之前運行正常的應用突然不能使用了。這是為什麼?在這我們就來講解一下Goog
android給我們提供了一個spinner控件,這個控件主要就是一個列表,那麼我們就來說說這個控件吧,這個控件在以前的也看見過,但今天還是從新介紹一遍吧。Spinner
上一篇關於Android中ListView的介紹講的是如何制作一個具有兩行文本的自定義控件,作為ListView的Item的使用方法。本文接下來也是圍繞ListView和