会捷通客户端 SDK (Android版) 开发文档

会捷通客户端 SDK (Android版) 开发文档

概述

画廊模式

会捷通客户端 SDK 是⼀款视频会议软终端开发套件,开发者可以利⽤本 SDK 开发出具有清晰流畅的⾳视频体验和⾼清内容协作的⾳视频会议终端应用。会捷通客户端 SDK 具备强⼤的⽹络适应性,和独特的⾳视频抗⽹络丢包算法,配合会捷通云视讯平台使⽤,可以保证在 30% ⽹络丢包环境下视频依然清晰流畅,即使⽹络丢包⾼达 50%,依然可以保证⾳频通畅。 会捷通客户端 SDK 提供了丰富,⽽且简单易⽤的 API 接⼝。开发者不需要掌握丰富的⾳视频和信令相关知识,也可以使⽤本 SDK 开发出专业的视频会议软终端应用。 本⽂档详细介绍了 SDK 的各项功能,以及它们的使⽤⽅法。

本文档对应 SDK 版本为 1.4

系统要求

  • Android 5.0 及以上版本的移动设备

开发环境

  • 推荐使⽤ AndroidStudio 3.0 或以上版本进⾏开发

示例代码

示例代码下载地址

https://hexmeet.coding.net/public/evsdk-svc-android-demo/EVSDK-SVC-Android-Demo/git (opens new window)

代码构成

提示

以下列表结构展示为在 Android Studio 中设置浏览模式为 Android 时

  • app
    • manifests

      • AndroidManifests.xml
    • java

      • com.hexmeet.hjt - 代码目录
        • cache - 缓存模块
        • call - 入会模块
        • event
        • fragment -呼叫模块
        • login - 登录、匿名呼叫模块
        • model
        • sdk - 调用EVSDK接口模块
        • service
        • utils
          • App
          • AppCons
          • BaseActivity - 父类
          • CrashHandler
          • FullscreenActivity - 父类
          • MainActivity -主界面
          • PermissionWrapper -权限
          • PhoneStateChangedReceiver -监听来电显示
          • SplashAcitvity - 闪屏页面
    • assets - 资源文件

    • jniLibs - 原⽣库(.so)⽂件夹,和 EVSDK.jar 一起使用

    • res - 资源文件

      • drawable
      • layout
      • mipmap
      • raw - 音频文件、图片等
      • valuse - 屏幕适配、颜色、字符串等
      • xml

快速定制化

修改应用的 gradle 文件

开发者应该根据需要对应用的 gradle 文件进行修改,特别是 applicationId 等关键内容

defaultConfig {
    // 设置应用的 applicationId,需要更改为开发者应用的真实 Id
    applicationId System.getenv("APPLICATION_ID") as String ?: "com.hexmeet.hjtsdkedmo"
    minSdkVersion 21
    targetSdkVersion 29
    versionCode System.getenv("BUILD_CODE") as Integer ?: 0
    versionName System.getenv("BUILD_VERSION") as String ?: "0.0"

    def appFileProviderAuth = applicationId + (".fileprovider")
    
    // it will be used in the AndroidManifest.xml file
    manifestPlaceholders = [APP_FILE_PROVIDER_AUTH: appFileProviderAuth]

    ndk {
        // 请注意 EVSDK 不支持 x86 平台,所以abiFilters 不能包含 ‘x86'
        moduleName "jniLibs"
        abiFilters "armeabi", "armeabi-v7a"
    }
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

开发入门

初始化 SDK

aar 形式:

文件 描述
添加 EVSDK 库⽂件 evsdk.aar 示例代码⽬录:swep-evsdk-android-demo\app\libs

在程序 SdkManagerImpl 中初始化SDK:

	private EVEngine engine;
	SVEngine.LayoutType gallery_layout[] = {
            SVEngine.LayoutType.type1,
            SVEngine.LayoutType.type2H,
            SVEngine.LayoutType.type4,
            SVEngine.LayoutType.type6W
    };

    SVEngine.LayoutType speaker_layout[] = {
            SVEngine.LayoutType.type1
    };

	@Override
    public void initSDK() {
        LOG.info("App - initSDK");
        Context appContext = App.getInstance().getContext();
        /**
		初始化raw文件
		注:需复制res/raw的全部文件,ResourceFile类相关代码
 		*/
        ResourceFile.getInstance().createAndStart(appContext);
        engine = EVFactory.createEngine();
        //设置存储路径
        String path = appContext.getFilesDir().getAbsolutePath();
        /**
         用于设置sdk日志相关
         level 日志等级
         path 日志存放位置
         filename 日志名称
         size 日志大小
         */
        engine.setLog("EasyVideo", path, "evsdk", 1024 * 1024 * 20);
        //开启日志
        engine.enableLog(true);
        //设置rootca.pem文件
        engine.setRootCA(path);
        engine.initialize(appContext,path, "config");
        //设置最大视频窗口数量
        engine.setMaxRecvVideo(4);
        //设置呼叫带宽 
        engine.setBandwidth(2048);
        //设置会中layout的两种模式
        engine.setLayoutCapacity(SVEngine.LayoutMode.GalleryMode, Arrays.asList(gallery_layout));
        engine.setLayoutCapacity(SVEngine.LayoutMode.SpeakerMode, Arrays.asList(speaker_layout));
        //添加CallBack回调
        engine.addEventListener(new EVListenr());
    }

申请权限

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

如果你的 targetSdkVersion ≥ 29,还需要在 AndroidManifest.xml 文件的 <application> 区域添加如下行:

<application
   android:requestLegacyExternalStorage="true">
   ...
</application>

防止代码混淆

app/proguard-rules.pro 文件中添加如下行,防止混淆 SDK 的代码:

-keep class ev.engine.** {*;}
-keep class ev.common.** {*;}

呼叫流程

下图说明客户端应用从初始化开始,呼叫进入一个会议,再结束呼叫的基本流程。

uml diagram

登录

匿名方式入会

用户匿名方式入会执行:定位->匿名登录->注册视频服务->呼叫 (其中匿名登录以及注册视频服务均由SDK 处理)

  1. 定位服务、匿名登录、注册视频服务统一都由SDK处理只需要调用以下 API

    /**
     用户执行匿名登录入会
    
      locationServer   定位服务器
      port             端口号
      conferenceNumber 会议号码
      displayName      用户在会议中的名称
      password         会议密码
      https			   是否是https
      return 返回 SDK 处理结果,UI一般可忽略
     */
    int joinConferenceWithLocation(String locationServer,int port, String conferenceNumber, String displayName, String password);
    
    //示例代码
    engine.enableSecure(https);
    engine.joinConferenceWithLocation("server","port","number","name","password");
    
  2. 当调用匿名登录的 API 后,UI 会收到以下 SDK 回调。需要做出处理:

    //定位或者匿名登录错误回调
     void onError(EVCommon.EVError err){};
    //入会成功回调
     void onCallConnected(EVCommon.CallInfo info){}
    //入会失败回调
     void onCallEnd(EVCommon.CallInfo info){} 
    

用户账号登录

用户账号登录会执行:定位->登录->注册视频服务(其中注册视频服务由 SDK 处理)

  1. 定位

    /**
      用户定位登录(包含定位跟登录)
    
      locationServer 定位服务器
      locationPort   端口号
      userName       用户名
      password       会议密码
      return         返回API结果UI可忽略
     */
    int loginWithLocation(String locationServer, int locationPort, String userName, String password);
    
    //示例代码
    engine.enableSecure(https);//是否是https
    password = engine.encryptPassword(password);//登录时需要传加密过后的密码
    
    engine.loginWithLocation(locationServer:"服务器地址", locationPort:"端口号", userName:"用户名", password:"密码");
    
    //登录后会收到SDK以下回调
    //定位或者登录错误会回调
    void onError(EVCommon.EVError err){};
    //登录成功会回调
    void onLoginSucceed(EVEngine.UserInfo user){}
    

窗口设置

EVSDK 视频窗口分为三类 1. 本地窗口 2. 远端窗口 3. 内容窗口(双流)

  • 本地窗口 - 一个,用于显示摄像头设备的图像
  • 远端窗口 - 多个,用于显示接收到的视频流
  • 内容窗口 - 一个,用于显示接收到的共享内容

App UI 可以自行控制这些窗口的摆放位置、大小、以及显示/隐藏等

1.本地窗口设置
/**
 设置本地窗口

 logcalVideo 本地窗口对象
 return      返回结果(UI可以忽视,不作处理)
 */
int setLocalVideoWindow(Object logcalVideo);
//示例代码(本地窗口为surfaceView对象)
EVFactory.createEngine().setLocalVideoWindow(surfaceView);

2.远端窗口
/**
 远端窗口 

 remoteVideo 远端窗口集合
 return      返回结果(UI可以忽视,不作处理)
 */
int setRemoteVideoWindow(List<Object> remoteVideo);
//示例代码(远端窗口为surfaceView对象)
EVFactory.createEngine().setRemoteVideoWindow(Arrays.asList(remoteBox.getAllSurfaces()));

3. 内容窗口
/**
 设置内容窗口

 remoteContent 内容窗口对象
 return        返回结果(UI可以忽视,不作处理)
 */
 int setRemoteContentWindow(Object remoteContent);
//示例代码(内容窗口为surfaceView对象)
engine.setRemoteContentWindow(surfaceView);

本地预览

/**
 设置本地预览窗口
   注:本地预览是指在非会议状态

 view        本地预览窗口对象
 enable      启用预览,设置本地预览时,请开启此方法
 return      返回结果(UI可以忽视,不作处理)
 */
 int enablePreview(boolean enable);
 int setLocalVideoPreviewWindow(SurfaceView view);
//示例代码
public void setLocalVideoPreviewWindow(SurfaceView view) {
      engine.enablePreview(true);
      engine.setLocalVideoPreviewWindow(view);
}

实现视频通话

视频通话主要分为两种,一种是匿名入会(参考登录模块的匿名登录入会),一种是用户登录后入会

  • 设置用户入会后的头像以及视频背景图片(关闭摄像头所展示的图片)
/**
 设置会议中用户的头像以及背景图片

 backgroundPath 背景图片
 userImagePath  用户图片
 return 返回结果(UI可以忽视,不作处理)
 */
int setUserImage(String backgroundPath,String userImagePath);
//示例
 engine.setUserImage("backgroundPath","userImagePath";
  • 用户创建会议
  /**
  用户创建会议
   @param conference_number 创建会议的号码,这个参数要求必须是数字字符串,字符串的长度范围由服务器设置,默认情况要求如下:
          端口模式自定义会议号码可以是任意数字开头的 5-11 位数字,在同一个 MRU 上,一个会议的号码不能是另一个号码的前缀。
   @param conference_title 创建会议显示的名字
   @param password 加入会议所需要的密码,不设置则不需要入会密码
   @return id 传出参数,用于删除会议时使用,如果不结束删除会议,会议中有参会人员,则会议不会自动结束,如果会议中没有参会人员,会议会在5分钟后自动结束并删除。如果 id 不为空,表示创建会议成功。
   */
public String createConference(String conferenceNumber, String conferenceDisplayName, String password)
  • 用户登录后主动入会
/**
 用户加入会议

 conferenceNumber 会议号码
 displayName      会议名称
 password         会议密码
 return 返回结果(UI可以忽视,不作处理)
 */
int joinConference(String conferenceNumber,String displayName, String password);
//示例 注:会议名称是用户名
engine.joinConference(conferenceNumber:"会议号码",displayName:"会议名称",password:"会议密码");
  • 当调用入会的 API 后,UI 会收到以下 SDK 回调。需要做出处理
//入会成功回调
 void onCallConnected(EVEngine.CallInfo info){}
//入会失败回调
 void onCallEnd(EVEngine.CallInfo info){} 

主讲视图或画廊视图

视图模式指的是应用的 UI 如何为接收到的远端视频做布局展示。典型分屏样式有:主讲视图 (即一大多小布局) 和画廊视图 (平均大小布局)。当布局样式为主讲视图时,一般希望大窗口显示的视频流分辨率比较其他远端视频流更高,而当布局样式为画廊视图时,一般希望每个视频流的分辨率尽量一致。因此,在 UI 需要改变布局样式时,需要通知 SDK,这样 SDK 可以根据需要向平台发出相应请求,告知平台 UI 希望呈现的分屏布局样式,从而平台可以尽量提供满足 app 需要的视频流。

另外,当平台侧通过会议控制,对分屏进行改变时,SDK 也会接收到变化通知,此时 SDK 通知 UI 分屏模式发生了何种变化,UI 应该根据变化的样式进行对应的布局改变。

相关细节可参考 API 的布局类型、分屏样式请求和布局变化事件回调部分。

  • 主讲视图 (一大多小布局) 实现示例

主讲模式

  • 画廊视图 (平均大小布局) 实现示例

画廊模式

设置视图模式

/**
 设置视频模式(主讲模式、画廊模式)

 layout 对象
 return 返回结果可忽视
 */
int setLayout(LayoutRequest layout);

//示例
SVEngine.LayoutType type = (svcLayoutMode == 0) ? SVEngine.LayoutType.typeAuto : ((svcLayoutMode == 2) ? SVEngine.LayoutType.type1 : SVEngine.LayoutType.type6W);
SVEngine.LayoutRequest request = new SVEngine.LayoutRequest(SVEngine.LayoutMode.fromInt(svcLayoutMode),type, SVEngine.LayoutPage.typeCurrent, EVEngine.VideoSize.VIDEO_SIZE_UNKNOWN,null);
engine.setLayout(request);

静音或解除静音

**执行本地麦克风开启或者关闭 **

/**
 是否启用麦克风

 mute   true 为关闭 false为开启
 return 返回结果可忽视
 */
void muteMic(boolean mute); 

//示例 注:先获取⻨克⻛状态  
public void setMicMute(boolean mute) {
    if(mute ^ !engine.micEnabled()){
         engine.enableMic(!mute);
      }
 }

是否打开高帧率

int enableHighFPS(boolean enable);

是否打开1080P

int enableHD(boolean enable)

启用视频或停用视频

执行本地视频开启或者关闭

/**
 是否关闭本地视频

 enable true 为关闭  false为开启
 return 返回结果可忽视
 */
int enableCamera(bool enable);

//示例代码 注:先获取本地视频状态
public void enableVideo(boolean enable) {
  if(enable ^ engine.cameraEnabled()){
      engine.enableCamera(enable);
    }
 }

接收和显示共享内容

收到共享或者共享结束 SDK 都会通过回调的形式告诉 UI

/**
 接收到内容分享或者内容分享结束的回调
 info ContentInfo对象
 enabled:  true 开,false 关 
 dir:     Upload 发 ,Download 收
 */
void onContent(EVCommon.ContentInfo info){}
//示例代码
  if(info.dir== EVCommon.StreamDir.Download){
      //收到分享(如果设置了内容窗口那么应该在此处显示出你的内容窗口,如果没有设置内容窗口SDK将会弹出一个窗口来显示分享内容)
  }else {
      //结束分享,关闭分享窗口        
  }

实现切换摄像头

int switchCamera();

//示例代码
public void switchCamera() {
  engine.switchCamera();
}

共享屏幕

/**
启动屏幕共享时,检测横竖屏时,调用此接口:
enable : true 横屏/ false 竖屏
*/
void setLandscape(boolean enable);

/**
开启屏幕共享:
    cts :上下文
    mediaProjection :mediaProjection对象
    mDisplay : Display对象
    handler  : 开启一个handler
 注:调用此接口时,回调"onContent"如果收到contentInfo.status类型为"Declined"时,代表没有权限,不	可共享。
*/
void setScreenCapture(Context cts,MediaProjection mediaProjection, Display mDisplay, Handler handler);
//示例代码具体请看Demo的'ScreenCaptureService'类

/**
停止屏幕共享
*/
int stopContent() ;

挂断会议

点击挂断需调用SDK的API

int leaveConference();

//示例代码
 public void dropCall() {
    engine.leaveConference();
  }
//退出成功后,收到sdk的回调
void onCallEnd(EVCommon.CallInfo info){} 

P2P (点对点) 呼叫相关内容

P2P 呼叫的基本流程如下图所示

uml diagram

P2P主叫

/**
  conferenceNumber 被叫人userId
  displayName      会议中显示自己名称
  password         密码设为无
  CallType         会议类型 :P2P
  return           返回结果:0 成功,否则失败 
*/
int joinConference(String conferenceNumber, String displayName, String password, EVEngine.CallType type);

接收到P2P呼叫请求

/**
 收到P2P会议邀请的通知,具体参数如下:
 	boolean isAudioOnly;      可忽略
 	boolean contentEnabled;   可忽略
 	String  peer;			  主叫名字
 	String  conferenceNumber; P2P入会号码
 	String  password;		  入会密码
 	EVEngine.EVError err;     错误信息
 	EVEngine.SvcCallType type;邀请类型SvcCallP2P 点对点呼叫
 	EVEngine.CallAction  action
                      {
						SvcNoAction  		  没有呼叫动作
 						SvcIncomingCallRing   呼入振铃
 						SvcIncomingCallCancel 呼入超时,取消呼叫
                       }
 */
void onJoinConferenceIndication(EVCommon.CallInfo info){}

接听

/**
  Number 此会议号码为:
  					收到P2P会议邀请通知的conferenceNumber去接听会议
*/
int declineIncommingCall(String Number){}

挂断

int leaveConference();

对方已接受P2P呼叫

void onCallPeerConnected(EVCommon.CallInfo info){}

获取主叫头像地址

void onPeerImageUrl(String imageUrl){};

API参考

以下为用户相关接口回调

setLog

/**
  用于设置sdk     日志相关
  logDomain     日志等级
  path          日志存放位置
  filename      日志名称
  size          日志大小
*/
void setLog(String logDomain, String path, String fileName, int size);

enableLog


/**
  打开 SDK ⽇志收集功能
  enable true:打开  false:关闭
*/
void enableLog(boolean enable);

compressLog

//获取⽇志⽂件路径,⽅便下载或⽤邮件发送⽇志
String compressLog();
//示例代码
String sdkPath= EVFactory.createEngine().compressLog();

setRootCA

/**
 设置rootca.pem文件,此文件在Demo的 res\raw 文件下。
 
 rootpath 文件路径
*/
int setRootCA(String rootPath);

initialize

/**
 
 使用配置文件初始化SDK
 appContext Application Context
 path           配置文件路径
 configFileName 配置文件名称
*/
int initialize(Context appContext,String path,String configFileName);

注意

SDK 1.3 版本后 initialize 需要传入 appContext 参数

setUserImage

/**
 设置会议中用户的头像以及背景图片

 backgroundPath 背景图片
 userImagePath  用户图片
 return 返回结果(UI可以忽视,不作处理)
 */
int setUserImage(String backgroundPath,String userImagePath);

enableHardDecoding

/**
是否开启硬件解码能力

注:此接口需要在进入会议前设置 
 */
int enableHardDecoding(boolean hardDecoding);

setUserAgent

/**
  设置 SIP 信令⽤户显示的 UserAgent 域的值
  userAgent : ⽤户代理名称
  version   : 应⽤版本号
 */
int setUserAgent(String userAgent,String version);
//例如:
setUserAgent("HexMeet EasyVideo Android","V2.0.1.3745");

setLayoutCapacity

 /**
  设置会议中终端layout模式的集合:
  mode : GalleryMode/SpeakerMode
  types: 终端layout集合,具体请看DEMO中设置的类型集合。
 */
int setLayoutCapacity(LayoutMode mode, List<LayoutType> types);

addEventListener

/**
  当SDK有事件需要通知上层 APP 时,通过该监听器回调到APP处理程序,通常使⽤在登陆状态和呼叫状态等。
 */
int addEventListener(EVEventListener listener);
//示例代码
engine.addEventListener(new EVListenr());

removeEventListener

/**
	删除监听器,当 APP 不再需要监听 SDK 事件时,需要删除对应的监听器。
 */
int removeEventListener(EVEventListener listener);
//示例代码
engine.removeEventListener(new EVListenr());

reloadVideoDevices

/**
    初始化视频采集设备,开启摄像头权限时调用此方法。如果不调用此方法,采集不到本地视频。
*/
int reloadVideoDevices();

setDeviceRotation

/**
   设置设备⽅向,取值 0, 90, 180, 270
*/
int setDeviceRotation(int rotation);

//示例代码
private void onNewDirection(final int direction) {
    if (direction != oldCameraDirection) {
          final int _cameraDirection = (360 - direction) % 360;
              Handler handler = new Handler(handlerThread.getLooper());
                    handler.post(new Runnable() {
                        @Override
                       public void run() {
                          EVFactory.createEngine().setDeviceRotation(_cameraDirection);
                             }
                       });
                     oldCameraDirection = direction;
                 }
        }

enableSecure

/**
   是否启用 HTTPS 协议
   secure true:HTTPS/false:HTTP
*/
int enableSecure(boolean secure);

enableHighFPS

/**
 是否打开高帧率
*/
int enableHighFPS(boolean enable);

//获取当前高帧率是否打开状态
boolean highFPSEnabled();

enableHD

/**
 是否打开1080P
*/
int enableHD(boolean enable)
 
//获取当前1080P是否打开状态
boolean HDEnabled();

encryptPassword

/**
  用户登录密码加密
  encryptionPassword  密码明文
  return 返回加密后的密码
*/
 String encryptPassword(String encryptionPassword);

loginWithLocation

/**
  用户定位登录(包含定位跟登录)

  locationServer 定位服务器
  locationPort   端口号
  userName       用户名
  password       会议密码
  return         返回API结果UI可忽略
 */
int loginWithLocation(String locationServer, int locationPort, String userName, String password);

logout

/**
 用户退出登录

 return 返回结果
 */
int logout();

//示例代码
 public void logout() {
	engine.logout();
 }

downloadUserImage

/**
 下载用户当前头像(需登录后调用)

 downLoadImage 指定储存地址
 return        返回结果可忽略
 */
int downloadUserImage(String downLoadImage);

uploadUserImage

/**
 向服务器上传用户头像

 path   上传图片地址
 return 返回结果可忽略
 */
int uploadUserImage(String upLoadImage);

changePassword

/**
 修改当前用户密码

 oldpassword   原密码
 newPassword   新密码
 return        返回结果 0 表示成功
 */
int changePassword(String oldpassword,String newPassword);
//示例代码  注:所有密码传给SDK都是加密过后的。
 String oldPass = engine.encryptPassword(oldPassword);
 String newPass = engine.encryptPassword(newPassword);
 int code = engine.changePassword(oldPass, newPass);
 if(code==0){
    //修改成功
 }

changeDisplayName

/**
 修改当前用户名称

 displayName   名称
 return        返回结果 0 表示成功
 */
int changeDisplayName(String displayName);

//示例代码
 int code = engine.changeDisplayName(name);
 if (code==0) {
     //修改成功
 }

setConfDisplayName

/**
  会议中修改名字
 */

int setConfDisplayName(String displayName);

setLocalVideoWindow

/**
 设置本地窗口

 logcalVideo 本地窗口对象
 return      返回结果(UI可以忽视,不作处理)
 */
int setLocalVideoWindow(Object logcalVideo);
//示例代码(本地窗口为surfaceView对象)
EVFactory.createEngine().setLocalVideoWindow(surfaceView);

setRemoteVideoWindow

/**
 远端窗口 

 remoteVideo 远端窗口集合
 return      返回结果(UI可以忽视,不作处理)
 */
int setRemoteVideoWindow(List<Object> remoteVideo);
//示例代码(远端窗口为surfaceView对象)
EVFactory.createEngine().setRemoteVideoWindow(Arrays.asList(remoteBox.getAllSurfaces()));

setRemoteContentWindow

/**
 设置内容窗口

 remoteContent 内容窗口对象
 return        返回结果(UI可以忽视,不作处理)
 */
int setRemoteContentWindow(Object remoteContent);
//示例代码(内容窗口为surfaceView对象)
engine.setRemoteContentWindow(surfaceView);

setPreviewVideoWindow

/**
在安卓系统中,为了在开启摄像头后能持续输出采集数据,需要通过该接⼝设置给 SDK 层⼀个空
surfaceview,但是系统中并不使⽤这个 View 来显示任何视频,⼀般将该 SurfaceView 设置为⼀个
像素即可。

previewVideo 窗口对象
return       返回结果(UI可以忽视,不作处理)
*/
int setPreviewVideoWindow(Object previewVideo);
//示例代码(内容窗口为surfaceView对象)
EVFactory.createEngine().setPreviewVideoWindow(surfaceView);

zoomRemoteWindow

/**
当在视频通话中时,如⽤户在视频窗⼝上使⽤⼿势功能,可以使⽤该⽅法对所显示的视频做相应的
放⼤,缩⼩,移动。

StreamType   : 视频流类型:视频、双流
float factor : 缩放⽐例
float cx     : 参考原点横轴坐标
float cy     : 参考原点纵轴坐标
*/
int zoomRemoteWindow(StreamType streamType,float factor,float cx,float cy);

setLocalVideoPreviewWindow

/**
 设置本地视频预览窗口,非呼叫中、需要预览本地视频时设置
 注:需要在 enablePreview() 之后调用
*/
int setLocalVideoPreviewWindow(Object localVideoPreview);

enablePreview

/**
启用预览,设置近端本地视频时,请开启此方法
*/
int enablePreview(boolean enable);

setBandwidth

/**
 设置呼叫带宽
 
 kbps   带宽大小
 return 返回设置结果
 */
int setBandwidth(int kbps);
//示例代码 设置2M带宽
engine.setBandwidth(2048);

getDevice

/**
 查看当前设置的音视频输入输出设备
 type :
 	AudioCapture
 	AudioPlayback
    VideoCapture
 */
engine.getDevice(DeviceType type);
//示例代码
//查看当前设置的视频输入输出设备
  EVEngine.Device current_device = engine.getDevice(EVEngine.DeviceType.VideoCapture);
//判断摄像头
        if(current_device==null || current_device.name == null || !current_device.name.endsWith("f")){
            LOG.info("current device is not front camera. device: " + current_device);
            List<EVEngine.Device> devices = engine.getDevices(EVEngine.DeviceType.VideoCapture);
            if(devices != null && devices.size() > 0) {
                for(int i = 0; i < devices.size(); i++) {
                    EVEngine.Device device = devices.get(i);
                    if(device != null && device.name != null && device.name.endsWith("f")) {
                        engine.setDevice(EVEngine.DeviceType.VideoCapture, device.id);
                        break;
                    }
                }
            }
        }

getDevices

/**
 查看当前设置的所有音视频输入输出设备
 type :
 	AudioCapture
 	AudioPlayback
    VideoCapture
 */
 engine.getDevices(DeviceType type)
 //示例代码
 //查找当前设备视频输入输出设备
  List<EVEngine.Device> devices = engine.getDevices(EVEngine.DeviceType.VideoCapture);
    if(devices != null && devices.size() > 0) {
       for(int i = 0; i < devices.size(); i++) {
           EVEngine.Device device = devices.get(i);
            //device.name 为摄像头name
            if(device != null && device.name != null && device.name.endsWith("f")) {
                engine.setDevice(EVEngine.DeviceType.VideoCapture, device.id);
                break;
               }
            }
         }
    }

setDevice

/**
 设置当前设备音视频输入输出
 type :
 	AudioCapture
 	AudioPlayback
    VideoCapture
 id: 当前设备标识
 */
engine.setDevice(DeviceType type, int id);
//示例代码
 //查找当前设备视频摄像头状态
  List<EVEngine.Device> devices = engine.getDevices(EVEngine.DeviceType.VideoCapture);
    if(devices != null && devices.size() > 0) {
       for(int i = 0; i < devices.size(); i++) {
           EVEngine.Device device = devices.get(i);
            //device.name 为摄像头name
            if(device != null && device.name != null && device.name.endsWith("f")) {
                engine.setDevice(EVEngine.DeviceType.VideoCapture, device.id);
                break;
               }
            }
         }
    }

setMaxRecvVideo

/**
 设置最大视频窗口路数
 
 num    窗口个数
 return 返回设置结果
 */
int setMaxRecvVideo(int num);

joinConference

/**
 用户加入会议

  conferenceNumber 会议号码
  displayName      用户在会议中的名称
  password         会议密码
  return           返回结果:0 成功,否则失败 
*/
int joinConference(String conferenceNumber, String displayName, String password);

joinConference

/**
  conferenceNumber 会议号码
  displayName      用户在会议中的名称
  password         会议密码
  CallType      会议类型
  return           返回结果:0 成功,否则失败 
*/
int joinConference(String conferenceNumber, String displayName, String password, EVEngine.CallType type);

joinConferenceWithLocation

/**
 用户执行匿名登录入会

  locationServer   定位服务器
  port             端口号
  conferenceNumber 会议号码
  displayName      用户在会议中的名称
  password         会议密码
  return           返回 SDK 处理结果,UI一般可忽略
  注:匿名方式入会执行:定位->匿名登录->注册视频服务->呼叫 (其中匿名登录以及注册视频服务均由SDK 处理)
 */
int joinConferenceWithLocation(String locationServer,int port, String conferenceNumber, String displayName, String password);

leaveConference

//挂断会议
int leaveConference();

//示例代码
 public void dropCall() {
    engine.leaveConference();
  }
//退出成功后,收到sdk的回调
void onCallEnd(EVCommon.CallInfo info){} 

cameraEnabled

/**
 获取本地视频状态
 
 return 返回结果 true 为关闭  false为开启
 注:和是否关闭本地视频接口结合使用
*/
boolean cameraEnabled();

enabledCamera

/**
 是否关闭本地视频

 enable true 为关闭  false为开启
 return 返回结果可忽视
 */
int enableCamera(bool enable);

//示例代码 注:先获取本地视频状态
public void enableVideo(boolean enable) {
  if(enable ^ engine.cameraEnabled()){
      engine.enableCamera(enable);
    }
 }

switchCamera

/**
 切换摄像头

 return 返回结果可忽略
 */
int switchCamera();

//示例代码
public void switchCamera() {
  engine.switchCamera();
}

micEnabled

/**
 获取⻨克⻛状态
 
 return 返回结果  true 关闭/false 打开
 注:和是否打开麦克风接口结合使用
*/
boolean micEnabled();

enableMic

/**
 是否启用麦克风

 mute   true 为关闭 false为开启
 return 返回结果可忽视
 */
void muteMic(boolean mute);

//示例 注:先获取⻨克⻛状态  
public void setMicMute(boolean mute) {
    if(mute ^ !engine.micEnabled()){
        engine.enableMic(!mute);
    }
}

remoteMuted

/**
 获取远端视频是否静音

 return 返回结果 true:非静音 / false :静音
 注:结合举手接口使用
 */
boolean remoteMuted();

requestRemoteUnmute

/**
 举手发言
 raise   true :发言申请已发送 /false :当前不能发言
 return  返回结果可忽略
 注:结合获取远端视频是否静音接口使用
 */
int requestRemoteUnmute(boolean raise);

//示例代码
public void handUp() {
  boolean remoteMute = engine.remoteMuted();
  LOG.info(" remoteMute : " + remoteMute);
  if(remoteMute){
    engine.requestRemoteUnmute(true);
  }else {
    engine.requestRemoteUnmute(false);
  }
}

setLayout

/**
 设置视频模式(主讲模式、画廊模式)

 layout 对象
 return 返回结果可忽视
 */
int setLayout(LayoutRequest layout);

//示例代码
// 1.画廊模式 (4 路)/ 主讲模式 (1路)
 public void setLayoutMode(int svcLayoutMode) {
    SVEngine.LayoutType type;
        if(svcLayoutMode == 0){
              type = SVEngine.LayoutType.typeAuto;
         }else if(svcLayoutMode == 2){
               type =SVEngine.LayoutType.type1;
         }else {
               type =SVEngine.LayoutType.type4;
         }
    SVEngine.LayoutRequest request = new SVEngine.LayoutRequest(
        SVEngine.LayoutMode.fromInt(svcLayoutMode),type,  			             	           SVEngine.LayoutPage.typeCurrent,                                                       EVEngine.VideoSize.VIDEO_SIZE_UNKNOWN,null);
    engine.setLayout(request);
 }

// 2.画廊模式 (6 路)/ 主讲模式 (1路)
 public void setLayoutMode(int svcLayoutMode) {
    SVEngine.LayoutType type;
        if(svcLayoutMode == 0){
              type = SVEngine.LayoutType.typeAuto;
         }else if(svcLayoutMode == 2){
               type =SVEngine.LayoutType.type1;
         }else {
               type =SVEngine.LayoutType.type6W;
         }
     SVEngine.LayoutRequest request = new SVEngine.LayoutRequest(
        SVEngine.LayoutMode.fromInt(svcLayoutMode),type,  			             	           SVEngine.LayoutPage.typeCurrent,                                                       EVEngine.VideoSize.VIDEO_SIZE_UNKNOWN,null);
     engine.setLayout(request);
 }
 

getNetworkQuality

/**
获取信号强弱
*/
float getNetworkQuality();

declineIncommingCall

/**
拒接P2P会议:
conferenceNumber 会议号码
*/
int declineIncommingCall(String conferenceNumber);

getStats

/**
 获取当前通话媒体统计信息
 return 返回结果为集合,通过循环展示本地音频、视频,远端音频、视频的传输信息
 具体参数如下:
  EVEngine.StreamType type;     媒体类型
  EVEngine.StreamDir dir;       媒体方向
  String payloadType;           传输协议类型
  float negoBandwidth;	        协商速率
  float realBandwidth;	        实际速率
  long cumPacket;               累计丢包数
  float fps;     			    帧率
  EVEngine.VideoSize resolution;分辨率
  long cumPacketLoss;  			累计丢包率
  float packetLossRate;  		丢包率
  boolean isEncrypted; 			是否加密
  int ssrc; 					同步源标识符
  String name;					名称
*/
ArrayList<StreamStats> getStats();

getPageInfo

/**
获取当前总页数和当前所在页:
PageInfo:
	int currentPage:当前页
	int pagesNumber:总页数
*/
SVEngine.PageInfo getPageInfo();

setPage

/**
设置滑动到下一页或者上一页的页数:
page_number:页数
*/
int setPage(int page_number);

setScreenCapture

/**
开启屏幕共享:
    cts :上下文
    mediaProjection :mediaProjection对象
    mDisplay : Display对象
    handler  :开启一个handler
 注:调用此接口时,回调"onContent"如果收到contentInfo.status类型为"Declined"时,代表没有权限,不	可共享。
*/
void setScreenCapture(Context cts,MediaProjection mediaProjection, Display mDisplay, Handler handler);

stopContent

/**
停止屏幕共享
*/
int stopContent() ;

setLandscape

/**
启动屏幕共享时,检测横竖屏时,调用此接口:
enable : true 横屏/ false 竖屏
*/
void setLandscape(boolean enable);

onError

/** 
SDK返回错误类型,分别由五种类型:
	EVErrorTypeSdk = "Sdk";        SDK错误
    EVErrorTypeServer = "Server";  服务器错误
    EVErrorTypeLocate = "Locate";  定位错误
    EVErrorTypeCall = "Call";      呼叫会议错误
    EVErrorTypeUnknown = "Unknown";未知错误
*/
void onError(EVCommon.EVError err){};

onLoginSucceed

/**
 登录成功回调
 user 返回用户基本信息
 */
void onLoginSucceed(EVCommon.UserInfo user){}

onRegister

/**
 视频服务注册状态(注册状态变化的时候将会走此回调)

 registered true为成功 false为失败
 */
void onRegister(boolean registered){}

onCallConnected

/**
 入会成功之后走此回调,具体参数如下:
 	boolean isAudioOnly;         可忽略
 	boolean contentEnabled;      可忽略
 	String  peer;                邀请入会者名字
 	String  conferenceNumber;    邀请入会号码
 	String  password;            入会密码
 	EVEngine.EVError err;        错误信息
 	EVEngine.SvcCallType type:
 	       	SvcCallConf 普通会议 正常入会
 	        SvcCallP2P  点对点 主叫本地摄像头可打开,入会成功走onCallPeerConnected()此回调
 	EVEngine.CallAction action:
 	
 */
void onCallConnected(EVCommon.CallInfo info){}

onCallPeerConnected

/**
	点对点呼叫成功走此回调:
	info 详情请看"onCallConnected()"回调
 */
void onCallPeerConnected(EVCommon.CallInfo info){}

onCallEnd

/**
 会议结束之后走此回调
 info 会议信息:
 
 boolean isAudioOnly;         可忽略
 boolean contentEnabled;      可忽略
 String  peer;                可忽略
 String  conferenceNumber;    可忽略
 String  password;            可忽略
 EVEngine.EVError err;        错误信息,请看错误码详情
 EVEngine.SvcCallType type:  可忽略
 EVEngine.CallAction action: 可忽略
 */
void onCallEnd(EVCommon.CallInfo info){}

onContent

/**
 接收到内容分享或者内容分享结束的回调
 ContentInfo:
 	    boolean enabled;          是否有双流  true 是,false 否
        EVEngine.StreamDir dir:  视频流方向
        		 Upload   发双流
        		 Download 收双流
        EVEngine.StreamType type: 双流类型:
        		 Content    传统视频双流
        		 WhiteBoard 白板
 */
void onContent(EVCommon.ContentInfo info){}

onDownloadUserImageComplete

/**
 用户头像下载成功回调

 path 保存路径
 */
void onDownloadUserImageComplete(String  path){}

onUploadUserImageComplete

/**
 用户上传头像成功回调

 path 头像路径
 */
void onUploadUserImageComplete(String path){}

onLayoutIndication

/**
 会议中布局的变化,具体参数如下:
 
   EVEngine.LayoutMode mode;         本地布局为 Speaker/Gallery 模式
   EVEngine.LayoutMode settingMode;  终端在服务器上设置的 AutoMode/Speaker/Gallery模式
   EVEngine.LayoutType type;         布局类型
   boolean modeSettable;             当前会议是否允许设置布局模式
   String speakerName;               当前会议发言者名称
   int speakerIndex;                 当前会议发言者所对应的site集合下标
   List<EVEngine.Site> sites;
    Site {
   		int window;          视频流windowid
        boolean isLocal;     true 本地视频流  false 远端视频流
        String name;         视频流显示的名字
        long deviceId;       视频流deviceid
        boolean micMuted;    视频流是否是本地静音
        boolean remoteMuted; 视频流是否被服务器静音
   	}
 
 */
void onLayoutIndication(EVCommon.LayoutIndication layout){}

onLayoutSiteIndication

/**
 会议中状态发生变化,具体参数如下:
 
 int      window;      视频流windowid
 boolean  isLocal;     true 本地视频流/false 远端视频流
 String   name;        视频流显示的名字
 long     deviceId;    视频流deviceid
 boolean  micMuted;    视频流是否是静音
 boolean  remoteMuted; 视频流是否被服务器静音
 
 */
void onLayoutSiteIndication(EVCommon.Site site){}

onLayoutSpeakerIndication

/**
 当前会议的发言者,具体参数如下:
 String speakerName;  当前会议发言者名称
 int speakerIndex;    当前会议发言者下标
 */
void onLayoutSpeakerIndication(EVCommon.LayoutSpeakerIndication speaker){}

onMuteSpeakingDetected

/**
当静音时说话,SDK提示打开麦克风
 */
void onMuteSpeakingDetected(){};

onJoinConferenceIndication

/**
 收到会议邀请的通知,具体参数如下:
 	boolean isAudioOnly;           可忽略
 	boolean contentEnabled;        可忽略
 	String  peer;                  邀请入会者名字
 	String  conferenceNumber;      邀请入会号码
 	String  password;              入会密码
 	EVEngine.EVError err;          错误信息
 	EVEngine.SvcCallType type;     邀请类型:SvcCallConf 普通会议邀请,SvcCallP2P 点对点呼叫
 	EVEngine.CallAction  action
                      {
						SvcNoAction           没有呼叫动作
 						SvcIncomingCallRing   呼入振铃
 						SvcIncomingCallCancel 呼入超时,取消呼叫
                       }
 */
void onJoinConferenceIndication(EVCommon.CallInfo info){}

onRecordingIndication

/**
 是否有录制终端参与,具体参数如下:
 	EVEngine.RecordingState states; //  None(停止)/On(开始)/Pause(暂停)
 	boolean live;  // 录制false / 直播true
 */
void onRecordingIndication(EVCommon.RecordingInfo state){}

onMessageOverlay

/**
 会议字幕相关通知,具体参数如下:
  	boolean enable;          是否显示字幕
  	String content;          字幕内容
  	int displayRepetitions;  重复次数
  	int displaySpeed;        滚动速度
  	int verticalBorder;      垂直位置
  	int transparency;        字体透明度
  	int fontSize;            字体大小
  	String foregroundColor;  字体颜色
  	String backgroundColor;  字体背景颜色
 */
void onMessageOverlay(EVCommon.MessageOverlay msg){}

onWarnMessage

/**
 会议中网络和带宽相关通知提示,分别为一下四种:
 NetworkPoor               网络差
 NetworkVeryPoor		   网络极差
 BandwidthInsufficient	   带宽不足
 BandwidthVeryInsufficient 带宽严重不足
 UnmuteAudioNotAllowed     当前会议禁止解除静音 
 UnmuteAudioIndication     主持人正在解除静音,是否同意解除静音?
 */
void onWarnMessage(EVCommon.Warning warnMessage){}

onNetworkQuality

/**
获取信号强弱
*/
void onNetworkQuality(float qualityRating){}

onParticipant

/**
获取当前会议总人数
*/
void onParticipant(int number){}

onMicMutedShow

/**
修改当前会议静音图标状态 : 
0 非静音
1 静音
*/
void onMicMutedShow(int mic_muted){};

onPeerImageUrl

/**
获取P2P主叫头像
*/
void onPeerImageUrl(String imageUrl){};

onPageInfo

/**
页数发生改变的回调信息:
PageInfo:
	int currentPage:当前页
	int pagesNumber:总页数
*/
public void onPageInfo(SVEngine.PageInfo info) {}

错误码以及枚举类型

错误码

//SDK返回错误信息类型
public enum ErrorType {
    public static final String EVErrorTypeSdk = "Sdk";//SDK错误
    public static final String EVErrorTypeServer = "Server";//服务器错误
    public static final String EVErrorTypeLocate = "Locate";//定位错误
    public static final String EVErrorTypeCall = "Call";//呼叫错误
    public static final String EVErrorTypeUnknown = "Unknown";//未知错误
}

//登陆返回错误码
public class LoginResultEvent {
    int LOGIN_ERROR_10009;//账号或密码错误
    int LOGIN_ERROR_1101;//密码错误。账号将在 x 次失败尝试后被锁定(5 分钟)
    int LOGIN_ERROR_1102;//密码错误次数已达上限,账号已被锁定,5分钟后自动解除
    int LOGIN_ERROR_8;//服务器不可达
    int LOGIN_ERROR_9;//无效服务器
}

//呼叫会议返回的错误码
public class ResourceUtils {
    int CALL_ERROR_1001 ;//无效的会议室号码
    int CALL_ERROR_2001 ;//您呼叫的云平台尚未激活
    int CALL_ERROR_2003 ;//没有可用的会议端口资源
    int CALL_ERROR_2005 ;//会议还没有开始
    int CALL_ERROR_2007 ;//呼叫超时,请重试
    int CALL_ERROR_2009 ;//您呼叫的群组尚未绑定云会议室
    int CALL_ERROR_2011 ;//您呼叫的云会议室已过期
    int CALL_ERROR_2023 ;//当前入会人数已达上限
    int CALL_ERROR_2024 ;//当前与会人数已达组织端口上限
    int CALL_ERROR_2025 ;//当前与会人数已达平台会议端口上限
    int CALL_ERROR_2031 ;//只允许会议室拥有者激活会议室
    int CALL_ERROR_2033 ;//本会议不允许匿名呼叫
    int CALL_ERROR_2035 ;//试用期已过期
    int CALL_ERROR_4049 ;//呼叫失败,对方正在通话中
    int CALL_ERROR_4051 ;//对方不在线
    int CALL_ERROR_4055 ;//加入失败,会议已锁定
    int CALL_ERROR_4057 ;//该会议室已被占用
    int CALL_ERROR_4067 ;//仅允许本公司用户加入
    int CALL_ERROR_4069 ;//仅允许被邀请人加入
    
    int MRU_NORMAL = 100;//会议结束或被主持人挂断
    int MRU_OPERATOR_DISCONNECT = 101;//会议结束或被主持人挂断
    int EP_NO_PACKET_RECEIVED = 11;//网络异常,请检查网络
    int MRU_NO_PACKET_RECEIVED = 102;//网络异常,请检查网络
}

枚举类型


/**
  枚举类型
*/
public enum RegisterState {
    IDLE,//初始化
    SUCCESS,//注册成功
    FAILED//注册失败
}

public enum CallState {
    IDLE,//初始化
    CONNECTING,//呼叫中
    AUTHORIZATION,//当会议有密码,调用此类型
    RING,//邀请入会
    CONNECTED//呼叫成功
}

public enum RecordingEvent {
    ON,//录制结束
    OFF//开始录制
}

public enum _EV_LAYOUT_MODE{//本地布局类型
    EV_LAYOUT_GALLERY_MODE =  0, 
    EV_LAYOUT_SPEAKER_MODE =  1
}

public enum _EV_LAYOUT_TYPE{//布局类型
    EV_LAYOUT_TYPE_AUTO       = -1, 
    EV_LAYOUT_TYPE_1          = 101,
    EV_LAYOUT_TYPE_2H         = 201,
    EV_LAYOUT_TYPE_2V         = 202,
    EV_LAYOUT_TYPE_2H_2       = 203,
    EV_LAYOUT_TYPE_2V_2       = 204,
    EV_LAYOUT_TYPE_2_1IN1     = 205,
    EV_LAYOUT_TYPE_2_1L_1RS   = 207,
    EV_LAYOUT_TYPE_3_1T_2B    = 301,
    EV_LAYOUT_TYPE_3_2T_1B    = 302,
    EV_LAYOUT_TYPE_3_1L_2R    = 303,
    EV_LAYOUT_TYPE_3_2IN1     = 304,
    EV_LAYOUT_TYPE_1P2W       = 305,
    EV_LAYOUT_TYPE_4          = 401,
    EV_LAYOUT_TYPE_4_3T_1B    = 402,
    EV_LAYOUT_TYPE_4_1L_3R    = 403,
    EV_LAYOUT_TYPE_4_1T_3B    = 404,
    EV_LAYOUT_TYPE_4_3IN1     = 405,
    EV_LAYOUT_TYPE_5_1L_4R    = 501,
    EV_LAYOUT_TYPE_5_4T_1B    = 502,
    EV_LAYOUT_TYPE_5_1T_4B    = 503,
    EV_LAYOUT_TYPE_6          = 601,
    EV_LAYOUT_TYPE_6W         = 602,
    EV_LAYOUT_TYPE_2P4W       = 603,
    EV_LAYOUT_TYPE_6CP        = 604,
    EV_LAYOUT_TYPE_8          = 801,
    EV_LAYOUT_TYPE_9          = 901,
    EV_LAYOUT_TYPE_9_1IN_8OUT = 902,
    EV_LAYOUT_TYPE_9_8T_1B    = 903,
    EV_LAYOUT_TYPE_9_1T_8B    = 904,
    EV_LAYOUT_TYPE_10         = 1001,
    EV_LAYOUT_TYPE_2TP8B      = 1002,
    EV_LAYOUT_TYPE_2CP4L4R    = 1003,
    EV_LAYOUT_TYPE_12W        = 1201,
    EV_LAYOUT_TYPE_13         = 1301,
    EV_LAYOUT_TYPE_1LTP12     = 1302,
    EV_LAYOUT_TYPE_16         = 1601,
    EV_LAYOUT_TYPE_1TLP16     = 1701,
    EV_LAYOUT_TYPE_1CP16      = 1702,
    EV_LAYOUT_TYPE_20         = 2001,
    EV_LAYOUT_TYPE_20_SQUARE  = 2002,
    EV_LAYOUT_TYPE_1TLP20     = 2101,
    EV_LAYOUT_TYPE_1CP20      = 2102,
    EV_LAYOUT_TYPE_25         = 2501,
    EV_LAYOUT_TYPE_30         = 3001,
    EV_LAYOUT_TYPE_30_SQUARE  = 3002,
    EV_LAYOUT_TYPE_36         = 3601
}

public enum _EV_SVC_CALL_TYPE {
    EV_SVC_CALL_CONF = 0,// 普通会议 正常入会  
    EV_SVC_CALL_P2P = 1 //SvcCallP2P  点对点 
}
public enum EV_SVC_CALL_ACTION_CLI{
    EV_SVC_NO_ACTION = 0,   // 没有呼叫动作
    EV_SVC_INCOMING_CALL_RING = 1, // 呼入振铃
    EV_SVC_INCOMING_CALL_CANCEL = 2 // 呼入超时,取消呼叫
}

public enum _EV_RECORDING_STATE {
    EV_RECORDING_STATE_NONE = 0,//通知
    EV_RECORDING_STATE_ON = 1,//开始
    EV_RECORDING_STATE_PAUSE = 2//暂停
} 

public enum _EV_STREAM_DIR {
	EV_STREAM_UPLOAD = 0,//发双流
	EV_STREAM_DOWNLOAD = 1//收双流
}

typedef enum _EV_STREAM_TYPE {
	EV_STREAM_CONTENT = 0//传统视频双流
	EV_STREAM_WHITE_BOARD = 1 //白板
}


上次更新: 9/16/2022, 5:10:26 PM