android app 如何与uvc摄像头通讯
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android app 如何与uvc摄像头通讯相关的知识,希望对你有一定的参考价值。
参考技术A 来看看是怎么操作UVC摄像头的吧.我们实现了一个专门检测UVC摄像头的服务:UVCCameraService类,主要代码如下:监听
mUSBMonitor = new USBMonitor(this, new USBMonitor.OnDeviceConnectListener() @Override
public void onAttach(final UsbDevice device)
Log.v(TAG, "onAttach:" + device);
mUSBMonitor.requestPermission(device);
@Override
public void onConnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock, final boolean createNew)
releaseCamera(); if (BuildConfig.DEBUG) Log.v(TAG, "onConnect:"); try final UVCCamera camera = new MyUVCCamera();
camera.open(ctrlBlock);
camera.setStatusCallback(new IStatusCallback() // ... uvc 摄像头链接成功
Toast.makeText(UVCCameraService.this, "UVCCamera connected!", Toast.LENGTH_SHORT).show(); if (device != null)
cameras.append(device.getDeviceId(), camera);
catch (Exception ex)
ex.printStackTrace();
@Override
public void onDisconnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock) // ... uvc 摄像头断开链接
if (device != null)
UVCCamera camera = cameras.get(device.getDeviceId()); if (mUVCCamera == camera)
mUVCCamera = null;
Toast.makeText(UVCCameraService.this, "UVCCamera disconnected!", Toast.LENGTH_SHORT).show();
liveData.postValue(null);
cameras.remove(device.getDeviceId());
else
Toast.makeText(UVCCameraService.this, "UVCCamera disconnected!", Toast.LENGTH_SHORT).show();
mUVCCamera = null;
liveData.postValue(null);
@Override
public void onCancel(UsbDevice usbDevice)
releaseCamera();
@Override
public void onDettach(final UsbDevice device)
Log.v(TAG, "onDettach:");
releaseCamera();// AppContext.getInstance().bus.post(new UVCCameraDisconnect());
);123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
这个类主要实现UVC摄像头的监听\链接\销毁\反监听.当有UVC摄像头链接成功后,会创建一个mUVCCamera对象.
然后在MediaStream里, 我们改造了switchCamera,当参数传2时,表示要切换到UVCCamera(0,1分别表示切换到后置\前置摄像头).
创建
在创建摄像头时,如果是要创建uvc摄像头,那直接从服务里面获取之前创建的mUVCCamera实例:
if (mCameraId == 2)
UVCCamera value = UVCCameraService.liveData.getValue(); if (value != null) // uvc camera.
uvcCamera = value;
value.setPreviewSize(width, height,1, 30, UVCCamera.PIXEL_FORMAT_YUV420SP,1.0f); return; // value.startPreview();
else
Log.i(TAG, "NO UVCCamera");
uvcError = new Exception("no uvccamera connected!"); return;
// mCameraId = 0;
123456789101112131415
预览
在预览时,如果uvc摄像头已经创建了,那执行uvc摄像头的预览操作:
UVCCamera value = uvcCamera;if (value != null)
SurfaceTexture holder = mSurfaceHolderRef.get(); if (holder != null)
value.setPreviewTexture(holder);
try
value.setFrameCallback(uvcFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP/*UVCCamera.PIXEL_FORMAT_NV21*/);
value.startPreview();
cameraPreviewResolution.postValue(new int[]width, height);
catch (Throwable e)
uvcError = e;
1234567891011121314
这里我们选的colorFormat为PIXEL_FORMAT_YUV420SP 相当于标准摄像头的NV21格式.
关闭预览
同理,关闭时,调用的是uvc摄像头的关闭.
UVCCamera value = uvcCamera; if (value != null)
value.stopPreview();
1234
销毁
因为我们这里并没有实质性的创建,所以销毁时也仅将实例置为null就可以了.
UVCCamera value = uvcCamera;if (value != null) // value.destroy();
uvcCamera = null;
12345
有了这些操作,我们看看上层怎么调用,
首先需要在Manifest里面增加若干代码,具体详见UVCCamera工程说明.如下:
<activity android:name=".UVCActivity" android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>1234567891011121314151617181920
然后,的代码在UVCActivity里,这个类可以在library分支的myapplication工程里找到.即这里.
启动或者停止UVC摄像头推送:
public void onPush(View view) // 异步获取到MediaStream对象.
getMediaStream().subscribe(new Consumer<MediaStream>() @Override
public void accept(final MediaStream mediaStream) throws Exception // 判断当前的推送状态.
MediaStream.PushingState state = mediaStream.getPushingState(); if (state != null && state.state > 0) // 当前正在推送,那终止推送和预览
mediaStream.stopStream();
mediaStream.closeCameraPreview();
else // switch 0表示后置,1表示前置,2表示UVC摄像头
// 异步开启UVC摄像头
RxHelper.single(mediaStream.switchCamera(2), null).subscribe(new Consumer<Object>() @Override
public void accept(Object o) throws Exception // 开启成功,进行推送.
// ...
mediaStream.startStream("cloud.easydarwin.org", "554", id);
, new Consumer<Throwable>() @Override
public void accept(final Throwable t) throws Exception // ooop...开启失败,提示下...
t.printStackTrace();
runOnUiThread(new Runnable() @Override
public void run()
Toast.makeText(UVCActivity.this, "UVC摄像头启动失败.." + t.getMessage(), Toast.LENGTH_SHORT).show();
);
);
);
12345678910111213141516171819202122232425262728293031323334353637
这样,整个推送就完成了.如果一切顺利,应当能在VLC播放出来UVC摄像头的视频了~~
我们再看看如何录像.也非常简单…
public void onRecord(View view) // 开始或结束录像.
final TextView txt = (TextView) view;
getMediaStream().subscribe(new Consumer<MediaStream>() @Override
public void accept(MediaStream mediaStream) throws Exception if (mediaStream.isRecording()) // 如果正在录像,那停止.
mediaStream.stopRecord();
txt.setText("录像");
else // 没在录像,开始录像...
// 表示最大录像时长为30秒,30秒后如果没有停止,会生成一个新文件.依次类推...
// 文件格式为test_uvc_0.mp4,test_uvc_1.mp4,test_uvc_2.mp4,test_uvc_3.mp4
String path = getExternalFilesDir(Environment.DIRECTORY_MOVIES) + "/test_uvc.mp4";
mediaStream.startRecord(path, 30000); final TextView pushingStateText = findViewById(R.id.pushing_state);
pushingStateText.append("\n录像地址:" + path);
txt.setText("停止");
);
123456789101112131415161718192021
UVC摄像头还支持后台推送,即不预览的情况下进行推送,同时再切换到前台继续预览.只需要调用一个接口即可实现,如下:
@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1)
ms.setSurfaceTexture(surfaceTexture); // 设置预览的surfaceTexture@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture)
ms.setSurfaceTexture(null); // 设置预览窗口为null,表示关闭预览功能
return true;
123456789
如果要彻底退出uvc摄像头的预览\推送,那只需要同时退出服务即可.
public void onQuit(View view) // 退出
finish(); // 终止服务...
Intent intent = new Intent(this, MediaStream.class);
stopService(intent);
1234567
## 获取更多信息 ##
手机APP与服务器通讯用什么协议?MQTT Server哪家强?
手机APP与服务器通讯协议选择
开发手机APP相关项目,手机APP通常大量使用HTTP协议与服务器通信,这样手机可以主动从服务器获取所需要的数据,但是如果要让服务器主动与手机通信,让手机去做一件事情该怎么办呢?假设一个需求:服务器在需要的时候会向手机发送一个指令,手机接到指令后利用摄像头拍一张照片上传到服务器,那么可以考虑几种方法来实现:
1.手机进行HTTP轮询,轮询间隔若干秒,服务器没有什么指令就返回空内容,如果有指令就返回指令内容。这样虽然写起来很简单,但是轮询间隔设置多大比较好呢,轮询的太频繁了,占用系统、网络资源,轮询的频率降低的话,指令没法及时送达,时效性不好。
2.由于HTTP轮询有占用资源多,时效性不好的缺点,看来还是得基于TCP长连接来实现才能解决问题。
早年间websocket还未出现的时候,基于网页的即时通讯应用,为了解决消息接收的及时性,在HTTP协议上做文章,使用长轮询(long polling)来解决普通短轮询的缺点,即客户端发起一个HTTP请求,服务端Hold住该请求,并不及时返回数据,比如持续Hold住该请求1分钟,在1分钟内,如果有消息要发给客户端,立刻返回HTTP正文,如果超过1分钟还没有任何消息需要发给客户端,则断开连接或者返回空的内容,客户端发现连接断开或者收到空的内容,立马重新发起一个HTTP请求,服务端继续Hold住,如此往复。至于每次请求最多Hold住几分钟,可以根据情况来决定,客户端和服务端只要调高超时值即可,但也不宜设置的太久,因为HTTP连接被Hold住期间,是没有心跳包机制的,如果设置的太长,比如10分钟,到第2分钟的时候,网络不好断开了,客户端浑然不知,依然在傻傻的等待到第10分钟才重连。后来这种长轮询模式被用到了手机APP上,也能使得服务器与手机实现更及时的通讯。
不过服务端这边实现起来比较麻烦,很多传统的HTTP服务端和WEB框架,设计之初都没有考虑到需要长时间Hold住HTTP请求,一般每个HTTP请求都要占用一个线程来处理,如果该连接被Hold住,该线程就被Hold住了,无法处理其它请求,当客户端数量较多达到几百个几千个的时候,为了同时Hold住那么多连接,就要配置服务端的工作线程数为数百个数千个,导致效率低下,性能下降。虽然使用node.js这样的异步服务端可以解决该问题,但很多时候我们因为团队技术方向、历史项目兼容原因,没法换用服务端技术方案。
3.直接基于TCP开发自定义协议,和很多游戏一样,客户端创建一个socket连接上服务器,保持TCP长连接,即可与服务器实时交换数据,当然服务器也能及时下发命令。自定义协议的缺点是客户端和服务端,还有很多事情要自己做,制定协议规则,处理错误和异常,比如发送的数据只发了一半,网络断开了怎么办,收到的数据是否完整,该怎么校验,收到了和协议规定不一样的异常数据怎么办,如何确认客户端已经收到了数据,错误重发,断线重连,身份验证,权限管理,等等功能,都要自己去实现,增加开发时间成本。
有没有一种现成的协议、服务、库已经帮我们做好这些事情了呢,当然有,那就是在手机等移动设备上非常常用的MQTT协议:
4.使用MQTT的优点在于,已经有大量的第三方客户端和服务端实现了该协议,他们已经做好了保持TCP长连接、处理网络错误等工作,并且提供了很多丰富的功能,我们只需要关心业务上的数据传输即可,最简单的方法就是客户端和服务端通过MQTT互相扔json来通讯,再结合HTTP协议,已经可以实现绝大部分的业务需求。
MQTT Broker的选择
其实没有MQTT Server这个说法,标题为了使得seo效果更好才这么写,一般刚听说MQTT的同学到网上搜索一般是以mqtt server为关键词,后来才知道应该叫mqtt broker,这个实现mqtt协议的服务端,确实应该叫做broker(中间人),因为他并没有提供具体的服务,他只是让服务端和手机方便通讯的中间人,服务还是由你的具体业务服务端来实现。那么mqtt broker那么多,该怎么选呢,这里讲几个我用过的:
1.Mosquitto (https://mosquitto.org)
正如其名,这个是超轻量的mqtt broker,只有300多kb,如果你只需要使用mqtt进行客户端与服务端的基本通信功能,这个broker已经完全够用了,如果要实现更多高级功能,比如最重要的身份认证和访问控制,Mosquitto 默认只能使用配置文件来配置,同时不能在运行过程中修改配置,如果要在运行过程中增删用户,修改密码,修改ACL规则,则需要修改配置文件并重启Mosquitto 服务才能生效,没法基于mysql等数据库来储存和实时修改这些规则,虽然有很多第三方插件可以实现这些需求,但是这些第三方插件很多都是个人开发者开发的,有的已经没有在维护,有的没有经过严谨的测试和商用考验,在复杂业务场合不是很推荐。
2.mosca (http://www.mosca.io)
正如它的定义“MQTT broker as a module”那样,mosca和其它大多数mqtt broker不一样,其它大多数mqtt broker都像redis、mysql那样是一个独立的可执行程序,启动后读取配置文件,监听指定的网络端口提供服务,而mosca是nodejs的一个模块(库),它可以嵌入到你的nodejs程序中,在你的进程中运行以提供mqtt服务。所以你需要写一段js代码,调用mosca这个库的API函数,传入相应的参数以启动mqtt服务,同时可以向mosca注册回调函数,干预mqtt服务的每个环节,高度可定制,对于上面提到的身份验证和访问控制,都可以在代码中实现,无论你要借助mysql数据库还是redis来管理规则,都可以自己写。
mosca的优势在于,如果你想要的高级功能mqtt broker本身并没有提供,你可以很方便的二次开发增加功能,而不像Mosquitto / EMQ那样,如果要增加功能,只能通过开发插件或修改Mosquitto / EMQ源代码并重新编译的方式,而且还需要使用C语言 / Erlang语言开发,很麻烦,而且不是所有团队都有这个技术栈。
当然mosca也有缺点,很多高级功能都没有提供,需要你自行实现,虽然实现起来比较方便,但是我这个人是很懒的,有现成的决不自己发明轮子。
3.EMQ (http://www.emqtt.com)
一个重量级高并发mqtt broker,还是国产开源软件,中文文档完善丰富,大部分业务需要的高级功能都已经实现了,集群、负载均衡,完善的身份认证和访问控制功能,支持多种身份认证方式,支持使用MySQL/MongoDB/PostgreSQ/Redis等管理用户身份及ACL规则,除了TCP和SLL,还支持WebSocket和HTTP访问mqtt服务,支持消息持久化,提供命令行控制台和WEB控制台,提供REST API监控管理客户端。虽然进行二次开发和插件开发仍然需要使用Erlang语言开发,不是方便。但实际上它本身已经提供了众多高级功能以及丰富的API接口,已经可以满足绝大多数业务需求。
尤其是这个WEB控制台,让我非常满意,一行代码都不用写,就可以方便的一览大局,看到每个客户端设备的状态和在线情况以及订阅情况,在这个控制台上还集成了一个简单的mqtt client,对于调试和手工排查问题非常方便。强烈安利!
本文由CharlesSimonyi发表于CSDN博客:https://blog.csdn.net/CharlesSimonyi/article/details/89365141 转载请注明出处
以上是关于android app 如何与uvc摄像头通讯的主要内容,如果未能解决你的问题,请参考以下文章
Android平台GB28181接入端如何对接UVC摄像头?
Android平台GB28181接入端如何对接UVC摄像头?