Android UVC External Camera Push-EasyPusher

Recently, EasyPusher has been adapted for UVC cameras. We combine UVCCamera and EasyPusher to support pushing the video of UVC cameras to RTSP servers. Special thanks to UVCCamera for this awesome project!

Let's see how to operate the UVC camera. We have implemented a service for detecting UVC cameras: UVCCameraService class, the main code is as follows:

monitor

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());
            }
        });

This class mainly implements monitoring\linking\destroying\anti-monitoring of UVC cameras. When a UVC camera is successfully linked, an mUVCCamera object will be created.

Then in MediaStream, we transformed switchCamera, when the parameter is passed 2, it means to switch to UVCCamera (0, 1 respectively means switch to rear/front camera).

create

When creating a camera, if you want to create a uvc camera, get the mUVCCamera instance created before directly from the service:

 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;
 }

preview

When previewing, if the uvc camera has been created, perform the preview operation of the uvc camera:

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;
  }
}

The colorFormat we choose here is PIXEL_FORMAT_YUV420SP, which is equivalent to the NV21 format of a standard camera.

close preview

Similarly, when it is closed, the shutdown of the uvc camera is called.

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

destroy

Because we don't actually create it here, we can just set the instance to null when destroying it.

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

With these operations, let's see how the upper layer calls,

First of all, you need to add some codes to the Manifest. For details, please refer to the UVCCamera project description. As follows:

<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>

Then, the code is in UVCActivity, and this class can be found in the myapplication project of the library branch. That is here .

To start or stop UVC camera push:

    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();
                                }
                            });
                        }
                    });
                }
            }
        });
    }

In this way, the whole push is completed. If all goes well, the video of the UVC camera should be able to be played in VLC~~

Let's take a look at how to record. It's also very simple...

    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("停止");
                }
            }
        });
    }

The UVC camera also supports background push, that is, push without preview, and then switch to the foreground to continue preview. It only needs to call an interface to achieve, as follows:

@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;
}

If you want to completely quit the preview\push of the uvc camera, you only need to quit the service at the same time.

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

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

## Get more info##

Email: [email protected]

EasyDarwin Open Source Streaming Server: www.EasyDarwin.org

EasyDSS Commercial Streaming Solutions: www.EasyDSS.com

EasyNVR no plug-in live broadcast solution: www.EasyNVR.com

Copyright © EasyDarwin Team 2012-2017

EasyDarwin

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325444057&siteId=291194637