赞
踩
小程序提供的live-pusher组件和live-player组件主要用于两种场景:一个是用于视频直播,另一个是用于为您的小程序提供实时音视频相关的功能。live-pusher和live-player两个组件,都有一个叫做 RTC 的模式,通过这种模式,可以在小程序中实现实时视频通话功能。本文我们介绍如何使用live-pusher组件和live-player组件以及腾讯云实时音视频产品来开发一个视频通话的小程序。
腾讯实时音视频(Tencent Real-Time Communication,TRTC),将腾讯多年来在网络与音视频技术上的深度积累,主打低延时互动直播和多人音视频两大解决方案,通过腾讯云服务向开发者开放,致力于帮助开发者快速搭建低成本、低延时、高品质的音视频互动解决方案。
使用腾讯实时音视频SDK前需要首先注册腾讯云账号并完成 实名认证。注册成功后需要在实时音视频产品中创建一个实时音视频应用,腾讯云会为您的应用分配一个SDKAppID以及相应的UserSig的密钥, 后续的编码中会用到这两个参数。创建实时音视频应用的操作方法为:登录实时音视频控制台,选择【应用管理】->【创建应用】:
创建实时音视频应用成功后可以在应用的列表界面中看到SDKAppID:
点击上图中的【功能配置】,可以在打开页面的【快速上手】中看到UserSig的密钥:
另外在编码前还需要下载trtc-wx.js文件,该文件将用于接下来的小程序视频通话页面,请通过以下的url地址来下载trtc-wx.js文件:
https://web.sdk.qcloud.com/trtc/miniapp/download/trtc-wx.zip
下图是腾讯实时音视频SDK文档中给出的实时音视频小程序API的调用时序图:
在 trtc-wx 包中导出的是一个名为 TRTC 的类,您需要在页面onLoad函数中实例化这个类,同时创建 Pusher,并监听 TRTC 抛出的事件。
onLoad(options) {
//初始化 TRTC 实例
this.TRTC = new TRTC(this)
//创建 Pusher
const pusher = this.TRTC.createPusher({beautyLevel: 9})
//事件监听
this.bindTRTCRoomEvent()
},
使用TRTC类的on(EventCode, handler, context)方法监听TRTC抛出的事件,例如:
//事件监听
bindTRTCRoomEvent() {
const TRTC_EVENT = this.TRTC.EVENT
this.TRTC.on(TRTC_EVENT.ERROR, (event) => {
console.log('* room ERROR', event)
})
}
首先需要调用enterRoom方法进入TRTC房间,之后您可以调用start() 方法开始推流。
onLoad(options) {
this.TRTC = new TRTC(this)
const pusher = this.TRTC.createPusher({beautyLevel: 9})
this.bindTRTCRoomEvent()
//进入房间
this.TRTC.enterRoom(this.data._rtcConfig)
//开始推流
this.TRTC.getPusherInstance().start()
}
调用enterRoom(params)方法后,若房间不存在,系统将自动创建一个新房间,否则直接进入房间,enterRoom(params)的参数说明如下:
如果收到远端新增视频流,您可以开始播放这路视频,将这个 player 的 muteVideo 状态设置为 false,这里需要您传入这个 player 的 id,更新完成后会返回给您更新后的playerList列表,您只需要将页面的 playerList 同步更新即可。
this.TRTC.on(TRTC_EVENT.REMOTE_VIDEO_ADD, (event) => {
console.log('* room REMOTE_VIDEO_ADD', event)
const { player } = event.data
this.setPlayerAttributesHandler(player, { muteVideo: false })
})
如果收到这个远端流减少的通知时,您可以取消对这一路的订阅,将 muteAudio 设置为 true。
this.TRTC.on(TRTC_EVENT.REMOTE_VIDEO_REMOVE, (event) => {
console.log('* room REMOTE_VIDEO_REMOVE', event)
const { player } = event.data
this.setPlayerAttributesHandler(player, { muteVideo: true })
})
调用exitRoom()退出房间,退出房间时需要重置状态机的状态,并同步到页面中,防止下次进房发生状态的混乱。
_hangUp() {
const result = this.TRTC.exitRoom()
this.setData({
pusher: result.pusher,
playerList: result.playerList,
})
wx.navigateBack({delta: 1,})
}
如若需要变更 live-pusher 标签上 enable-mic 和 enable-camera 的属性,您可以通过调用 setPusherAttributes 函数对状态机中管理的推流状态进行改变,并将返回给您更新后的状态值更新到页面中。
//上行音频流
this.setData({
pusher: this.TRTC.setPusherAttributes({enableMic: true})
})
//上行视频流
this.setData({
pusher: this.TRTC.setPusherAttributes({enableCamera: true})
})
接下的代码中我们来实现一个视频对话功能的小程序,在小程序的入口页面(home页面)中我们为通话的双方用户指定一个房间号,并为当前用户产生一个随机用户ID,然后跳转到通话页面(room页面)进行视频通话。下面的代码是room页面的代码:
<view class="template-1v1"> <view wx:for="{{playerList}}" wx:key="streamID" wx:if="{{item.src && (item.hasVideo || item.hasAudio)}}" class="view-container player-container {{item.isVisible?'':'none'}}"> <live-player class="player" id="{{item.streamID}}" data-userid="{{item.userID}}" data-streamid="{{item.streamID}}" data-streamtype="{{item.streamType}}" src= "{{item.src}}" mode= "RTC" autoplay= "{{item.autoplay}}" mute-audio= "{{item.muteAudio}}" mute-video= "{{item.muteVideo}}" orientation= "{{item.orientation}}" object-fit= "{{item.objectFit}}" background-mute= "{{item.enableBackgroundMute}}" min-cache= "{{item.minCache}}" max-cache= "{{item.maxCache}}" sound-mode= "{{item.soundMode}}" enable-recv-message= "{{item.enableRecvMessage}}" auto-pause-if-navigate= "{{item.autoPauseIfNavigate}}" auto-pause-if-open-native="{{item.autoPauseIfOpenNative}}" debug="{{debug}}" bindstatechange="_playerStateChange" bindfullscreenchange="_playerFullscreenChange" bindnetstatus="_playerNetStatus" bindaudiovolumenotify ="_playerAudioVolumeNotify"/> </view> <view class="view-container pusher-container {{pusher.isVisible?'':'none'}} {{playerList.length===0? 'fullscreen':''}}"> <live-pusher class="pusher" url="{{pusher.url}}" mode="{{pusher.mode}}" autopush="{{pusher.autopush}}" enable-camera="{{pusher.enableCamera}}" enable-mic="{{pusher.enableMic}}" muted="{{!pusher.enableMic}}" enable-agc="{{pusher.enableAgc}}" enable-ans="{{pusher.enableAns}}" enable-ear-monitor="{{pusher.enableEarMonitor}}" auto-focus="{{pusher.enableAutoFocus}}" zoom="{{pusher.enableZoom}}" min-bitrate="{{pusher.minBitrate}}" max-bitrate="{{pusher.maxBitrate}}" video-width="{{pusher.videoWidth}}" video-height="{{pusher.videoHeight}}" beauty="{{pusher.beautyLevel}}" whiteness="{{pusher.whitenessLevel}}" orientation="{{pusher.videoOrientation}}" aspect="{{pusher.videoAspect}}" device-position="{{pusher.frontCamera}}" remote-mirror="{{pusher.enableRemoteMirror}}" local-mirror="{{pusher.localMirror}}" background-mute="{{pusher.enableBackgroundMute}}" audio-quality="{{pusher.audioQuality}}" audio-volume-type="{{pusher.audioVolumeType}}" audio-reverb-type="{{pusher.audioReverbType}}" waiting-image="{{pusher.waitingImage}}" debug="{{debug}}" bindstatechange="_pusherStateChangeHandler" bindnetstatus="_pusherNetStatusHandler" binderror="_pusherErrorHandler" bindbgmstart="_pusherBGMStartHandler" bindbgmprogress="_pusherBGMProgressHandler" bindbgmcomplete="_pusherBGMCompleteHandler" bindaudiovolumenotify="_pusherAudioVolumeNotify"/> <view class="loading" wx:if="{{playerList.length === 0}}"> <view class="loading-img"> <image src="../../../static/images/loading.png" class="rotate-img"></image> </view> <view class="loading-text">等待接听中...</view> </view> </view> <view class="handle-btns"> <view class="btn-normal" bindtap="_pusherAudioHandler"> <image class="btn-image" src="{{pusher.enableMic? '../../../static/images/audio-true.png': '../../../static/images/audio-false.png'}} "></image> </view> <view class="btn-normal" bindtap="_pusherSwitchCamera" > <image class="btn-image" src="../../../static/images/switch.png"></image> </view> <view class="btn-normal" bindtap="_setPlayerSoundMode"> <image class="btn-image" src="{{playerList[0].soundMode === 'ear' ? '../../../static/images/speaker-false.png': '../../../static/images/speaker-true.png'}} "></image> </view> </view> <view class="bottom-btns"> <view class="btn-normal" data-key="beautyLevel" data-value="9|0" data-value-type="number" bindtap="_setPusherBeautyHandle"> <image class="btn-image" src="{{pusher.beautyLevel == 9 ? '../../../static/images/beauty-true.png': '../../../static/images/beauty-false.png'}} "></image> </view> <view class="btn-hangup" bindtap="_hangUp"> <image class="btn-image" src="../../../static/images/hangup.png"></image> </view> </view> </view>
import TRTC from '../../../static/trtc-wx' Page({ data: { _rtcConfig: { sdkAppID: '', //开通实时音视频服务创建应用后分配的sdkAppID roomID: '', //房间号可以由您的系统指定 userID: '', //用户ID可以由您的系统指定 userSig: '', //身份签名,相当于登录密码的作用 }, roomID: 0, pusher: null, playerList: [], }, onLoad(options) { this.TRTC = new TRTC(this) const pusher = this.TRTC.createPusher({beautyLevel: 9}) this.setData({ _rtcConfig: { userID: options.userID, sdkAppID: options.sdkAppID, userSig: options.userSig, roomID: options.roomID, }, pusher: pusher.pusherAttributes }) //事件监听 this.bindTRTCRoomEvent() //进入房间 this.setData({ pusher: this.TRTC.enterRoom(this.data._rtcConfig), }, () => { this.TRTC.getPusherInstance().start() }) }, //设置pusher属性 setPusherAttributesHandler(options) { this.setData({ pusher: this.TRTC.setPusherAttributes(options), }) }, //设置某个player属性 setPlayerAttributesHandler(player, options) { this.setData({ playerList: this.TRTC.setPlayerAttributes(player.streamID, options), }) }, //事件监听 bindTRTCRoomEvent() { const TRTC_EVENT = this.TRTC.EVENT this.TRTC.on(TRTC_EVENT.ERROR, (event) => { console.log('* room ERROR', event) }) //成功进入房间 this.TRTC.on(TRTC_EVENT.LOCAL_JOIN, (event) => { console.log('* room LOCAL_JOIN', event) this.setPusherAttributesHandler({ enableCamera: true }) this.setPusherAttributesHandler({ enableMic: true }) }) //成功离开房间 this.TRTC.on(TRTC_EVENT.LOCAL_LEAVE, (event) => { console.log('* room LOCAL_LEAVE', event) }) //远端用户退出 this.TRTC.on(TRTC_EVENT.REMOTE_USER_LEAVE, (event) => { const { playerList } = event.data this.setData({ playerList: playerList }) console.log('* room REMOTE_USER_LEAVE', event) }) //远端用户推送视频 this.TRTC.on(TRTC_EVENT.REMOTE_VIDEO_ADD, (event) => { console.log('* room REMOTE_VIDEO_ADD', event) const { player } = event.data // 开始播放远端的视频流,默认是不播放的 this.setPlayerAttributesHandler(player, { muteVideo: false }) }) // 远端用户取消推送视频 this.TRTC.on(TRTC_EVENT.REMOTE_VIDEO_REMOVE, (event) => { console.log('* room REMOTE_VIDEO_REMOVE', event) const { player } = event.data this.setPlayerAttributesHandler(player, { muteVideo: true }) }) // 远端用户推送音频 this.TRTC.on(TRTC_EVENT.REMOTE_AUDIO_ADD, (event) => { console.log('* room REMOTE_AUDIO_ADD', event) const { player } = event.data this.setPlayerAttributesHandler(player, { muteAudio: false }) }) // 远端用户取消推送音频 this.TRTC.on(TRTC_EVENT.REMOTE_AUDIO_REMOVE, (event) => { console.log('* room REMOTE_AUDIO_REMOVE', event) const { player } = event.data this.setPlayerAttributesHandler(player, { muteAudio: true }) }) }, //挂断退出房间 _hangUp() { const result = this.TRTC.exitRoom() this.setData({ pusher: result.pusher, playerList: result.playerList, }) wx.navigateBack({delta: 1,}) }, //设置美颜 _setPusherBeautyHandle() { const beautyLevel = this.data.pusher.beautyLevel === 0 ? 9 : 0 this.setPusherAttributesHandler({ beautyLevel }) }, //发布/取消发布Audio _pusherAudioHandler() { if (this.data.pusher.enableMic) { this.setPusherAttributesHandler({ enableMic: false }) } else { this.setPusherAttributesHandler({ enableMic: true }) } }, _pusherSwitchCamera() { const frontCamera = this.data.pusher.frontCamera === 'front' ? 'back' : 'front' this.TRTC.getPusherInstance().switchCamera(frontCamera) }, _setPlayerSoundMode() { if (this.data.playerList.length === 0) { return } const player = this.TRTC.getPlayerList() const soundMode = player[0].soundMode === 'speaker' ? 'ear' : 'speaker' this.setPlayerAttributesHandler(player[0], { soundMode }) }, _pusherStateChangeHandler(event) { this.TRTC.pusherEventHandler(event) }, _pusherNetStatusHandler(event) { this.TRTC.pusherNetStatusHandler(event) }, _pusherErrorHandler(event) { this.TRTC.pusherErrorHandler(event) }, _pusherBGMStartHandler(event) { this.TRTC.pusherBGMStartHandler(event) }, _pusherBGMProgressHandler(event) { this.TRTC.pusherBGMProgressHandler(event) }, _pusherBGMCompleteHandler(event) { this.TRTC.pusherBGMCompleteHandler(event) }, _pusherAudioVolumeNotify(event) { this.TRTC.pusherAudioVolumeNotify(event) }, _playerStateChange(event) { this.TRTC.playerEventHandler(event) }, _playerFullscreenChange(event) { this.TRTC.playerFullscreenChange(event) }, _playerNetStatus(event) { this.TRTC.playerNetStatus(event) }, _playerAudioVolumeNotify(event) { this.TRTC.playerAudioVolumeNotify(event) }, })
从小程序的入口页面进入room页面需要传递以下参数:
func GenUserSig(sdkappid int, key string, userid string, expire int) (string, error) { currTime := time.Now().Unix() var sigDoc map[string]interface{} sigDoc = make(map[string]interface{}) sigDoc["TLS.ver"] = "2.0" sigDoc["TLS.identifier"] = identifier sigDoc["TLS.sdkappid"] = sdkappid sigDoc["TLS.expire"] = expire sigDoc["TLS.time"] = currTime sigDoc["TLS.sig"] = _hmacsha256(sdkappid, key, userid, currTime, expire) data, _:= json.Marshal(sigDoc) var b bytes.Buffer w := zlib.NewWriter(&b) w.Write(data) w.Close() return base64urlEncode(b.Bytes()), nil } func base64urlEncode(data []byte) string { str := base64.StdEncoding.EncodeToString(data) str = strings.Replace(str, "+", "*", -1) str = strings.Replace(str, "/", "-", -1) str = strings.Replace(str, "=", "_", -1) return str } func _hmacsha256(sdkappid int, key string, identifier string, currTime int64, expire int) string { content := "TLS.identifier:" + identifier + "\n" content += "TLS.sdkappid:" + strconv.Itoa(sdkappid) + "\n" content += "TLS.time:" + strconv.FormatInt(currTime, 10) + "\n" content += "TLS.expire:" + strconv.Itoa(expire) + "\n" h := hmac.New(sha256.New, []byte(key)) h.Write([]byte(content )) return base64.StdEncoding.EncodeToString(h.Sum(nil)) }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。