Android4.2.2的preview的数据流和控制流以及最终的预览显示

Android源码版本Version:4.2.2; 硬件平台 全志A31

step1:之前在讲到CameraService处的setPreviewWindow中传入一个窗口给HAL

[cpp]  view plain  copy
  1. status_t setPreviewWindow(const sp<ANativeWindow>& buf)  
  2. {  
  3.     ALOGV("%s(%s) buf %p", __FUNCTION__, mName.string(), buf.get());  
  4.   
  5.     if (mDevice->ops->set_preview_window) {  
  6.         mPreviewWindow = buf;  
  7.         mHalPreviewWindow.user = this;  
  8.         ALOGV("%s &mHalPreviewWindow %p mHalPreviewWindow.user %p", __FUNCTION__,  
  9.                 &mHalPreviewWindow, mHalPreviewWindow.user);  
  10.         return mDevice->ops->set_preview_window(mDevice,  
  11.                 buf.get() ? &mHalPreviewWindow.nw : 0);//调用底层硬件hal接口  
  12.     }  
  13.     return INVALID_OPERATION;  
  14. }  

传入给HAL的参数buf为一个Surface,还有一个变量是关于预览窗口的数据流操作nw

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. struct camera_preview_window {  
  2.     struct preview_stream_ops nw;  
  3.     void *user;  
  4. };  

该变量的初始如下:这些函数接口看上去很熟悉,的确在SurfaceFlinger中,客户端的Surface也就是通过这些接口来向SurfaceFlinger申请图形缓存并处理图形缓存显示的,只是之前的操作都交个了OpenGL ES的eglswapbuf()来对这个本地窗口进行如下的dequeueBuffer和enqueuebuffer的操作而已。而在Camera的预览中,这些操作将手动完成。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void initHalPreviewWindow()  
  2. {  
  3.     mHalPreviewWindow.nw.cancel_buffer = __cancel_buffer;  
  4.     mHalPreviewWindow.nw.lock_buffer = __lock_buffer;  
  5.     mHalPreviewWindow.nw.dequeue_buffer = __dequeue_buffer;  
  6.     mHalPreviewWindow.nw.enqueue_buffer = __enqueue_buffer;  
  7.     mHalPreviewWindow.nw.set_buffer_count = __set_buffer_count;  
  8.     mHalPreviewWindow.nw.set_buffers_geometry = __set_buffers_geometry;  
  9.     mHalPreviewWindow.nw.set_crop = __set_crop;  
  10.     mHalPreviewWindow.nw.set_timestamp = __set_timestamp;  
  11.     mHalPreviewWindow.nw.set_usage = __set_usage;  
  12.     mHalPreviewWindow.nw.set_swap_interval = __set_swap_interval;  
  13.   
  14.     mHalPreviewWindow.nw.get_min_undequeued_buffer_count =  
  15.             __get_min_undequeued_buffer_count;  
  16. }  

step2.继续前面的preview的处理操作,在CameraService处的CameraClinet已经调用了CameraHardwareInterface的startPreview函数,实际就是操作HAL处的Camera设备如下

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. status_t startPreview()  
  2. {  
  3.     ALOGV("%s(%s)", __FUNCTION__, mName.string());  
  4.     if (mDevice->ops->start_preview)  
  5.         return mDevice->ops->start_preview(mDevice);  
  6.     return INVALID_OPERATION;  
  7. }  


step3.进入HAL来看Preview的处理

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.       
  2. status_t CameraHardware::doStartPreview(){  
  3. ...........  
  4. res = camera_dev->startDevice(mCaptureWidth, mCaptureHeight, org_fmt, video_hint);//启动设备  
  5. ......  
  6. }  

调用V4L2设备来启动视频流的采集,startDevice()函数更好的解释了预览的启动也就是视频采集的启动。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. status_t V4L2CameraDevice::startDevice(int width,  
  2.                                        int height,  
  3.                                        uint32_t pix_fmt,  
  4.                                        bool video_hint)  
  5. {  
  6.     LOGD("%s, wxh: %dx%d, fmt: %d", __FUNCTION__, width, height, pix_fmt);  
  7.       
  8.     Mutex::Autolock locker(&mObjectLock);  
  9.       
  10.     if (!isConnected())   
  11.     {  
  12.         LOGE("%s: camera device is not connected.", __FUNCTION__);  
  13.         return EINVAL;  
  14.     }  
  15.       
  16.     if (isStarted())   
  17.     {  
  18.         LOGE("%s: camera device is already started.", __FUNCTION__);  
  19.         return EINVAL;  
  20.     }  
  21.   
  22.     // VE encoder need this format  
  23.     mVideoFormat = pix_fmt;  
  24.     mCurrentV4l2buf = NULL;  
  25.   
  26.     mVideoHint = video_hint;  
  27.     mCanBeDisconnected = false;  
  28.   
  29.     // set capture mode and fps  
  30.     // CHECK_NO_ERROR(v4l2setCaptureParams());  // do not check this error  
  31.     v4l2setCaptureParams();  
  32.       
  33.     // set v4l2 device parameters, it maybe change the value of mFrameWidth and mFrameHeight.  
  34.     CHECK_NO_ERROR(v4l2SetVideoParams(width, height, pix_fmt));  
  35.       
  36.     // v4l2 request buffers  
  37.     int buf_cnt = (mTakePictureState == TAKE_PICTURE_NORMAL) ? 1 : NB_BUFFER;  
  38.     CHECK_NO_ERROR(v4l2ReqBufs(&buf_cnt));//buf申请  
  39.     mBufferCnt = buf_cnt;  
  40.   
  41.     // v4l2 query buffers  
  42.     CHECK_NO_ERROR(v4l2QueryBuf());//buffer的query,完成mmap等操作  
  43.       
  44.     // stream on the v4l2 device  
  45.     CHECK_NO_ERROR(v4l2StartStreaming());//启动视频流采集  
  46.   
  47.     mCameraDeviceState = STATE_STARTED;  
  48.   
  49.     mContinuousPictureAfter = 1000000 / 10;  
  50.     mFaceDectectAfter = 1000000 / 15;  
  51.     mPreviewAfter = 1000000 / 24;  
  52.       
  53.     return NO_ERROR;  
  54. }  

这个是完全参考了V4L2的视频采集处理流程:

1.v4l2setCaptureParams()设置采集的相关参数;

2.v4l2QueryBuf():获取内核图像缓存的信息,并将所有的内核图像缓存映射到当前的进程中来。方便用户空间的处理

3.v4l2StartStreaming():开启V4L2的视频采集流程。

step4: 图像采集线程bool V4L2CameraDevice::captureThread();

该函数的内容比较复杂,但核心是 ret = getPreviewFrame(&buf),获取当前一帧图像:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int V4L2CameraDevice::getPreviewFrame(v4l2_buffer *buf)  
  2. {  
  3.     int ret = UNKNOWN_ERROR;  
  4.       
  5.     buf->type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
  6.     buf->memory = V4L2_MEMORY_MMAP;   
  7.    
  8.     ret = ioctl(mCameraFd, VIDIOC_DQBUF, buf); //获取一帧数据  
  9.     if (ret < 0)   
  10.     {   
  11.         LOGW("GetPreviewFrame: VIDIOC_DQBUF Failed, %s", strerror(errno));   
  12.         return __LINE__;            // can not return false  
  13.     }  
  14.   
  15.     return OK;  
  16. }  

调用了典型的VIDIOC_DQBUF命令,出列一帧图形缓存,提取到用户空间供显示。


当前的平台通过定义一个V4L2BUF_t结构体来表示当前采集到的一帧图像,分别记录到Y和C所在的物理地址和用户空间的虚拟地址。虚拟地址是对内核采集缓存的映射

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. typedef struct V4L2BUF_t  
  2. {  
  3.     unsigned int    addrPhyY;       // physical Y address of this frame  
  4.     unsigned int    addrPhyC;       // physical Y address of this frame  
  5.     unsigned int    addrVirY;       // virtual Y address of this frame  
  6.     unsigned int    addrVirC;       // virtual Y address of this frame  
  7.     unsigned int    width;  
  8.     unsigned int    height;  
  9.     int             index;          // DQUE id number  
  10.     long long       timeStamp;      // time stamp of this frame  
  11.     RECT_t          crop_rect;  
  12.     int             format;  
  13.     void*           overlay_info;  
  14.       
  15.     // thumb   
  16.     unsigned char   isThumbAvailable;  
  17.     unsigned char   thumbUsedForPreview;  
  18.     unsigned char   thumbUsedForPhoto;  
  19.     unsigned char   thumbUsedForVideo;  
  20.     unsigned int    thumbAddrPhyY;      // physical Y address of thumb buffer  
  21.     unsigned int    thumbAddrVirY;      // virtual Y address of thumb buffer  
  22.     unsigned int    thumbWidth;  
  23.     unsigned int    thumbHeight;  
  24.     RECT_t          thumb_crop_rect;  
  25.     int             thumbFormat;  
  26.       
  27.     int             refCnt;         // used for releasing this frame  
  28.     unsigned int    bytesused;      // used by compressed source  
  29. }V4L2BUF_t;  

来看看该结构体的初始化代码:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. V4L2BUF_t v4l2_buf;  
  2. if (mVideoFormat != V4L2_PIX_FMT_YUYV  
  3.     && mCaptureFormat == V4L2_PIX_FMT_YUYV)  
  4. {  
  5.     v4l2_buf.addrPhyY       = mVideoBuffer.buf_phy_addr[buf.index];   
  6.     v4l2_buf.addrVirY       = mVideoBuffer.buf_vir_addr[buf.index];   
  7. }  
  8. else  
  9. {  
  10.     v4l2_buf.addrPhyY       = buf.m.offset & 0x0fffffff;//内核物理地址  
  11.     v4l2_buf.addrVirY       = (unsigned int)mMapMem.mem[buf.index];//虚拟地址  
  12. }  
  13. v4l2_buf.index              = buf.index;//内部采集缓存的索引  
  14. v4l2_buf.timeStamp          = mCurFrameTimestamp;  
  15. v4l2_buf.width              = mFrameWidth;  
  16. v4l2_buf.height             = mFrameHeight;  
  17. v4l2_buf.crop_rect.left     = mRectCrop.left;  
  18. v4l2_buf.crop_rect.top      = mRectCrop.top;  
  19. v4l2_buf.crop_rect.width    = mRectCrop.right - mRectCrop.left + 1;  
  20. v4l2_buf.crop_rect.height   = mRectCrop.bottom - mRectCrop.top + 1;  
  21. v4l2_buf.format             = mVideoFormat;  

addrPhy和addrViry分别记录到Y和C所在的物理地址和用户空间的虚拟地址。而这个地址都是通过当前Buf的index直接设置的,为什么?因为内核的图像缓存区的mmap操作将每一个缓存,以其Index分别逐一的映射到了用户空间,并记录缓存的物理和虚拟地址,而这主要是方便后续图像的显示而已。

 

step5:bool V4L2CameraDevice::previewThread()//预览线程
获得了一帧数据必须通知预览线程进行图像的显示,采集线程和显示线程之间通过pthread_cond_wait(&mPreviewCond, &mPreviewMutex);进程间锁进行等待。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bool V4L2CameraDevice::previewThread()//预览线程  
  2. {  
  3.     V4L2BUF_t * pbuf = (V4L2BUF_t *)OSAL_Dequeue(&mQueueBufferPreview);//获取预览帧buffer信息  
  4.     if (pbuf == NULL)  
  5.     {  
  6.         // LOGV("picture queue no buffer, sleep...");  
  7.         pthread_mutex_lock(&mPreviewMutex);  
  8.         pthread_cond_wait(&mPreviewCond, &mPreviewMutex);//等待  
  9.         pthread_mutex_unlock(&mPreviewMutex);  
  10.         return true;  
  11.     }  
  12.   
  13.     Mutex::Autolock locker(&mObjectLock);  
  14.     if (mMapMem.mem[pbuf->index] == NULL  
  15.         || pbuf->addrPhyY == 0)  
  16.     {  
  17.         LOGV("preview buffer have been released...");  
  18.         return true;  
  19.     }  
  20.   
  21.     // callback  
  22.     mCallbackNotifier->onNextFrameAvailable((void*)pbuf, mUseHwEncoder);//回调采集到帧数据  
  23.   
  24.     // preview  
  25.     if (isPreviewTime())//预览  
  26.     {  
  27.         mPreviewWindow->onNextFrameAvailable((void*)pbuf);//帧可以显示  
  28.     }  
  29.   
  30.     // LOGD("preview id : %d", pbuf->index);  
  31.   
  32.     releasePreviewFrame(pbuf->index);  
  33.   
  34.     return true;  
  35. }  

预览线程主要做了两件事,一是完成图像缓存数据的回调供最最上层的使用;另一件当然是送显。

 

step6:预览线程如何显示?

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bool PreviewWindow::onNextFrameAvailable(const void* frame)//使用本地窗口surface 进行初始化  
  2. {  
  3.     int res;  
  4.     Mutex::Autolock locker(&mObjectLock);  
  5.   
  6.     V4L2BUF_t * pv4l2_buf = (V4L2BUF_t *)frame;//一帧图像所在的地址信息  
  7. ......  
  8.         res = mPreviewWindow->set_buffers_geometry(mPreviewWindow,  
  9.                                                    mPreviewFrameWidth,  
  10.                                                    mPreviewFrameHeight,  
  11.                                format);//设置本地窗口的buffer的几何熟悉  
  12. ......  
  13.     res = mPreviewWindow->dequeue_buffer(mPreviewWindow, &buffer, &stride);//申请SF进行bufferqueue的图形缓存操作。返回当前进程地址到buffer  
  14. ..................  
  15.     res = grbuffer_mapper.lock(*buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, rect, &img);//把映射回来的buffer信息中的地址放到img中,用来填充  
  16. .............  
  17.     mPreviewWindow->enqueue_buffer(mPreviewWindow, buffer);//交由surfaceFlinger去做显示  
  18. ............  
  19. }  

上述代码实时了本地窗口图像向SurfaceFlinger的投递,为何这么说,看下面的分析:

1.PreviewWindow类里的mPreviewWindow成员变量是什么?

这个是从应用端的setPreviewDisplay()设置过来的,传入到HAL的地方在CameraHardwareInterface的initialize函数里:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.     return mDevice->ops->set_preview_window(mDevice,  
  2.             buf.get() ? &mHalPreviewWindow.nw : 0);//调用底层硬件hal接口  
  3. }  

nw的操作在step1里面已经有说明了,初始化相关的一些操作。

2.以dequeue_buffer为例:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static int __dequeue_buffer(struct preview_stream_ops* w,  
  2.                             buffer_handle_t** buffer, int *stride)  
  3. {  
  4.     int rc;  
  5.     ANativeWindow *a = anw(w);  
  6.     ANativeWindowBuffer* anb;  
  7.     rc = native_window_dequeue_buffer_and_wait(a, &anb);  
  8.     if (!rc) {  
  9.         *buffer = &anb->handle;  
  10.         *stride = anb->stride;  
  11.     }  
  12.     return rc;  
  13. }  

调用到本地的窗口,通过w获得ANativeWindow对象,来看看该宏的实现:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.     static ANativeWindow *__to_anw(void *user)  
  2.     {  
  3.         CameraHardwareInterface *__this =  
  4.                 reinterpret_cast<CameraHardwareInterface *>(user);  
  5.         return __this->mPreviewWindow.get();  
  6.     }  
  7. #define anw(n) __to_anw(((struct camera_preview_window *)n)->user)  

首先获取user对象为CameraHardwareInterface对象,通过它获得之前初始化的Surface对象即成员变量mPreviewWindow(属于本地窗口ANativeWindow类)。

 

3.本地窗口的操作

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,  
  2.         struct ANativeWindowBuffer** anb) {  
  3.     return anw->dequeueBuffer_DEPRECATED(anw, anb);  
  4. }  

上述的过程其实是调用应用层创建的Surface对象,该对象已经完全打包传递给了CameraService,来进行绘图和渲染的处理。如下所示:BpCamera

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // pass the buffered Surface to the camera service  
  2.  status_t setPreviewDisplay(const sp<Surface>& surface)  
  3.  {  
  4.      ALOGV("setPreviewDisplay");  
  5.      Parcel data, reply;  
  6.      data.writeInterfaceToken(ICamera::getInterfaceDescriptor());  
  7.      Surface::writeToParcel(surface, &data);//数据打包  
  8.      remote()->transact(SET_PREVIEW_DISPLAY, data, &reply);  
  9.      return reply.readInt32();  
  10.  }  

BnCamera处,内部实现了新建一个CameraService处的Surface,但是都是用客户端处的参数来初始化的。即两者再不同进程中,但所包含的信息完全一样。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. case SET_PREVIEW_DISPLAY: {  
  2.     ALOGV("SET_PREVIEW_DISPLAY");  
  3.     CHECK_INTERFACE(ICamera, data, reply);  
  4.     sp<Surface> surface = Surface::readFromParcel(data);  
  5.     reply->writeInt32(setPreviewDisplay(surface));//设置sueface  
  6.     return NO_ERROR;  
  7. break;  
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Surface::Surface(const Parcel& parcel, const sp<IBinder>& ref)  
  2.     : SurfaceTextureClient()  
  3. {  
  4.     mSurface = interface_cast<ISurface>(ref);  
  5.     sp<IBinder> st_binder(parcel.readStrongBinder());  
  6.     sp<ISurfaceTexture> st;  
  7.     if (st_binder != NULL) {  
  8.         st = interface_cast<ISurfaceTexture>(st_binder);  
  9.     } else if (mSurface != NULL) {  
  10.         st = mSurface->getSurfaceTexture();  
  11.     }  
  12.   
  13.     mIdentity   = parcel.readInt32();  
  14.     init(st);  
  15. }  

这里的Surface建立是通过mSurface来完成和SurfaceFlinger的通信的,因为之前Camera客户端处的Surface是和SurfaceFLinger进行Binder通信,现在要将原先的Bpxxx相关的写入到CameraService进一步和SurfaceFlinger做后续的Binder通信处理,如queueBuffer()处理中和SurfaceFlinger的Bufferqueue的通信等。

4.故anw->dequeueBuffer的函数就和之前的从Android Bootanimation理解SurfaceFlinger的客户端建立完全对应起来,而且完全一样,只是Bootanimation进程创建的Surface交给OpenGL Es来进行底层的比如dequeue(缓存申请,填充当前的buffer)和enqueue(入列渲染)的绘图操作而已,见Android4.2.2 SurfaceFlinger之图形缓存区申请与分配dequeueBuffer一文。具体的绘图就不在这里说明了。通过该方法已经和SurfaceFlinger建立起连接,最终交由其进行显示。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值