本文主要是使用 custom 面板实现直播互动,上下麦操作。如有疑问可参考 微信小程序TRTC使用custom自定义面板(理解篇)。本文只是简单演示如何实现主播与观众进行连麦互动,在实际中,通常是有主播端发起与粉丝进行直播互动,通过粉丝来接受,进行连麦。发送这种消息可以使用IM(即时通讯)完成。
由于小程序在直播模式中没有特别指定主播和观众身份,所以在编写代码时需要自己进行区分,主播身份就进行视频流和音频流的推送;观众端则不进行推流,当需要连麦时,将观众的音频流推上去。
以下是实现代码:(代码中用到少量 iViewUI )
首先用一个页面进行数据的收集:
live.wxml中的简单代码
代码语言:html复制<!--pages/live/live.wxml-->
<view>
<van-field id="userID" label="userID" placeholder="请输入用户名" value="{{userID}}" bind:change="inputtext"></van-field>
<van-field id="roomID" label="roomID" placeholder="请输入房间号" value="{{roomID}}" bind:change="inputtext"></van-field>
<van-radio-group direction="horizontal" value="{{ radio }}" bind:change="onChange">
<van-radio name="1">主播</van-radio>
<van-radio name="2">观众</van-radio>
</van-radio-group>
<van-button type="primary" size='large' bind:click="toroom">确定</van-button>
</view>
live.js的代码
代码语言:javascript复制// pages/live/live.js
Page({
data: {
roomID: null,
userID: null,
role: 'Anchor', // Audience观众
radio: 1
},
// 点选框的选择
onChange(e) {
if (e.detail == 1) {
this.setData({
radio: e.detail,
role: 'Anchor',
})
} else if (e.detail == 2) {
this.setData({
radio: e.detail,
role: 'Audience',
})
}
},
// 输入框输入
inputtext(e) {
this.setData({
[e.currentTarget.id]: e.detail
})
},
// 进入liveroom页面,里面有<trtc-room>标签
toroom() {
wx.navigateTo({
url: './liveroom?roomID=' this.data.roomID '&userID=' this.data.userID '&role=' this.data.role,
})
}
})
然后就是引入<trtc-room>标签的页面
liveroom.wxml的代码
代码语言:html复制<!--pages/live/liveroom.wxml-->
<view class="win">
<trtc-room id="trtcroom" config="{{trtcConfig}}"></trtc-room>
<!-- 以下是观众的按键 -->
<view wx:if="{{role=='Anchor'?false:true}}" class="content" bindtap="lianmai"
style="color:{{lianmai?'#2db7f5':'#c5c8ce'}}">连麦</view>
<view wx:if="{{role=='Anchor'?false:true}}" class="exit" bindtap="leave">退房</view>
</view>
liveroom.wxss的代码
代码语言:css复制/* pages/live/liveroom.wxss */
.win {
position: relative;
height: 100vh;
width: 100vw;
}
.content,
.exit {
z-index: 100;
position: absolute;
width: 100px;
height: 100px;
border-radius: 50px;
line-height: 100px;
text-align: center;
}
.content {
bottom: 0;
right: 0;
color: #c5c8ce;
background: white;
}
.exit {
bottom: 0;
left: 0;
color: #f8f8f9;
background: red;
}
liveroom.js的代码,需要注意sdkAppID请填入自己的,关于UserSig可以参考 UserSig相关问题
代码语言:javascript复制// pages/live/liveroom.js
// 计算UserSig的函数
import {
genTestUserSig
} from '../../utils/GenerateTestUserSig'
Page({
data: {
trtcConfig: {
scene: 'live', // 通话场景'trc' 'live'
sdkAppID: '', // SDKAppID,请填入自己的
userID: '', // 用户ID
userSig: '', // 身份签名
template: 'custom', // 画面排版'1v1' 'grid' 'custom'
enableCamera: false, // 开启摄像头
enableMic: true, // 开启麦克风
enableAgc: false, // 开启音频自动增益
enableAns: false, // 开启音频噪声抑制
enableEarMonitor: false, // 开启耳返(ios)
enableAutoFocus: true, // 开启摄像头自动对焦
enableZoom: false, // 支持双手滑动调整摄像头焦距
minBitrate: '200', // 最小码率,不建议设置太低
maxBitrate: '1000', // 最大码率,需要跟分辨率相匹配,建议参考 分辨率码率参照表。
videoWidth: '360', // 视频宽,若设置视频宽高,会忽略 aspect
videoHeight: '640', // 视频高,若设置视频宽高,会忽略 aspec
beautyLevel: 0, // 美颜,取值范围 0-9,0表示关闭
whitenessLevel: 0, // 美白,取值范围 0-9,0表示关闭。
videoOrientation: 'vertical', // 设置本地画面方向,可选值:vertical 或 horizontal。
videoAspect: '9:16', // 宽高比,可选值:3:4 或 9:16。
frontCamera: 'front', // 设置前置还是后置摄像头,可选值:front 或 back。
enableRemoteMirror: false, // 设置观众端看到的画面的镜像效果
localMirror: 'auto', // 设置主播本地摄像头预览画面的镜像效果
enableBackgroundMute: false, // 设置主播端小程序切入后台时是否暂停声音的采集
audioQuality: 'high', // 高音质(48KHz)或低音质(16KHz),可选值:high 或 low。
audioVolumeType: 'voicecall', // 系统音量类型'media' 'voicecall'
audioReverbType: 0, // 音频混响类型,可选值为: 0:关闭,1:KTV,2:小房间,3:大会堂,4:低沉,5:洪亮,6:金属声,7:磁性。
enableIM: false,
debugMode: false,
},
roomID: '',
trtcRoomContext: null,
lianmai: false,
},
onLoad: function (options) {
console.log(options)
let trtcConfig = this.data.trtcConfig
trtcConfig['userID'] = options.userID
trtcConfig['enableCamera'] = options.role == 'Anchor' ? true : false
trtcConfig['enableMic'] = options.role == 'Anchor' ? true : false
trtcConfig['userSig'] = genTestUserSig(options.userID).userSig
this.setData({
trtcConfig,
roomID: options.roomID,
role: options.role
})
},
onShow: function () {
// 获取trtc-room实例
let trtcRoomContext = this.selectComponent('#trtcroom')
// 绑定事件监听
const TRTC_EVENT = trtcRoomContext.EVENT
trtcRoomContext.on(TRTC_EVENT.LOCAL_JOIN, (event) => {
// 进房成功,触发该事件后可以对本地视频和音频进行设置
if (this.data.role == 'Anchor') {
trtcComponent.publishLocalVideo()
trtcComponent.publishLocalAudio()
trtcComponent.setViewRect({
userID: event.userID,
xAxis: '0rpx',
yAxis: '0rpx',
width: '100vw',
height: '100vh',
})
}
})
// 远端用户推送视频
trtcRoomContext.on(TRTC_EVENT.REMOTE_VIDEO_ADD, (event) => {
// 订阅视频
// const userList = trtcRoomContext.getRemoteUserList()
const data = event.data
trtcRoomContext.subscribeRemoteVideo({
userID: data.userID,
streamType: data.streamType,
})
})
// 远端用户推送音频
trtcRoomContext.on(TRTC_EVENT.REMOTE_AUDIO_ADD, (event) => {
// 订阅音频
const data = event.data
trtcRoomContext.subscribeRemoteAudio({
userID: data.userID,
})
})
trtcRoomContext.enterRoom({
roomID: parseInt(this.data.roomID)
})
this.setData({
trtcRoomContext
})
},
// 连麦操作
lianmai() {
if (this.data.role == 'Audience') {
let trtcRoomContext = this.data.trtcRoomContext
if (this.data.lianmai) {
trtcRoomContext.unpublishLocalAudio()
this.setData({
lianmai: false
})
} else {
trtcRoomContext.publishLocalAudio()
this.setData({
lianmai: true
})
}
}
},
// 离开房间
leave() {
if (this.data.role == 'Audience') {
let trtcRoomContext = this.data.trtcRoomContext
trtcRoomContext.exitRoom()
wx.navigateBack({
delta: 1,
})
}
}
})
编辑custom面板,找到custom自定义面板
custom.wxml的代码:
代码语言:html复制<!-- template custom -->
<template name='custom'>
<view class="win">
<view wx:for="{{streamList}}" wx:key="streamID" class="player">
<live-player class="player" id="{{item.streamID}}" data-userid="{{item.userID}}" data-streamid="{{item.streamID}}"
data-streamtype="{{item.streamType}}" src="{{item.src}}" mode="{{item.mode}}" 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>
<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" />
<!-- 以下是主播的按钮,具体可以更具业务需要自己编写,其中引用的函数可以参考 https://cloud.tencent.com/developer/article/1801901 -->
<view class="buttonbox" wx:if="{{streamList.length < 2}}">
<Button type="error" shape="circle" bindtap="_hangUp">退出</Button>
<Button type="{{pusher.enableMic?'primary':'default'}}" shape="circle" bindtap="_toggleAudio">话筒</Button>
<Button type="{{pusher.enableCamera?'primary':'default'}}" shape="circle" bindtap="_toggleVideo">镜头</Button>
<Button type="{{pusher.beautyLevel==9?'primary':'default'}}" shape="circle" bindtap="_setPuserProperty" data-key="beautyLevel" data-value="9|0"
data-value-type="number">美颜</Button>
<Button type="primary" shape="circle" bindtap="switchCamera">切换</Button>
</view>
</view>
</template>
custom.wxss的代码:
代码语言:css复制/* 通过方法自定义模式 */
.win {
height: 100vh;
width: 100vw;
position: relative;
}
.buttonbox {
position: absolute;
width: 100vw;
height: 100px;
bottom: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
}