Android4.4 Camera 数据流分析

来源:互联网 发布:淘宝控股人是谁 编辑:程序博客网 时间:2024/05/21 21:48

开门见山:
这里给出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, &reg);        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数据流封装的很严实。

0 0
原创粉丝点击