清空v4l2 usbcamera缓存

做过usbcamera的同学们应该都知道,usbcamera有一个缓存队列,当应用上面调用startPreview的时候,就会层层的调到usbcamera 的ioctl(mFd, VIDIOC_DQBUF, &tmp_buf);用于从队列里出去一个缓存,并将这个缓存返回给用户去处理。当处理完后,要接着调用ioctl (mFd, VIDIOC_QBUF, &tmp_buf);来将刚刚出队的这个缓存重新放回队列。否则会导致队列里的缓存不够而读不出任何东西来,表现为黑屏。

usbcamera提供了一系列的ioctl来操作视频流的处理,但没有提供清空缓存队列这个功能。如果刚好某个应用,只open了usbcamera一次,然后就重复调用startPreview和stopPreview,这个时候就会出现一个问题。

即当用户在stopPreview的时候,最后一次读取的这个缓存队列(假设有8个缓存buf)里的buf的id为1,也就是只读了这个队列的第1个,还有剩下的7个缓存里面的数据没有被读出来。而这个时候上层用户调用了stopPreview,停止调用缓存数据。那么就会出,下次调用startPreview的时候,会先将这剩下的7个buf读出来的情况。因为我们的队列是先进先出的,你上次stop的时候出了一个,剩下的7个自然又自动往前排了,坐等下次取出来。

以倒车为例,上次司机倒车完后,下次如果再启动倒车,再调用startPreview的时候,首先会显示上次倒车时最后的影像,然后才会接着显示新的影像。

要解决这个BUG,首先想到的是要将这个队列给清空。蛋疼的是,usbcamera没有清空的ioctl操作。不过我们可以自己想办法来模拟清空的操作。比如我们在启动倒车前,先循环将这个缓存里的Buf出队,然后再入队。这么一来,入队的buf,录的自然就是新的影像了。上代码:

        //倒车前,先清掉缓存里残留的上次倒车最后画面
        if(status == UVC_PARK)
        {
		   int numBufs = 0;
		   int result = 0;
	 	   struct v4l2_buffer tmp_buf ;   
#if V4L2DEVICE_FPS_LIMIT > 0
			   struct timeval prevTime;
			   gettimeofday(&prevTime, NULL);
#endif		   
	 	   for (numBufs = 0; numBufs < V4L2DEVICE_BUF_COUNT; numBufs++)
	 	   {
	 		   memset( &tmp_buf, 0, sizeof(tmp_buf) );
	 		   tmp_buf.type =  V4L2_BUF_TYPE_VIDEO_CAPTURE ;   
			   tmp_buf.memory = V4L2_MEMORY_MMAP ;	 
			   tmp_buf.index = 0;
 
			   
			   result = ioctl(mFd, VIDIOC_DQBUF, &tmp_buf);
			   if ( result != 0)
			   {
					ALOGD("v4l2UvcStatusUpdate VIDIOC_DQBUF error (id=%u): %s (%d)", numBufs, strerror(errno), errno);
				//   return;
			   }
			   else
			   {
			       ALOGD("v4l2UvcStatusUpdate VIDIOC_DQBUF done numBufs is %d, index is %d ", numBufs, tmp_buf.index);   
			   }
			   
			   
			   result = ioctl (mFd, VIDIOC_QBUF, &tmp_buf);
			   if ( result < 0)
			   {
				   ALOGD("v4l2UvcStatusUpdate Could not VIDIOC_QBUF %d: %s (%d)", numBufs, strerror(errno), errno);
			   }
			   else
			   {
			       ALOGD("v4l2UvcStatusUpdate VIDIOC_QBUF done numBufs is %d ", numBufs);
			   }
#if V4L2DEVICE_FPS_LIMIT > 0
               double  step = 1000*1000/V4L2DEVICE_FPS_LIMIT;//微秒
			   struct timeval nowTime;
			   gettimeofday(&nowTime, NULL);
			   double  interval = (nowTime.tv_usec - prevTime.tv_usec);
			   ALOGD("v4l2UvcStatusUpdate startTime is [%ld] , nowTime is [%ld] \n", prevTime.tv_usec, nowTime.tv_usec);
			   ALOGD("v4l2UvcStatusUpdate FPS is [%d] , interval time is [%lf] , step is %lf\n", V4L2DEVICE_FPS_LIMIT, interval, step);   
			   //如果是放在缓存里,两次取缓存的时间间隔会非常的短,都在1000微秒以内。而如果是正常读取队列的话,会在30*1000到50*1000之间。
			   //所以下面用一个正常时的间隔的一半值来做阈值就足够了。
			   if(interval >= step/2)
			   {
			       ALOGD("v4l2UvcStatusUpdate this frame is not buff ");
			       break;
			   }
			   prevTime = nowTime;
#endif
	 	   	}
 
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值