Android UVCCamera/libuvc遇到的卡死崩溃bug和解决方法

Android UVCCamera/libuvc遇到的卡死崩溃bug和解决方法

分享下debug uvccamera/libuvc这些so库遇到的卡死崩溃问题和思路


1.UVCCamera执行stopPreview或release时阻塞卡死

在多次重复打开关闭uvc摄像头的时候,上层调用stopPreview/release会概率性卡死,经过调试一步步定位调用函数和阻塞住的位置

android java层调用略,下面直接从so库的代码开始;
–>
UVCCamera/UVCCamera.cpp-stopPreview()
–>
UVCCamera/UVCPreView.cpp-stopPreview()
这里设置mIsRunning = false;停止预览帧处理;
调试发现,在下面这一句代码阻塞住没有往下走了:
pthread_join(preview_thread, NULL) != EXIT_SUCCESS;
pthread_join会阻塞到preview_thread预览线程执行结束为止,所以预览处理没有正常结束;
–>
UVCCamera/UVCPreview.cpp-do_preview()
结束预览时会停止libuvc的流处理uvc_stop_streaming(mDeviceHandle);,所以流处理没有正常结束;
–>
libuvc/stream.c-uvc_stop_streaming()
–>
libuvc/stream.c-uvc_stream_close()
–>
libuvc/stream.c-uvc_stream_stop()
这一步停止流的传输,并将所有流转换器都释放掉:
strmh->running = 0;并遍历调用libusb_cancel_transfer(strmh->transfers[i]);
至于关键的阻塞代码如下:

1
2
3
4
5
6
7
8
9
10
/* Wait for transfers to complete/cancel */
for (; 1 ;) {
for (i = 0; i < LIBUVC_NUM_TRANSFER_BUFS; i++) {
if (strmh->transfers[i] != NULL)
break;
}
if (i == LIBUVC_NUM_TRANSFER_BUFS)
break;
pthread_cond_wait(&strmh->cb_cond, &strmh->cb_mutex);
}

这里会一直循环等待所有流转换器都释放置空完成,而问题就在于偏偏有个别流转换器就是无法正常释放。

为什么没有正常释放完成,
是因为libusb_cancel_transfer失败返回LIBUSB_ERROR_NOT_FOUND的状态时,
转换器持有无效的buffer和指针,导致上面的循环代码一直不往下走。

一般释放正常时的Log如下:

1
2
3
4
[4632*stream.c:989:_uvc_stream_callback]:not retrying transfer, status = 3
[4632*stream.c:629:_uvc_delete_transfer]:begin
[4632*stream.c:642:_uvc_delete_transfer]:Freeing transfer 5 (0x72fdbe5960)
[4632*stream.c:656:_uvc_delete_transfer]:end

但函数阻塞住时,我发现并没有看到index为6的transfer执行delete的Log,于是我怀疑是否跟transfer的数量有关。

这时查看源码里有一个transfer buffers的数量定义如下:

1
2
3
4
5
6
7
8
9
10
libuvc/libuvc_internal.h
/*
set a high number of transfer buffers. This uses a lot of ram, but
avoids problems with scheduling delays on slow boards causing missed
transfers. A better approach may be to make the transfer thread FIFO
scheduled (if we have root).
We could/should change this to allow reduce it to, say, 5 by default
and then allow the user to change the number of buffers as required.
*/
#define LIBUVC_NUM_TRANSFER_BUFS 10

如果设置得太高,不仅会提高内存的占用,在处理速度慢的板子上也会有transfer浪费掉。
这里我尝试降低数量值为5,再次调试开关100次,stopPreview卡死的问题不再复现。

2.JNI DETECTED ERROR IN APPLICATION: jmethodID was NULL 空指针

由于java上层设置setFrameCallback(null, 0); iframecallback_fields.onFrame在另外一个线程被置为NULL
而以下这段代码没有判空,导致的空指针:
env->CallVoidMethod(mFrameCallbackObj, iframecallback_fields.onFrame, buf);

解决方法为增加空指针判断如下

1
2
3
4
5
6
7
void UVCPreview::do_capture_callback(JNIEnv *env, uvc_frame_t *frame){
...
+if (iframecallback_fields.onFrame != NULL) {
env->CallVoidMethod(mFrameCallbackObj, iframecallback_fields.onFrame, buf);
+}
...
}

3.UVCCamera could not open camera:err=-99

要检查打开的usb设备是否已经被其他进程打开了/其他进程持有没释放/打开的是不是UVC摄像头,是否打开了错误的其他usb设备

4.其他一些别人发现的bug和修复方法

https://github.com/saki4510t/UVCCamera/pull/520
https://github.com/saki4510t/UVCCamera/pull/328

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×