EasyPusher:开源RTSP直播推流Android UVC外接摄像头推送源码

方案背景

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

本篇文章主要是介绍怎么操作UVC摄像头的。为此,我们实现了一个专门检测UVC摄像头的服务:UVCCameraService类。

检测UVC摄像头的服务:UVCCameraService类

监听

```java
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实例:

```java
 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摄像头的预览操作:

```java
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摄像头的关闭。

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

销毁

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

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

上层如何调用

有了这些操作,我们看看上层怎么调用的。
需要在Manifest里面增加若干代码,具体详见UVCCamera工程说明,代码如下:

```xml
<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摄像头推送:

```java
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摄像头的视频了~~

我们再看看如何录像,也非常简单…

```java
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摄像头还支持后台推送,即不预览的情况下进行推送,同时再切换到前台继续预览只需要调用一个接口即可实现,如下:

```java
@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摄像头的预览、推送,,只需要同时退出服务即可。

```java
public void onQuit(View view) {     // 退出
  finish();

  // 终止服务...
  Intent intent = new Intent(this, MediaStream.class);
  stopService(intent);
}
```

EasyPusher

EasyPusher推流是EasyDarwin开源流媒体团队开发的一款推送流媒体音/视频流给标准RTSP流媒体服务器(如EasyDarwin、Wowza)的流媒体推送库,全平台支持(包括Windows/Linux(32 & 64),ARM各平台,Android、iOS),通过EasyPusher我们就可以避免接触到稍显复杂的RTSP/RTP/RTCP推送流程,只需要调用EasyPusher的几个API接口,就能轻松、稳定地把流媒体音视频数据推送给RTSP流媒体服务器进行处理和转发,EasyPusher经过长时间的企业用户体验,稳定性非常高。

tsingsee
tsingsee

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值