設(shè)備外接usb攝像頭,進(jìn)行基本的預(yù)覽、拍照、錄像。相信有些同學(xué)在工作中有遇到類似的需求。
usb攝像頭在Android設(shè)備上,應(yīng)用的確很廣泛,我之前公司做的是車載產(chǎn)品,眾所周知,現(xiàn)在汽車上是安裝了越來越多的攝像頭,通過攝像頭采集的實時信息,能協(xié)助我們進(jìn)行更加安全的駕駛。而這些攝像頭,除了比較常見的mipi攝像頭,很多也開始用的usb攝像頭。
除了車載產(chǎn)品,類似安防、醫(yī)療等各方面,和監(jiān)控、視頻相關(guān)的很多領(lǐng)域,也都會用到usb攝像頭。
uvc camera?有camera不管你之前有沒用過,有沒遇到過,相信看完這篇文章,一定會帶給你不一樣的收獲。
這篇文章將從下面幾點展開講解:
一、什么是UVC?
二、UVCCamera開源項目?
三、開源項目集成?
四、遇到的問題及解決?
一、什么是UVC?
UVC全稱為USB Video Class,直接翻譯過來的意思就是:USB視頻類,它是一種專門為USB視頻捕獲設(shè)備定義的協(xié)議標(biāo)準(zhǔn)。
這個標(biāo)準(zhǔn)是Microsoft與另外幾家設(shè)備廠商聯(lián)合推出的為USB視頻捕獲設(shè)備定義的協(xié)議標(biāo)準(zhǔn),已經(jīng)成為USB org標(biāo)準(zhǔn)之一。
現(xiàn)在的主流操作系統(tǒng),都已提供UVC設(shè)備驅(qū)動,因此符合UVC規(guī)格的硬件設(shè)備在不需要安裝任何的驅(qū)動程序下即可在主機中正常使用。是的,目前Android系統(tǒng)已經(jīng)支持uvc設(shè)備。
小結(jié):
講到這里大家應(yīng)該有這么個概念了,uvc是一種協(xié)議,不同的設(shè)備可能會支持不同的協(xié)議。如果我們的usb攝像頭,需要在Android設(shè)備上獲得支持的話,那這個攝像頭就得是支持uvc協(xié)議的攝像頭。
二、UVCCamera開源項目?
https://github.com/saki4510t/UVCCamera
現(xiàn)在我們在網(wǎng)上搜索uvc camera相關(guān)的文章,能查找到的uvc camera相關(guān)的項目,可以毫不夸張的說,基本都是基于上面這個開源項目來改的,這個開源項目的確比較牛逼,而且類封裝的很好,代碼邏輯比較清晰,使用起來也是非常的方便,而且關(guān)于攝像頭基本的預(yù)覽、拍照、錄像功能都實現(xiàn)了,是個比較完整的工程項目。
我們通過git pull先把代碼拉到本地,導(dǎo)入到AndroidStudio中,(不通過git pull 也行,直接下載代碼也是可以的。github 網(wǎng)站在國內(nèi)不翻墻的話,可能有時訪問不了,如何訪問不了,大家也可以嘗試在gitee上去搜索這個項目下載)。
整個工程的目錄結(jié)構(gòu)如下圖所示。當(dāng)然導(dǎo)入的過程中,會遇到一些報錯的問題,其實主要是gradle版本的問題,導(dǎo)入報錯的問題,我們統(tǒng)一在這個文章后面再給大家做詳細(xì)的講解,包括遇到的問題以及是如何去解決這些問題的。

這個開源項目,除了sdk庫的源碼,作者還提供了8個demo。這8個demo的具體功能介紹如下:
1)USBCameraTest0
顯示如何使用SurfaceView來啟動/停止預(yù)覽。
2)USBCameraTest
顯示如何啟動/停止預(yù)覽。這與USBCameraTest0幾乎相同,
但是使用自定義的TextureView來顯示相機圖像而不是使用SurfaceView。
3)USBCameraTest2
演示如何使用MediaCodec編碼器將UVC相機(無音頻)的視頻記錄為.MP4文件。
此示例需要API>=18,因為MediaMuxer僅支持API>=18。
4)USBCameraTest3
演示如何將音頻(來自內(nèi)部麥克風(fēng))的視頻(來自UVC相機)錄制為.MP4文件。
這也顯示了幾種捕捉靜止圖像的方式。此示例可能最適用于您的定制應(yīng)用程序的基礎(chǔ)項目。
5)USBCameraTest4
顯示了訪問UVC相機并將視頻圖像保存到后臺服務(wù)的方式。
這是最復(fù)雜的示例之一,因為這需要使用AIDL的IPC。
6)USBCameraTest5
和USBCameraTest3幾乎相同,但使用IFrameCallback接口保存視頻圖像,
而不是使用來自MediaCodec編碼器的輸入Surface。
在大多數(shù)情況下,您不應(yīng)使用IFrameCallback來保存圖像,因為IFrameCallback比使用Surface要慢很多。
但是,如果您想獲取視頻幀數(shù)據(jù)并自行處理它們或?qū)⑺鼈冏鳛樽止?jié)緩沖區(qū)傳遞給其他外部庫,
則IFrameCallback將非常有用。
7)USBCameraTest6
這顯示了如何將視頻圖像分割為多個Surface。你可以在這個應(yīng)用程序中看到視頻圖像并排觀看。
這個例子還展示了如何使用EGL來渲染圖像。
如果您想在添加視覺效果/濾鏡效果后顯示視頻圖像,則此示例可能會對您有所幫助。
8)USBCameraTest7
這顯示了如何使用兩個攝像頭并顯示來自每個攝像頭的視頻圖像。這仍然是實驗性的,可能有一些問題。
9)usbCameraTest8
這顯示了如何設(shè)置/獲取uvc控件。目前這只支持亮度和對比度。
提供的demo,代碼邏輯都很清晰,大家可以根據(jù)自己的需求去看對應(yīng)的demo。這些demo包含了預(yù)覽、錄像、拍照這些基本的功能。關(guān)于調(diào)節(jié)亮度、對比度這點,可能是不同攝像頭的原因,我本地驗證了下,看實際上并沒有效果,如果有哪位同學(xué)后面試試到有效果的,歡迎給我留言,大家交流交流。
Demo7我們可以看到是一個支持2個攝像頭的Demo。有多攝像頭支持需求的,可以參考這個里面的邏輯。記得之前做車載的時候,公司內(nèi)部在高通8953上的一個預(yù)研項目,是掛了6個攝像頭,那會驅(qū)動的同事也是花了不少的時間去點亮這些攝像頭,現(xiàn)在想想,如果采用uvc攝像頭,再參考這個開源項目,那應(yīng)該很快就可以搞個簡單的demo出來。
三、開源項目UVCCaemra的編譯、集成?
UVCCamera的核心代碼都在libuvccamera里面了。
我們要在我們的項目工程中集成這個項目的話,需要2個東西,一個是so庫,一個可以調(diào)用的java sdk源碼。
從上面的截圖我們可以很清楚的看到,代碼里面主要是包含了jni和java兩大部分的內(nèi)容。編譯jni,就可以得到我們需要的so庫,java代碼可以打包成aar,或者之間直接把整個代碼復(fù)制到我們的工程目錄下,作為庫引用也是可以的。
1) so庫的編譯
現(xiàn)在so庫的編譯,已經(jīng)非常的方便了,如下圖所示,我們在as的Terminal終端界面,切到j(luò)ni目錄下,直接ndk-build,就可以生成我們需要的so庫文件了。

這里有個地方我們得注意下,就是我們需要Android 32位還是64位的庫文件,這個是在Application.mk里面配的,上面的截圖我已經(jīng)把Application.mk這個文件的位置圈出來了。如果是32位,這里邊APP_ABI的內(nèi)容修改為armeabi-v7a即可,64位則是arm64-v8a,其它平臺的類推。
2)打包aar
我們項目要集成這個開源項目,那肯定得提供java代碼我們才能調(diào)用。我這里采用的方式是,把UVCCamera的核心代碼(也就是不包含8個demo)的內(nèi)容,打包成aar,然后在我自己的工程目錄中引用打包好的aar.
打包成aar在AS里面操作也是非常簡單的。先貼下圖片。


從上面的截圖,我們可以看到,需要打包的module有2個,分別是libuvccamera和usbCameraCommon。按照截圖上的操作順序來,從1到3。先是點擊as界面右側(cè)的Gradle,在騰出的界面中,雙擊執(zhí)行assembleRelease,執(zhí)行完沒有啥報錯的話,在module的build output路徑下,就可以看到生成的aar文件了。
最后就是把生成的so庫文件,已經(jīng)生成的aar文件,都拷貝到我們自己工程的libs目錄下,導(dǎo)入到項目中使用即可。
3)將UVCCamera sdk集成到自己項目
通過上面的步驟,我們已經(jīng)成功的編譯出了so庫文件以及aar文件。下圖顯示的就是我們把生成的文件導(dǎo)入到我們自己的工程項目中。

四、demo小改動:錄像的同時,獲取實時yuv流?
關(guān)于uvcCamea的文章,我之前也寫過一篇,這里貼下地址,連同demo地址也一并給出,感興趣的同學(xué)也可以看看。
這個demo,除了基本的預(yù)覽、拍照、錄像功能,我根據(jù)自己的需求,還添加了個返回實時yuv流的接口。對有需求將實時流視頻流進(jìn)行類似人臉識別、上傳后臺之類的,相信能給你帶來幫助。
“一篇文章帶你了解Android Usb攝像頭”
http://www.itdecent.cn/p/35124f098c24
demo地址:
https://github.com/yorkZJC/UvcCameraDemo
關(guān)于我自己的這個demo,如果需要修改分辨率,統(tǒng)一在如下圖所示的MyConstants.java文件中修改即可。


五、遇到的問題及解決?
1)sdk、ndk配置?
第一步,我們需要把sdk、nkd先配置好,sdk相信很多同學(xué)都會配,另外就是這里需要用ndk-build來編譯so庫,所以ndk一定得配好,我本地的ndk版本是r17,相信這個ndk的版本影響不是很大。
ndk的配置,有2種方式,可以直接在local.properties文件種修改,也可以在視圖界面,Project Structure中選擇我們本地的nkd路徑來配置。下面截圖分別對應(yīng)的是這2種不同的修改方式,采用其中任意一種即可。


2)導(dǎo)入Android Studio,gradle版本配置?
下面是我遇到的一些問題,按照我的修改操作來,相信大家都能成功運行起來。
【error 1】
Caused by: org.apache.http.conn.HttpHostConnectException: Connect to maven.google.com:443
[maven.google.com/142.250.204.46] failed: Connection timed out: connect

【error2】
ERROR: The minSdk version should not be declared in the android manifest file. You can move the version from the manifest to the defaultConfig in the build.gradle file.
Remove minSdkVersion and sync project
Affected Modules: libuvccamera

【error3】
* What went wrong:
Execution failed for task ':libuvccamera:ndkBuild'.
> A problem occurred starting process 'command 'null/ndk-build.cmd''

Android NDK: The armeabi ABI is no longer supported. Use armeabi-v7a.
Android NDK: NDK Application 'local' targets unknown ABI(s): armeabi mips
D:/APPS/sdk/android-ndk-r17b/build//../build/core/setup-app.mk:79: *** Android NDK: Aborting . Stop.
2021-06-11 10:08:11.386 3105-3105/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.serenegiant.usbcameratest0, PID: 3105
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.serenegiant.usbcameratest0/com.serenegiant.usbcameratest0.MainActivity}: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3432)
我是通過git pull拉代碼到本地的,所以本地的每筆修改都可以通過git 來追蹤記錄到。關(guān)于編譯報錯的,我們來看下我一共是修改了哪些內(nèi)容。

上面截圖,我們看到一共是修改了5個地方。
i、工程根目錄下build.gradle的修改;
ii、libuvccamera/build.gradle的修改;
3)拔出usb攝像頭,crash異常導(dǎo)致應(yīng)用退出?
原生的庫文件,有個bug,就是在使用usb攝像頭的過程中,我們?nèi)グ褦z像頭拔掉,這時so庫有個crash,到導(dǎo)致我們的應(yīng)用直接異常退出。
這個問題,網(wǎng)上的其它大神已經(jīng)給出了解決方案,我這里貼下修改的地方。我自己也是親自修改驗證了。

diff --git a/libuvccamera/src/main/jni/libusb/libusb/os/android_usbfs.c b/libuvccamera/src/main/jni/libusb/libusb/os/android_usbfs.c
index 8626595..c4842c4 100644
--- a/libuvccamera/src/main/jni/libusb/libusb/os/android_usbfs.c
+++ b/libuvccamera/src/main/jni/libusb/libusb/os/android_usbfs.c
@@ -2726,6 +2726,12 @@ static int handle_iso_completion(struct libusb_device_handle *handle, // XXX add
usbi_mutex_lock(&itransfer->lock);
for (i = 0; i < num_urbs; i++) {
+ //+Add by york.zhou on 2021.05.19,fix issue app crash on remove usb device
+ if (tpriv->iso_urbs == NULL){
+ break;
+ }
+ //-Add by york.zhou on 2021.05.19,fix issue app crash on remove usb device
+
if (urb == tpriv->iso_urbs[i]) {
urb_idx = i + 1;
break;
diff --git a/libuvccamera/src/main/jni/libuvc/src/stream.c b/libuvccamera/src/main/jni/libuvc/src/stream.c
index 8a1e90a..b7cedcc 100644
--- a/libuvccamera/src/main/jni/libuvc/src/stream.c
+++ b/libuvccamera/src/main/jni/libuvc/src/stream.c
@@ -641,7 +641,8 @@ static void _uvc_delete_transfer(struct libusb_transfer *transfer) {
libusb_cancel_transfer(strmh->transfers[i]); // XXX 20141112追加
UVC_DEBUG("Freeing transfer %d (%p)", i, transfer);
free(transfer->buffer);
- libusb_free_transfer(transfer);
+ //+Add york.zhou 2021.05-19,fix remove usb devices,app crash
+ //libusb_free_transfer(transfer);
strmh->transfers[i] = NULL;
break;
}
4)有些usb攝像頭識別不了?
有些同學(xué)可能還會遇到有些usb攝像頭識別不了的問題。這里面有個前提,就是確認(rèn)這個usb攝像頭,插到電腦上是能正常識別使用的,只是插到我們的設(shè)備上,識別不了。
遇到這種問題,可以抓個完整的logcat日志,然后在日志中全局搜索subclass,將搜索到的subclass相關(guān)信息,按照下面截圖的格式,在xml 目錄下的device_filter.xml中配置下。

關(guān)于uvcCamera的內(nèi)容,講到這里就結(jié)束了。感謝大家的閱讀。也歡迎大家一起交流。