解决使用uvcCamera库传输数据时遇到的oom问题

项目需求:

        应用端能直接获取h264格式的4k图像数据,但是谷歌没有提供获取h264格式图像的接口,于是只能使用uvcCamera私有库来解决该问题。

项目难点:

前提:根据uvc协议,当相机的interface→num_altsetting > 1时,会走同步传输来传输数据,速率为24MB/s;当interface→num_altsetting = 1时,会走bulk传输来传送视频数据,速率为48MB/s。因为是传输4k压缩数据,iso传输可能带宽不够,所以所使用的相机选择了bulk传输urb数据。

当使用uvcCamera私有库的时候,出现了如下报错:

进入代码找到uvc_stream_start_bandwidth(位于stream.c中)位置,如上所述,在该函数中,会先判断interface→num_altsetting值,然后根据该值使用bulk进行传输。

先封装需要提交的transfer:

for (transfer_id = 0; transfer_id < LIBUVC_NUM_TRANSFER_BUFS; ++transfer_id) {
			transfer = libusb_alloc_transfer(0);
			strmh->transfers[transfer_id] = transfer;
			strmh->transfer_bufs[transfer_id] = malloc(strmh->cur_ctrl.dwMaxPayloadTransferSize);
			libusb_fill_bulk_transfer(transfer, strmh->devh->usb_devh,
				format_desc->parent->bEndpointAddress,
				strmh->transfer_bufs[transfer_id],
				strmh->cur_ctrl.dwMaxPayloadTransferSize, _uvc_stream_callback,
				(void *)strmh, 5000);
		}

提交刚才封装好的transfer:

MARK("submit transfers");
	for (transfer_id = 0; transfer_id < LIBUVC_NUM_TRANSFER_BUFS; transfer_id++) {
		ret = libusb_submit_transfer(strmh->transfers[transfer_id]);
		if (UNLIKELY(ret != UVC_SUCCESS)) {
			UVC_DEBUG("libusb_submit_transfer failed");
			__android_log_print(ANDROID_LOG_ERROR,LOG_TAG, "yilan uvc_stream_start_bandwidth libusb_submit_transfer failed errno = %d, %s", errno, strerror(errno));
			break;
		}
	}

这里的返回值是-1,也就是LIBUSB_ERROR_IO错误。

 来看看libusb_submit_transfer的具体实现:

 函数的核心内容在红色方框内,因此追进去看下,由于电视是android设备,因此我们一路跟到android设备下的submit_transfer方法中:

 找到返回LIBUSB_ERROR_IO的地方:

 

显然,这里是通过ioctl的方式与linux内核进行通信,因此我们再进去内核中继续追踪。由于ioctl的命令是IOCTL_USBFS_SUBMITURB,我们直接在内核代码的根目录下搜寻相应的字符,发现没找到。因为ioctl命令可能在内核中进行了相应的重定义,但是关键字符应该是相同的,于是就在搜索SUBMITURB时搜到了关键线索:

 然后就是追着submit关键字继续查找,发现带有submit关键字的函数最终都会执行proc_do_submiturb函数,于是我在该函数的头部加了log,而该log也确实打印了,因此可以确定是这个函数里爆出问题,报错函数为usbfs_increase_memory_usage:

 

 可以看到内核这边为这些transfer提供的最大内存为16<<20,也就是16,777,216字节,camera那边申请的payload大小为固定值width*height*1.5,当申请4k数据时,由于会超过最大值4m,因此payload的大小被固定成4m(4194304字节),也就是一个tranfer对应的buffer大小,对应内核的大小为4202784(也就是amount的大小,从上述截图可以看到amount的计算方式)。接下来,我们再回过头看看进行libusb_submit_transfer地方:

其中LIBUVC_NUM_TRANSFER_BUFS定义为10。因此需要内核这边需要分配(10*4202784)字节的空间,对应usbfs_memory_mb的值为:(10*4202784)>> 20 = 40。我以为修改成40以后就万事大吉,可是usbfs_memory_mb设置到33的时候,电话会自动重启。于是我将LIBUVC_NUM_TRANSFER_BUFS的值改成了6(这里主要怕改太小影响libusb传输,中立值选择了6),usbfs_memory_mb的值改到24-32之间,就可以满足要求。

ps:这里需要吐槽一下,因为uvc需要先分配内存才能开流,因此我们不能根据每次提交payload的大小来分配相应的内存。只能先开辟一块内存来存储相应的payload。mjpg的压缩比差不多为0.3,h264的压缩比相较mjpg更高,因此分配h*w*0.3足以,如果需要带上深度信息,可以额外再加上点深度信息对应的内存,也不至于分配w*h*1.5这么大的内存。通过抓取一千张图片,我发现基本上0.3的压缩比已经足够满足。

 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值