Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android4.4 Camera 數據流分析

Android4.4 Camera 數據流分析

編輯:關於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數據流封裝的很嚴實。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved