Tutorial: 开始集成音视频通话

开始集成音视频通话

前提条件

  1. 本地化部署公司的媒体服务器,获取到服务器地址、AccessKey和SecretKey。
  2. 为了避免防火墙安全策略限制正常的 RTC 数据传输,需要对防火墙策略进行设置。
  3. 为了保证通话体验,建议在正式开始音视频通话前,进行设备检测和浏览器兼容性检测

集成 SDK

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

Require集成

  1. 在您的 app.js配置中添加如下代码即可:
var avdSDK = require('./lib/rtc-wx-3.2.6.4.min.js');
var pluginStore = new avdSDK.PluginStore();
self.data.getAvdSDK = pluginStore.getAvdSDK();

资源下载

SDK 使用逻辑概览

基本概念

您在使用 RTC WX SDK 时,会接触到以下概念:

  • [AVDEngine] 类,其实例代表一个本地客户端。AVDEngine的对象方法提供了获取摄像头列表、麦克风列表、实例化房间类、控制日志等级及日志保存等功能等功能。

实现音视频通话基本逻辑

  1. 调用
   var avdSDK = require('./lib/rtc-wx-3.2.6.4.min.js');

  var pluginStore = new avdSDK.PluginStore();
  
  self.data.getAvdSDK= pluginStore.getAvdSDK()方法来实例化创建getAvdSDK对象。
  1. 调用 //获取应用实例 const app = getApp();
      var room = app.data.getAvdSDK.obtainRoom(roomId)方法来实例化房间类。
  1. 调用 room.join()进入房间。

  2. 在进入房间后,可以开启摄像头和麦克风并发布到房间。

   - 调用 app.data.getAvdSDK.room.selfUser.videos[0].publish()开启摄像头并发布到房间。
   - 调用 app.data.getAvdSDK.room.selfUser.audio.publish()开启麦克风并发布到房间。
  1. 当一个远端用户发布了音视频后,本端会收到远端视频发布通知,本端需要做订阅操作,音频SDK会默认自动订阅。您需要通过如下方式来播放远端音视频:
    • 在进房间后监听user.addCallback(app.data.getAvdSDK.Enum.UserCallback.publish_camera_notify, self.onPublishCameraNotify)事件,就能收到该远端用户的发布视频。
    • 在进房间后监听 user.addCallback(UserCallback.subscrible_microphone_result, onSubscribleMicrophoneResult)事件,就能收到该远端用户的发布音频。
    • 在onSubscribleCameraResult事件回调函数中,调用user.addCallback(app.data.getAvdSDK.Enum.UserCallback.subscrible_camera_result, self.onSubscribleCameraResult);方法播放远端视频。
    • 在onSubscribleMicrophoneResult事件回调函数中,调用user.addCallback(app.data.getAvdSDK.Enum.UserCallback.subscrible_microphone_result, self.onSubscribleMicrophoneResult)方法播放远端音频。

创建 AVDEngine 对象

 var avdSDK = require('./lib/rtc-wx-3.2.6.4.min.js');

var pluginStore = new avdSDK.PluginStore();

self.data.getAvdSDK= pluginStore.getAvdSDK()

var api = require('../../restApi/api.js');
getAccessToken: function () {
    var self = this;
    var currTimeStamp = parseInt(new Date().getTime() / 1000)
    var promise = new Promise((resolve, reject) => {
		api.getCertificate(currTimeStamp).then(res => { 
			if(res.data) {
                //deParams()解密
				var certResData = JSON.parse(api.deParams(res.data)); 
                // 比对请求时携带的时间戳和返回的时间戳,用于确认返回的token是此次请求的
				if(certResData['timestamp'] != currTimeStamp){
					reject('processingError is currTimeStamp');
					return;
				}
                // 通过凭证获取token,certResData()加密
       		api.getAccessToken(certResData['access_key'],certResData['secret_key']).then((data)=> {
					resolve(data.token);
               }).catch((error) => {
                   reject(error);
               });
            }
            }).catch(error => {
                reject(error);
            })
    });
    return promise;
  } 
// 引擎初始化
app.data.getAvdSDK.avdEngine.setDefaultMediaPublishKeepAliveTime(16000);   
// serverUrl: MCU服务器地址
// token: 访问令牌
app.data.getAvdSDK.avdEngine.init(config.serverUrl,token).then(self.initSuccess).catch(self.initError)

创建 Room对象

//获取应用实例 const app = getApp();

 var room = app.data.getAvdSDK.obtainRoom(roomId)

进入房间

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

  • userId: 用户 ID,由您指定。
  • userName: 用户名,由您指定。
  • userData: 用户扩展字段。 可以做为应用层逻辑上的可自定义字段使用,如填入角色。
room.join(userId, userName, '', password).then(function(){
    console.log('进房成功');
    this.joinSuccess()
}).otherwise(function(error){
    console.error('进房失败 ', error);
    this.joinError()
});

开启摄像头、麦克风

1.开启摄像头

使用app.data.getAvdSDK.room.selfUser.videos[0].publish()方法开启摄像头,并发布到房间。

//获取应用实例
const app = getApp();
//SDK支持多摄像头的同时开启及发布,这里voides[0]表示取了摄像头设备集里的第一摄像头
var video = app.data.getAvdSDK.room.selfUser.videos[0] 
video.publish().then(function(){ 
    var playerUrl = app.data.getAvdSDK.room.selfUser.pusherUrl 
    console.log(new Date(), "发布视频成功, 推流地址:", playerUrl);
}).otherwise(function(error){
    console.log(new Date(), "发布视频出错,error:", error);
});
// 监听推流状态
onPushEvent: function(e) {
    var self = this;
    // return;
    self.createPusherContext();

    var code;
    if (e.detail) {
      code = e.detail.code;
    } else {
      code = e;
    }

    var errmessage = '';
    switch (code) {
      case 1002:
        console.log('推流情况:', code);
        break;

      case -1301:
        console.error('打开摄像头失败: ', code);
        break;

      case -1302:
        console.error('打开麦克风失败: ', code);
        break;

      case -1307:
        console.error('推流连接断开: ', code);

        self.data.pusher.pusherContext.stop();
        console.log('推流连接断开后主动停止推流');
        setTimeout(() => {
          self.data.pusher.pusherContext.start();
          console.log('推流连接断开后主动重新开始推流');
        }, 500);
        wx.showLoading({
          title: '推流连接断开',
        })
        break;

      default:
        console.log('推流情况:', code);

    }

  },
//在wxml中,使用 <live-pusher> 组件播放

<live-pusher  url="{{pusher.url}}"  bindstatechange="onPushEvent"  />
       

2.开启麦克风

使用app.data.getAvdSDK.room.selfUser.audio.publish()方法开启麦克风,并发布到房间。

var audio = app.data.getAvdSDK.room.selfUser.audio;
audio.publish().then(function(){
    console.log('麦克风开启及发布成功');
    self.data.pusher.url = app.data.getAvdSDK.room.selfUser.pusherUrl
}).otherwise(function(error){
     console.log(new Date(), "发布音频出错,error:", error);
});
//在wxml中,使用 <live-pusher> 组件播放
<live-pusher  url="{{pusher.url}}" mode="RTC" autopush bindstatechange="statechange"  /> 

播放远端音视频

a.播放远端音频

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

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

user.addCallback(app.data.getAvdSDK.Enum.UserCallback.subscrible_microphone_result,self.onSubscribleMicrophoneResult);

/**
 * 订阅远端音频流反馈
 * @param {Object} stream- 远端音频流
 * @param {Object} userId- 所属用户ID
 * @param {Object} userName-所属用户名称
 */
onSubscribleMicrophoneResult: function(userId, userName, microphoneId) {
    var user = app.data.getAvdSDK.room.getUser(userId);
    if(user.userAgent == 'avd_mp4_importer'){
        console.log('+++onSubscribleMicrophoneResult avd_mp4_importer user: ', 	                 		JSON.parse(JSON.stringify(user))) 
    }else{
     	self.data.player.playUrlAssist = user.audio.playerUrl;
    	console.log(new Date(), "+++onSubscribleMicrophoneResult(),user.audio.playerUrl:", 				user.audio.playerUrl);
      self.data.player.audioPlaying = true; 
    }
}

//在wxml中,使用 <live-player> 组件播放 
<live-player src="{{player.playUrlAssist}}" mode="RTC" autoplay bindstatechange="statechange" />

b.播放远端视频

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

user.addCallback(app.data.getAvdSDK.Enum.UserCallback.publish_camera_notify, self.onPublishCameraNotify);
user.addCallback(app.data.getAvdSDK.Enum.UserCallback.subscrible_camera_result, self.onSubscribleCameraResult);

/**
 *远端用户发布视频通知,在回调事件里可以做订阅操作
 *@param {Object[]} videos - 远端已发布的视频对象集
*/
function onPublishCameraNotify(videos) {
      //订阅未订阅的视频
    console.log(new Date(), "+++onPublishCameraNotify(),videos:", 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 - 所属用户ID
 * @param {Object} userName- 所属用户名称
 * @param {Object} cameraId- 摄像头设备ID
 */
function onSubscribleCameraResult(userId, userName, cameraId) {
    var videoPuber = app.data.getAvdSDK.room.getUser(userId);
	var video = videoPuber.getVideo(cameraId)        
    if(videoPuber && videoPuber.userAgent == 'avd_mp4_importer'){
      console.log(new Date(), '+++onSubscribleCameraResult, tts player, video.playUrl: ', video.playerUrl) 
      self.data.ttsPlayer.playUrlMain = video.playerUrl; 
      self.setData({
        ttsPlayer: self.data.ttsPlayer
      })
    } 
}
// 监听拉流状态                      
 onTTSPlayEvent: function(error) {
    var self = this;
     // code == 2006	拉流:视频播放结束
     // code == 2105	拉流:当前视频播放出现卡顿
     if (error.detail.code < 0) {
      	console.error('+++onPlayEvent(),拉流失败:', error.detail.code + "," + error.detail.message);
      	if(error.detail.code == "-2301"){ //网络断连,且经多次重连抢救无效,更多重试请自行重启播放
            var playerContext = wx.createLivePlayerContext("ttsPlayer");
            playerContext.play();
            console.log('+++onPlayEvent(),拉流失败,已经重启播放');
      	}
    } else {
      console.log('TTS拉流情况:', error.detail.code + "," + error.detail.message);
    }
  },

//在wxml中,使用 <live-player> 组件播放
<live-player src="{{ttsPlayer.playUrlAssist}}" mode="RTC" autoplay bindstatechange="onTTSPlayEvent" />

3.退出房间

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

var reason = 1; //退会原因
if (app.data.getAvdSDK.room) {
      app.data.getAvdSDK.room.leave(reason).then(function() { 
       console.log('退会成功!')
      });
}

4.处理被踢

除了用户主动退出房间之外,用户也有可能因为如下原因被踢出房间,此时 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调用接口关闭会议。
  3. reason == 811:服务更新维护时关闭会议。
  4. reason == 815:调用restful接口关闭会议。
 app.data.getAvdSDK.room.addCallback(app.data.getAvdSDK.Enum.RoomCallback.leave_indication, this.onLeaveIndication);

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

    if (reason == 804) { //同一个userid加会, 把前一个人踢下线
      wx.showModal({
        title: '退会提示',
        content: '您已在另一终端登陆,被踢出会议室。被踢原因:' + reason,
        showCancel: false
      });

    } else if (reason == 808) { //调用Restful接口把人踢下线或SDK调踢人接口通知会议中的第三方用户
      wx.showModal({
        title: '退会提示',
        content: '您被' + userName + '踢出会议室。被踢原因:' + reason,
        showCancel: false
      });

    } else if (reason == 809) { //无授权 15分钟以后强制踢人
      wx.showModal({
        title: '退会提示',
        content: '无授权,试用时限到期,您被' + userName + '踢出会议室。被踢原因:' + reason,
        showCancel: false
      });

    } else {
      wx.showModal({
        title: '退会提示',
        content: '您已被主持人从会议中踢出',
        showCancel: false
      });
    }
}


app.data.getAvdSDK.room.addCallback(app.data.getAvdSDK.Enum.RoomCallback.close_room_notify, this.onCloseRoomNotify);

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

    wx.showModal({
      title: '关闭会议提示',
      content: '会议已关闭。原因:' + tips,
      showCancel: false
    });
}

联系我们

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