# Tutorial: 开始集成音视频通话

## 开始集成音视频通话

## 前提条件

1. 本地化部署公司的媒体服务器，获取到服务器地址、AccessKey和SecretKey。
2. 为了体验完整的 RTC 能力，建议开发时使用 `http://localhost` ，生产环境用 `https://[域名]` 访问页面，参考文档页面访问协议限制说明。
3. 为了避免防火墙安全策略限制正常的 RTC 数据传输，需要对防火墙策略进行设置。
4. 为了保证通话体验，建议在正式开始音视频通话前，进行设备检测和浏览器兼容性检测，可以参考文档浏览器兼容信息，通话前环境与设备检测。

## 集成 SDK

SDK 提供了 UMD类型的模块，兼容AMD、CommonJS和全局变量等多种使用场景，满足在不同类型项目中集成。

### Script 集成

1. 在您的 Web 页面中添加如下代码即可：

    <script type="text/javascript" src="js/rtc-4.0.7.4.min.js?timestamp=202403061417"></script>

资源下载

- 单击下载 SDK 及示例代码

## SDK 使用逻辑概览

基本概念

您在使用 RTC Web SDK 时，会接触到以下概念：

- AVDEngine类，其实例代表一个本地客户端。AVDEngine的对象方法提供了获取摄像头列表、麦克风列表、检查浏览器兼容性、实例化房间类、控制日志等级及日志保存等功能等功能。

实现音视频通话基本逻辑

1. 调用 var AVDEngine = ModuleBase.use(ModulesEnum.avdEngine); var avdEngine = new AVDEngine()；方法来实例化创建avdEngine对象。
2. 调用 var room = avdEngine.obtainRoom(roomId)方法来实例化房间类。
3. 调用 room.joinEfficient()进入房间。
4. 在进入房间后，可以开启摄像头和麦克风并发布到房间。
   - 调用 room.selfUser.videos[0].previewAndPublish(element)开启摄像头并发布到房间。
   - 调用 room.selfUser.audio.openMicrophone(element)开启麦克风并发布到房间。
5. 当一个远端用户发布了音视频后，本端会收到远端视频发布通知，本端需要做订阅操作，音频SDK会默认自动订阅。您需要通过如下方式来播放远端音视频：
   - 在进房间后监听user.addCallback(UserCallback.subscrible_camera_result, onSubscribleCameraResult)事件，就能收到该远端用户的发布视频。
   - 在进房间后监听 user.addCallback(UserCallback.subscrible_microphone_result, onSubscribleMicrophoneResult)事件，就能收到该远端用户的发布音频。
   - 在onSubscribleCameraResult事件回调函数中，调用room.selfUser.attachVideoElementMediaStream(remoteVideoElement, stream)方法播放远端视频。
   - 在onSubscribleMicrophoneResult事件回调函数中，调用room.selfUser.attachAudioElementMediaStream(remoteAudioElement, stream)方法播放远端音频。

## 创建 AVDEngine 对象

    var AVDEngine = ModuleBase.use(ModulesEnum.avdEngine);
    var avdEngine = new AVDEngine();

## 创建 Room对象

    var room = avdEngine.obtainRoom(roomId);

## 进入房间

调用room.joinEfficient()进入房间。通常在`开始通话`按钮的点击回调里进行调用。 关键参数：

- `serverURI`: MCU服务器地址。
- `accessToken`: 访问令牌。
- `userId`: 用户 ID，由您指定。
- `userName`: 用户名，由您指定。
- `userData`: 用户扩展字段。 可以做为应用层逻辑上的可自定义字段使用，如填入角色。

    room.joinEfficient(serverURI, accessToken, userId, userName, userData, password).then(function(){
        console.log('进房成功');
    }).otherwise(function(error){
        console.error('进房失败 ', error);
    });

## 开启摄像头、麦克风

### 开启摄像头

使用room.selfUser.videos[0].previewAndPublish(element) 方法开启摄像头，并发布到房间。

    // 为了预览摄像头画面，您需在 DOM 中放置一个 HTMLElement，可以是一个 div 标签，假设其 id 为 local-video。
    var view = 'local-video';
    var video = room.selfUser.videos[0]; //SDK支持多摄像头的同时开启及发布，这里voides[0]表示取了摄像头设备集里的第一摄像头
    video.previewAndPublish(view).then(function(){
        console.log('视频开启及发布成功');
    }).otherwise(function(error){
        console.error('视频开启及发布失败 ', error);
    });

### 开启麦克风

使用room.selfUser.audio.openMicrophone(element)方法开启麦克风，并发布到房间。

    var audio = room.selfUser.audio;
    audio.openMicrophone().then(function(){
        console.log('麦克风开启及发布成功');
    }).otherwise(function(error){
        console.error('麦克风开启及发布失败 ', error);
    });

## 播放远端音视频

### 播放远端音频

默认情况下，SDK 会自动订阅远端音频，您无需调用 API 来订阅操作。

在进房后监听UserCallback.subscrible_microphone_result事件，在收到远端声频订阅反馈事件时，本端可以播放远端声频流。

- 需要注意的是：如果用户在进房前没有与页面产生过交互，自动播放音频可能会因为【浏览器的自动播放策略限制】而失败，您需要添加用户交互例如弹窗引导用户点击同意按钮等。

      user.addCallback(UserCallback.subscrible_microphone_result, onSubscribleMicrophoneResult);

      /**
       * 订阅远端音频流反馈
       * @param {Object} stream－ 远端音频流
       * @param {Object} userId－ 所属用户ＩＤ
       * @param {Object} userName－所属用户名称
       */
      function onSubscribleMicrophoneResult(stream, userId, userName) {
          // 为了听远端声音，您需在 DOM 中放置一个audio HTMLElement，可以是一个 div 标签，假设其 id 为 remote-audio。
          var view = 'remote-audio';
          room.selfUser.attachAudioElementMediaStream(view, stream);
      }

### 播放远端视频

在进房后监听UserCallback.publish_camera_notify事件，在收到远端视频发布通知事件时，通过video.subscrible()进行订阅操作。然后通过监听 UserCallback.subscrible_camera_result事件，在收到远端视频订阅反馈事件时，本端可以播放远端视频流。

这里需要注意的，如果是后加入会议，那加会登陆前，会议中已经发布的视频资源,是不会发送UserCallback.publish_camera_notify事件的，需要在加会成功后添加方法处理。

    function joinSuccess() {
        onPublishCameraNotify(roomClient.pubVideos); //加会登陆前，会议中已经发布的视频资源,采取订阅处理
        onPublishScreenNotify(roomClient.pubScreens); //加会登陆前，会议中已经发布的桌面共享资源,采取订阅处理);、
    }

    user.addCallback(UserCallback.publish_camera_notify, onPublishCameraNotify);
    user.addCallback(UserCallback.subscrible_camera_result, onSubscribleCameraResult);

    /**
     *远端用户发布视频通知，在回调事件里可以做订阅操作
     *@param {Object[]} videos － 远端已发布的视频对象集
    */
    function onPublishCameraNotify(videos) {
        videos.forEach(function(video) {
             video.subscrible().then(function(){
    		      console.log("订阅视频成功,videoId：",video.id);
    	     }).otherwise(function(error){
                  console.error('订阅视频失败,videoId：",video.id);
             });
        }
    }

    /**
     * 订阅远端视频流反馈
     * @param {Object} stream － 远端视频流
     * @param {Object} userId － 所属用户ＩＤ
     * @param {Object} userName－ 所属用户名称
     * @param {Object} cameraId－ 摄像头设备ＩＤ
     */
    function onSubscribleCameraResult(stream, userId, userName, cameraId) {
        // 为了预览摄像头画面，您需在 DOM 中放置一个 HTMLElement，可以是一个 div 标签，假设其 id 为 remote-video。
        var view = 'remote-video';
        room.selfUser.attachVideoElementMediaStream(view, stream);
    }

## 退出房间

调用room.leave()方法退出房间，结束音视频通话。

    var reason = 1; //退会原因
    room.leave(reason).then(function() {
        console.log("退会成功");
    });

处理被踢

除了用户主动退出房间之外，用户也有可能因为如下原因被踢出房间，此时 SDK 会抛出RoomCallback.leave_indication事件，这时不需要调用 `room.leave()` 退房，SDK 自动进入退房状态。

1. `reason==804`：两个相同 userId 的用户进入相同房间，前一个进房的用户会被踢出。同名用户同时进入同一房间是不允许的行为，可能会导致双方音视频通话异常，应避免出现这种情况。
2. `reason == 808`：通过调用服务端踢人Restful接口或时SDK中调踢人接口，将某个用户踢出某个房间，该用户会收到被踢事件。

另一种场景是某个用户把房间关闭了，关闭房间之后，该房间的所有用户都会收到RoomCallback.close_room_notify事件，所有用户都会被踢；

1. reason==809：服务没有授权用户入会(最多2个用户),超过15分钟服务器主动关闭房间强制踢人。
2. `reason == 810`：SDK调用接口关闭会议。g
3. `reason == 811`：服务更新维护时关闭会议。
4. `reason == 815`：调用restful接口关闭会议。

    room.addCallback(RoomCallback.leave_indication, onLeaveIndication);

    /**
     * @desc 被踢出会议室
     * @param {Object} reason - 被踢原因
     * @param {Object} userId - 踢人的操作者
     */
    function onLeaveIndication(reason, userId) {
    	var user = room.getUser(userId);
    	var userName = '';
    	if(user != null) {
    		userName = user.name;
    	}

    	if(reason == 804) {
    		//同一个userid加会， 把前一个人踢下线
    		 console.log("你已在另一个地方登陆，被踢出会议室。被踢原因：" + reason);
    	} else if(reason == 808) {
    		//调用Restful接口把人踢下线或SDK调踢人接口通知会议中的第三方用户
    		 console.log("你被" + userName + "踢出会议室。被踢原因：" + reason);
    	} else {
    		 console.log("你被" + userName + "踢出会议室。被踢原因：" + reason);
    	}
    }

    room.addCallback(RoomCallback.close_room_notify, onCloseRoomNotify);

    /**
     * @desc 收到关闭房间通知，退出会议操作
     * @param {int} roomId - 房间ID
     * @param {int} reason  -关闭会议原因
     */
    function onCloseRoomNotify(roomId, reason) {
    	if(reason == 809) {
    		console.log("服务没有授权用户入会(最多2个用户),超过15分钟服务器主动关闭房间强制踢人");
    	}else if(reason == 810) {
    		console.log("SDK调用接口关闭会议");
    	}else if(reason == 811){
    		console.log("服务更新维护时关闭会议");
    	}else if(reason == 815){
    		console.log("调用restful接口关闭会议");
    	}
    }

## 联系我们

如在接入实现过程中遇到问题，欢迎到微信技术对接群里联系沟通，我们会尽快处理。