使用EasyPusher进行手机低延时直播推流便捷开发

基于EasyPusher sdk库工程(即library module)实现一个推送客户端非常简单便捷,因为sdk端已经将各种烦人的状态维护\错误检查\权限判定\UI同步等功能都实现了,开发者仅仅只需要实现若干接口即可.

让我们看看如何实现一个Pusher吧!

首先我们介绍一下Pusher sdk的封装用到了哪些技术.

  1. Android Architecture Components

Android architecture components是Google 官方出的一个Android APP的开发标准与指南.其官方介绍文档如下:

A collection of libraries that help you design robust, testable, and maintainable apps. Start with classes for managing your UI component lifecycle and handling data persistence.

基于这个AAC库,Pusher 的sdk的封装做了两件事情:

  • 在底层可以捕获到上层Activity\Fragment的生命周期,从而自己管理自己的状态,包括初始化\启动\停止\反初始化等工作.这样,上层就轻松多了.
  • 使用LiveData保存维护自己的一些状态,上层可以方便通过注册LiveData,来获取到当前的状态,以及获取到状态变更的回调.

    1. Reactivestreams

reactive streams 即响应式编程,是一种基于”数据流和变化传播的编程范式”,更多的介绍见ReactiveX.pusher的library层一些接口基于Reactivestreams实现,这样很方便上层使用rxjava等库进行开发.

好了,我们看看如何基于library库实现一个pusher吧.sdk主要功能在MediaStream里实现,这个类是一个服务类.

  1. 启动服务:
// 启动服务...
Intent intent = new Intent(this, MediaStream.class);
startService(intent);
  1. 获取MediaStream对象.由于MediaStream是个服务,所以我们无法直接创建其对象,只能通过binder来绑定.这个过程是异步的.sdk内部对这个过程进行了封装,返回一个publisher,我们可以观察这个publisher来获取到MediaStream的对象.上层代码如下:

    // (异步)获取MediaStream对象
    private Single<MediaStream> getMediaStream() {
     Single<MediaStream> single = RxHelper.single(MediaStream.getBindedMediaStream(this, this), mediaStream);
     if (mediaStream == null) {
     return single.doOnSuccess(new Consumer<MediaStream>() {
     @Override
     public void accept(MediaStream ms) throws Exception {
     mediaStream = ms;
     }
     });
     } else {
     return single;
     }
    }
  2. 观察MediaStream内部状态更改并更新UI:

    final TextView pushingStateText = findViewById(R.id.pushing_state);
    final TextView pushingBtn = findViewById(R.id.pushing);
    ms.observePushingState(MainActivity.this, new Observer<MediaStream.PushingState>() {
    
     @Override
     public void onChanged(@Nullable MediaStream.PushingState pushingState) {
       if (pushingState.screenPushing) {
         pushingStateText.setText("屏幕推送");
    
         // 更改屏幕推送按钮状态.
    
         TextView tview = findViewById(R.id.pushing_desktop);
         if (pushingState.state > 0) {
           tview.setText("取消推送");
         } else {
           tview.setText("推送屏幕");
         }
         findViewById(R.id.pushing_desktop).setEnabled(true);
       } else {
         pushingStateText.setText("推送");
    
         if (pushingState.state > 0) {
           pushingBtn.setText("停止");
         } else {
           pushingBtn.setText("推送");
         }
    
       }
       pushingStateText.append(":\t" + pushingState.msg);
       if (pushingState.state > 0) {
         pushingStateText.append(pushingState.url);
       }
    
     }
    });

  3. 设置或者关闭摄像头View.

    TextureView textureView = findViewById(R.id.texture_view);
    textureView.setSurfaceTextureListener(new SurfaceTextureListenerWrapper() {
     @Override
     public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
       ms.setSurfaceTexture(surfaceTexture);
     }
     @Override
     public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
       ms.setSurfaceTexture(null);
       return true;
     }
    });
    
  4. 尝试申请权限,并在权限获取到后,告知SDK.

    
    if (ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||
       ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
     ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA_PERMISSION);
    }
    mediaStream.notifyPermissionGranted();
  5. 打开摄像头并预览.

    mediaStream.openCameraPreview();
  6. 启动推送

    mediaStream.startStream("cloud.easydarwin.org", "554", id);
  7. 停止推送

    mediaStream.stopStream();
  8. 停止预览

    mediaStream.closeCameraPreview();
  9. 退出

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

以上代码就是一个启动推送\退出的过程,注意,其中包括了摄像头权限申请,推送状态回调,摄像头预览界面设置,界面关闭时自动释放,Activity recreate处理等等许多隐性工作..这些工作看似不是SDK主要的工作,但是作为一个稳定的APP却必须十分小心地处理,我们必须慎之又慎才能处理好这些隐性工作.而通常处理的过程中会使得我们的逻辑变得异常复杂,导致bug频繁.

以上相关代码都可以在EasyPusher的library分支的demo里面看到.地址:

https://github.com/EasyDSS/EasyPusher-Android/tree/library

除此之外,sdk还支持摄像头切换\录像\UVC摄像头推送等功能.将会在别的文章进行介绍.

获取更多信息

邮件:support@easydarwin.org

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

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

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

Copyright © EasyDarwin Team 2012-2017

EasyDarwin

posted @ 2017-12-26 06:53  Babosa|EasyDarwin  阅读(659)  评论(0编辑  收藏  举报