# 萤石云开放平台Android 音视频SDK使用说明
简介
本文档用于说明萤石开放平台音视频SDK Android版本接口使用。
名词解释
| 名词 | 释义 | 
|---|---|
| accessToken | 访问令牌,由server返回给client用于认证 | 
| AppKey | 应用程序key,AppKey的申请可以参阅: https://open.ys7.com/view/app/app_edit.html | 
| deviceSerial | 设备序列号 | 
| CameraNo | 设备通道号 | 
| deviceSerial+CameraNo | 摄像头唯一标志 | 
| 多方音视频 | 音视频通话的会议模式,可支持最多3方通话 | 
功能介绍
| 功能 | 说明 | 
|---|---|
| 单设备呼叫 | 针对类手表设备,发起呼叫和通话功能,结合推送可做双向呼叫 | 
| 多方通话 | 创建多方通话房间,并通过房间号,密码邀请多方进入房间通话 | 
| 多方音频,视频,mic,摄像头控制 | 可根据业务需求操作对方音视频展示和本地音视频采集 | 
工程配置
   环境准备
   支持 Android Studio 1.4 以上
   支持 JDK 7.0 以上版本
   支持 Android 手机系统 4.0 以上版本
创建应用
首先,你需要在萤石开放平台官网的 “ 开发者服务-我的应用-应用秘钥 ” 查看Appkey。
安装 SDK
SDK 的安装方式
使用 Gradle 获得
如果是之前采用过直接下载方式的需要删除之前拷贝进来的所有so库文件以及jar包
dependencies {
    /*萤石SDK核心模块,必须依赖*/
    implementation 'com.ezviz.sdk:ezviz-sdk:4.16.1'
    /*视频通话模块,音视频通话必须依赖*/
    implementation 'com.ezviz.sdk:videotalk:1.2.0'
 }
ps:最新版本请查看 https://bintray.com/open-ezviz/mobile-sdk
配置工程(前提准备)
参考 萤石云开放平台Android SDK使用说明 完成SDK初始化等相关操作。
音视频SDK接入
步骤
- 创建通话控制器
- 设置通话回调
- 开启音视频通话
- 关闭音视频通话
- 释放音视频控制器
布局准备
<!-- 本机相机,可切换前置后置,开关录像等,sdk初始化必须 -->
<FrameLayout
     android:id="@+id/vg_child_watch_video_talk_camera"
     android:layout_width="150dp"
     android:layout_height="150dp"
     android:background="@android:color/black">
    <com.ezviz.sdk.videotalk.EvcLocalWindowView
        android:id="@+id/view_child_watch_video_talk_camera"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:keepScreenOn="true"/>
</FrameLayout>
<!-- 对方画面,建议使用 TextureView-->
<FrameLayout
    android:id="@+id/vg_child_watch_video_talk_player"
    android:layout_width="150dp"
    android:layout_height="150dp"
    android:background="@android:color/black">
    <TextureView
        android:id="@+id/view_child_watch_video_talk_player"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>
手表(单设备呼叫)场景
1.创建通话控制器
    // 带参数配置
    EzvizVideoCall ezVideoCall = new EzvizVideoCall(EvcLocalWindowView, VideoCallParams, EvcMsgCallback)
    // 不带参数配置
    EzvizVideoCall ezVideoCall = new EzvizVideoCall(EvcLocalWindowView, EvcMsgCallback)
    ezVideoCall.setLogPrintEnable(true) // debug模式下开启日志打印
    注意在EvcMsgCallback的回调方法中设置画面视图,用于显示接通后对方的画面
    void onCallEstablished(int width, int height, int clientId) {
        runOnUiThread {
            mEzvizVideoCall.setDisplay(new Surface(mPlayerView.getSurfaceTexture()), clientId)
        }
    }
    // 建议对 TextureView 设置如下监听,否则容易出现黑屏或画面尺寸不同步(以下是 kotlin 语法)
    private val mSurfaceTextureListener = object : TextureView.SurfaceTextureListener{
        var mLastSurface: SurfaceTexture? = null
        override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {
            LogUtil.d(TAG, "onSurfaceTextureAvailable")
            mEzvizVideoCall?.setDisplay(Surface(surface!!))
            if (mLastSurface == null){
                mEzvizVideoCall?.setDisplay(Surface(surface))
                mLastSurface = surface;
            }else{
                mEzvizVideoCall?.refreshWindow()
            }
        }
        override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
            // do nothing
        }
        override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean {
            mEzvizVideoCall?.setDisplay(null)
            return true
        }
        override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
            mEzvizVideoCall?.setDisplay(Surface(surface!!))
        }
    }
2.开启音视频通话(呼叫)
    /**
     * 呼叫设备
     * @param deviceSerial
     * @param selfId
     */
    public void callVideoTalk(String deviceSerial, String selfId)
    /**
     * 接听呼叫
     * @param deviceSerial
     * @param selfId
     * @param roomId
     */
    public void answerVideoTalk(String deviceSerial, String selfId, int roomId) 
    /**
     * 拒绝接听
     * @param deviceSerial
     * @param selfId
     * @param roomId
     */
    public void refuseVideoTalk(String deviceSerial, String selfId, int roomId)
    // ======== 以上为预置功能,以下为完整方法,应对不通场景,一般不建议使用 =================
    /*视频通话服务器域名*/
    private const val SERVER_DOMAIN = "vtm.ys7.com"
    /*视频通话服务器地址*/
    private const val SERVER_PORT = 8554
    /*对应手表联系人ID*/
    private const val SELF_ID = "1234567891"
    /* 完整方法,应对不同场景 */
    private fun startVideoTalk(roomId: Int) {
        if (!hasNeededPermissions()){
            return
        }
        val param = EvcParam()
        param.operation = operation  // EvcOperationEnum.CALL 呼叫,EvcOperationEnum.ANSWER 接听
        param.roomId = roomId           // 接听时传,呼叫时不传
        param.serverIp = SERVER_DOMAIN
        param.serverPort = SERVER_PORT
        param.selfClientType = EvcParamValueEnum.EvcClientType.ANDROID_PHONE  // 固定
        param.otherClientType = EvcParamValueEnum.EvcClientType.CHILD_WATCH   // 固定
        param.streamType = EvcParamValueEnum.EvcStreamType.VIDEO_TALK         // 固定
        param.otherId = mWatchSerial  // 设备序列号
        param.selfId = mSelfId        //对应手表联系人ID   一般设备呼叫可不传
        mEzvizVideoCall?.startVideoTalk(param)
    }
3.结束通话和释放资源
    // 停止通话
    mEzvizVideoCall?.stopVideoTalk() 
    // 页面销毁前释放资源
    public void onDestroy() {
        super.onDestroy()
        if (mEzvizVideoCall != null) {
            mEzvizVideoCall?.release()
        }
    }
安全帽等(单设备)纯音频场景
// 创建通话控制器
EzvizVoiceCall mEzvizVoiceCall = new EzvizVoiceCall(EvcMsgCallback);
// 开启通话
mEzvizVoiceCall.startVideoTalk(Context context, String deviceSerial);
// 挂断,结束通话
mEzvizVoiceCall.stopVoiceTalk();
// 释放资源  onDestry() 中调用
mEzvizVoiceCall.release();
// ========== 以下为通话中功能方法 ==========
// 开启声音(对方声音)
mEzvizVoiceCall.openAudio();
// 静音(对方声音)
mEzvizVoiceCall.stopAudio();
多方会议通话
1. 基本流程
初始化
    /**
    * EvcLocalWindowView 本机视图
    * nickname 当前设备在会议中显示的名称
    * enableCamera 进入会议时是否开启相机
    * enableMic 进入会议时是否开启麦克风
    * EZMeetingCall.CallBack 会议通话回调
    * VideoCallParams 相机,画面, fps,I帧等参数 -- 可不传
    */
    EZMeetingCall mEZMeetingCall = new EZMeetingCall(EvcLocalWindowView view, String nickname, boolean enableCamera, boolean enableMic, EZMeetingCall.CallBack mMettingCallback, VideoCallParams)
    // debug 下开启日志
    mEZMeetingCall.setLogPrintEnable(true)
当前用户:创建会议,可带设备创建
    // 1. 创建会议同时,呼叫一台设备
    mEZMeetingCall.createMeetingWithDevice(mDeviceSerial?:"", mChannelId, mPassword)
    // 2. 单纯创建会议房间
    mEZMeetingCall.createMeeting(mPassword)
其他用户:主动加入房间
    mEZMeetingCall.joinMeeting(mInputtedRoomID, mPassword)
退出会议房间
    if (mEZMeetingCall != null) {
        mEZMeetingCall?.quitMeeting()
        mEZMeetingCall = null
    }
补充,通话中的功能
    // 当有用户加入会议(房间)时,调用该方法显示用户画面, cameraView 为 TextureView
    mEZMeetingCall.showJoinUser(cameraView, clientId)
    // 当有用户从房间退出时调用,自动销毁画面
    mEZMeetingCall.leaveRoom(this.clientId)
2. 创建/加入房间回调
    EZMeetingCall.CallBack {
        // 异常回调
        override fun onError(code: Int, message: String) {
            showToast("onError: $code\n$message")
            if (code == 50017) {
                // 已被人接听或房间已满
            } else if (code == 50103 || code == 50106) {
                   // 已被人接听或房间已满
            } else if (code == 10152) {
                // 没有麦克风权限
            } else if (code == 20153) {
                // 没有相机权限
            } else if (code == 50105) {
                // 设备正在通话中
            } else if (code == 50007 || code == 50005) {
                // 网络断开,退出房间
                finish()
            } else if(code == EvcErrorMessage.MULTI_CALL_FAILED_DEVICE_ENCRYPT.code){ // 300002
                showToast("不支持加密设备,退出房间")
                finish()
            }
        }
        // 房间创建回调
        override fun onRoomCreated(roomId: Int) {
            showToast("onRoomCreated: $roomId")
            // 返回房间号,建议自行保存,用于其他端加入会议房间使用
        }
        // 有用户进入房间
        override fun onJoinRoom(roomId: Int, clientId: Int, username: String) {
            runOnUiThread {
                // 展示进入房间的用户 cameraView 为 TextureView
                mEZMeetingCall.showJoinUser(cameraView, clientId)
            }
        }
        // 用户 clientId 的第一帧画面到来时回调
        override fun onFirstFrameDisplayed(width: Int, height: Int, clientId: Int) {
        }
        // 当用户 clientId 退出房间时回调
        override fun onQuitRoom(roomId: Int, clientId: Int) {
            runOnUiThread {
                // 解绑用户的视图
                mEZMeetingCall.leaveRoom(clientId)
            }
        }
        // 网络较差时回调
        override fun onBadNet(delayTimeMs: Int) {
            runOnUiThread {
                showToast(getString(R.string.video_talk_signal_weak))
            }
        }
    }
3. 补充功能,通话中
    // 对会议中某个用户进行静音,只在当前设备生效
    mEZMeetingCall.mute(isNute, clientId)
    // 是否在通话中
    mEZMeetingCall.isTalking()
    // 是否开启相机
    mEZMeetingCall.enableCamera(boolean isEnable)
    // 是否开启麦克风
    mEZMeetingCall.enableMic(boolean isEnable)
    // 前置后置切换
    mEZMeetingCall.switchCamera()
4. 注意
    // 建议加上以下代码,保证在回到桌面后,再次回到app中 相机功能正常
    override fun onPause() {
        super.onPause()
        mEZMeetingCall?.enableCamera(false)
    }
    override fun onRestart() {
        super.onRestart()
        mEZMeetingCall?.enableCamera(true)
    }
若文档有未尽之处,请参考以下资料。 1.萤石SDK demo,点击跳转到github下载