最近EasyPusher针对UVC摄像头做了适配.我们结合了UVCCamera与EasyPusher,支持将UVC摄像头的视频推送到RTSP服务器上.在此特别感谢UVCCamera这个牛逼的项目!

来看看是怎么操作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());
}
});

这个类主要实现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;
}

预览

在预览时,如果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;
}
}

这里我们选的colorFormat为PIXEL_FORMAT_YUV420SP 相当于标准摄像头的NV21格式.

关闭预览

同理,关闭时,调用的是uvc摄像头的关闭.

        UVCCamera value = uvcCamera;
if (value != null) {
value.stopPreview();
}

销毁

因为我们这里并没有实质性的创建,所以销毁时也仅将实例置为null就可以了.

UVCCamera value = uvcCamera;
if (value != null) {
// value.destroy();
uvcCamera = null;
}

有了这些操作,我们看看上层怎么调用,

首先需要在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>

然后,的代码在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();
}
});
}
});
}
}
});
}

这样,整个推送就完成了.如果一切顺利,应当能在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("停止");
}
}
});
}

UVC摄像头还支持后台推送,即不预览的情况下进行推送,同时再切换到前台继续预览.只需要调用一个接口即可实现,如下:

@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
ms.setSurfaceTexture(surfaceTexture); // 设置预览的surfaceTexture
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
ms.setSurfaceTexture(null); // 设置预览窗口为null,表示关闭预览功能
return true;
}

如果要彻底退出uvc摄像头的预览\推送,那只需要同时退出服务即可.

public void onQuit(View view) {     // 退出
finish(); // 终止服务...
Intent intent = new Intent(this, MediaStream.class);
stopService(intent);
}

## 获取更多信息 ##

邮件:support@easydarwin.org

EasyDarwin开源流媒体服务器:www.EasyDarwin.org

EasyDSS商用流媒体解决方案:www.EasyDSS.com

EasyNVR无插件直播方案:www.EasyNVR.com

Copyright © EasyDarwin Team 2012-2017

EasyPusher进行Android UVC外接摄像头直播推送实现方法的更多相关文章

  1. EasyRTMP实现的一套简单、高效、易用的全平台(Windows/Linux/ARM/Android/iOS)RTMP直播推送库

    本文转自EasyDarwin开源团队成员Kim的博客:http://blog.csdn.net/jinlong0603/article/details/52938980 EasyRTMP介绍 Easy ...

  2. 基于EasyDarwin EasyPusher实现Android手机直播推送功能

    EasyPusher直播推送在之前就已经稳定支持了Windows.Linux.ARM上的RTSP直播推送功能,配合EasyDarwin开源流媒体服务器,延时基本在1s以内,这个技术方案经过一年多时间, ...

  3. EasyPusher安卓Android手机直播推送之RTSP流媒体协议流程

    EasyPusher移动端推送同我们平时用的RTSP直播推送流程一样,都是采用标准RTSP/RTP推送流程:ANNOUNCE->SETUP->PLAY->RTP/RTCP->T ...

  4. EasyDarwin开源手机直播方案:EasyPusher手机直播推送,EasyDarwin流媒体服务器,EasyPlayer手机播放器

    在不断进行EasyDarwin开源流媒体服务器的功能和性能完善的同时,我们也配套实现了目前在安防和移动互联网行业比较火热的移动端手机直播方案,主要就是我们的 EasyPusher直播推送项目 和 Ea ...

  5. EasyDarwin开源手机直播方案:EasyPusher手机直播推送,EasyDarwin流媒体server,EasyPlayer手机播放器

    在不断进行EasyDarwin开源流媒体server的功能和性能完好的同一时候,我们也配套实现了眼下在安防和移动互联网行业比較火热的移动端手机直播方案,主要就是我们的 EasyPusher直播推送项目 ...

  6. EasyPusher直播推送中用到的缓冲区设计和丢帧原理

    问题描述 我们在开发直播过程中,会需要用到直播推送端,推送端将直播的音视频数据推送到流媒体服务器或者cdn,再由流媒体服务器/CDN进行视频的转发和分发,提供给客户端进行观看.由于直播推送端会存在于各 ...

  7. 安卓Android手机直播推送同步录像功能设计与实现源码

    本文转自:http://blog.csdn.net/jyt0551/article/details/58714595 EasyPusher是一款非常棒的推送客户端.稳定.高效.低延迟,音视频同步等都特 ...

  8. Android 基于Netty的消息推送方案之对象的传递(四)

    在上一篇文章中<Android 基于Netty的消息推送方案之字符串的接收和发送(三)>我们介绍了Netty的字符串传递,我们知道了Netty的消息传递都是基于流,通过ChannelBuf ...

  9. Android 基于Netty的消息推送方案之字符串的接收和发送(三)

    在上一篇文章中<Android 基于Netty的消息推送方案之概念和工作原理(二)> ,我们介绍过一些关于Netty的概念和工作原理的内容,今天我们先来介绍一个叫做ChannelBuffe ...

随机推荐

  1. AC日记——【模板】树链剖分 洛谷 P3384

    题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...

  2. 判断scrollView的滑动方向

    第一种方式: float lastContentOffset; - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { las ...

  3. Hdoj 2509 Be the Winner

    Diciption Let's consider m apples divided into n groups. Each group contains no more than 100 apples ...

  4. 由ASIHttpRequest里的block引发的思考

    项目发http请求,现在一般的都是用的第三方开源库,当然发异步请求时我们也会写几个回调函数来进行请求返回时的处理.不过前段时间看一个朋友写的代码,里面很用block简单的实现了回调相关的部分.比如: ...

  5. 数据库访问的弹性化---WebLogic和Oracle RAC的整合:Active GridLink

        1.  什么是Active GridLink Data Source 从Oracle WebLogic Server 10.3.4版本开始引进了一种单数据源实现来支持Oracle RAC集群. ...

  6. php实现将人民币金额转大写的办法

    class Num2Cny{ static $basical=array(0=>'零','壹','贰','叁','肆','伍','陆','柒','捌','玖'); static $advance ...

  7. Flutter开发记录part3

    (1) 获取当前屏幕宽度 width: MediaQuery.of(context).size.width, (1) pull_to_refresh,smartrefresh 自定义文字: new S ...

  8. 更新到mysql 5.7后解决0000-00-00日期问题

    更新到mysql 5.7后解决0000-00-00日期问题 学习了:http://www.07net01.com/2016/04/1479450.html mysql 5.7 默认开始用以下sql m ...

  9. 【Python】写入文件

    1.1写入空文件 若将文本写入文件,在调用open()时候需要提供另外一个实参,告诉Python你要写入打开的文件 file_path = 'txt\MyFavoriteFruit.txt' with ...

  10. 《C陷阱与缺陷》学习笔记(一)

    前言和导读 "得心应手的工具在初学时的困难程度往往超过那些easy上手的工具."比較认同这句话. 我至今认为自己事实上还是个刚入了门的刚開始学习的人. 第一章 "词法&q ...