zoukankan      html  css  js  c++  java
  • EasyPusher进行Android UVC外接摄像头直播推送实现方法

    最近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("
    录像地址:" + 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

    EasyDarwin

  • 相关阅读:
    SD卡镜像系列文件(uImage、zImage、dtb、rbf、uboot、preloader)如何更新到SD卡
    printk()打印的信息没办法在终端显示,那怎么调试驱动呢?
    【动态规划】ybt1301 大盗阿福
    【广度优先搜索】ybt1250 The Castle
    【数论/递归】P1017 进制转换
    【深度优先搜索/字符串】P1019 单词接龙
    4-18整理
    【数据结构】用结构体实现并查集
    【数据结构】图论基础整理
    【数据结构】二叉树基本内容整理(一)
  • 原文地址:https://www.cnblogs.com/babosa/p/8206952.html
Copyright © 2011-2022 走看看