在使用腾讯的 trtc 产品时,要是想在微信小程序实现直播的业务能力,首先需要指定 ‘主播’ 和 ‘观众’ 角色,小程序的 <trtc-room> 中的标签属性和实例对象的方法都没有做直播状态时的角色区分;再就是想在主播端和观众端实现一些弹幕,点赞,连麦互动时需要使用到 custom 自定义面板,需要对页面进行设计,我也有对 custom 面板的简单使用写了两篇文章可供参考:微信小程序TRTC使用custom自定义面板(理解篇)、微信小程序TRTC使用custom自定义面板(实现篇)。为了方便开发者,腾讯云还有 移动直播SDK,其中集成了弹幕、点赞、美颜、连麦等一些功能的,微信小程序端的调用方式也是以组件形势进行调用。
腾讯 移动直播SDK 基于 RTMP SDK 的多平台直播开发工具,配合腾讯云直播、云点播、IM等云服务为客户提供了快速接入一体化 的直播方案,支持包括iOS、安卓、小程序等多种接入方式。配合腾讯云直播服务使用 RTMP over QUIC 快速推流至腾讯云,让直播更低卡顿、更低延迟。通过建立加速通道为双向传输的连麦降低音视频传输的延迟,保证直播连麦服务更加流畅。借助AI识别技术,对人脸进行识别定位。支持动销贴纸、手势互动、智能抠背、美颜美妆、高级滤镜等功能,满足客户的多样直播场景。
目前SDK的集成只支持iOS、安卓、微信小程序;摄像头推流的功能也是只有iOS、安卓、微信小程序;录屏推流功能只有iOS和安卓;拉流播放的功能是支持iOS、安卓、微信小程序和Web端;对于连麦互动的方案,微信小程序采用RTMP方案,安卓和iOS可以使用RTMP方案之外还可以采用RTC方案。
微信小程序集成的 移动直播SDK 版本是基础版本,版本功能可以到官方文档 SDK下载 进行查看。微信小程序的集成方法也可以上官网查看完成的流程。其中就是需要添加一个自己的域名来作为播放域名。
微信小程序使用 移动直播SDK 和使用trtc一样,直接引入组件,然后在需要直播的页面中写入 <mlvb-live-room> 组件,以下是代码说明:(参考官方Demo)
代码语言:javascript复制const app = getApp()
Page({
component: null,
data: {
userName: '',
roomID: '',
roomName: '',
pureAudio: false,
role: 'audience',
showLiveRoom: false,
rooms: [],
comment: [],
linked: false,
debug: false,
frontCamera: true,
inputMsg: [],
muted: false,
toview: '',
beauty: 5,
shouldExit: false,
phoneNum: '',
headerHeight: app.globalData.headerHeight,
statusBarHeight: app.globalData.statusBarHeight,
},
// 进入页面将上个页面收集的一些配置信息进行保存
onLoad: function (options) {
var self = this;
console.log("--> onLoad: ", options)
var role = options.type == 'create' ? 'anchor' : 'audience';
if (role == 'audience') {
self.setData({
roomID: options.roomID,
roomName: options.roomName,
userName: options.userName,
role: role,
showLiveRoom: true
}, function () {
self.start();
})
} else {
self.setData({
roomName: options.roomName,
userName: options.userName,
pureAudio: JSON.parse(options.pureAudio),
role: role,
showLiveRoom: true
}, function () {
console.log('======> page data: ', self.data)
self.start();
})
}
},
// 设置监听 mlvb-live-room 组件返回的事件
onRoomEvent: function (e) {
var self = this;
var args = e.detail;
console.log('onRoomEvent', args)
switch (args.tag) {
case 'roomClosed': {
wx.showModal({
content: `房间已解散`,
showCancel: false, // 标识房间已经解散
complete: () => {
wx.navigateBack({ delta: 1 })
}
});
break;
}
case 'error': {
wx.showToast({
title: `${args.detail}[${args.code}]`,
icon: 'none',
duration: 1500
})
if (args.code == 5000) {
this.data.shouldExit = true;
} else {
console.error("收到error:", args)
if (args.code != -9004) {
setTimeout(() => {
wx.navigateBack({ delta: 1 })
}, 1500)
} else {
self.setData({
linked: false, // 出错断开连麦标识
phoneNum: ''
})
}
}
break;
}
case 'linkOn': { // 连麦连上
self.setData({
linked: true, // 标识连麦连上
phoneNum: ''
})
break;
}
case 'linkOut': { //连麦断开
self.setData({
linked: false, // 标识断开连麦
phoneNum: ''
})
break;
}
case 'recvTextMsg': {
console.log('收到消息:', e.detail.detail);
var msg = e.detail.detail;
self.data.comment.push({
content: msg.message,
name: msg.userName,
time: msg.time
});
self.setData({
comment: self.data.comment,
toview: ''
});
// 滚动条置底
self.setData({
toview: 'scroll-bottom'
});
break;
}
case 'requestJoinAnchor': { //收到连麦请求
var jioner = args.detail;
var showBeginTime = Math.round(Date.now());
wx.showModal({
content: `${jioner.userName} 请求连麦`,
cancelText: '拒绝',
confirmText: '接受',
success: function (sm) {
var timeLapse = Math.round(Date.now()) - showBeginTime;
if (timeLapse < 10000) {
if (sm.confirm) {
console.log('用户点击同意')
self.component && self.component.respondJoinAnchor(true, jioner);
} else if (sm.cancel) {
console.log('用户点击取消')
self.component && self.component.respondJoinAnchor(false, jioner);
}
} else {
wx.showToast({
title: '连麦超时',
})
}
}
})
break;
}
default: {
console.log('onRoomEvent default: ', e)
break;
}
}
},
// 开始直播或者播放直播
start: function () {
var self = this;
// 选择<mlvb-live-demo>实例
self.component = self.selectComponent("#id_liveroom")
console.log('self.component: ', self.component)
console.log('self:', self);
// 启动
self.component.start();
},
// 设置页面标签为房间名
onReady: function () {
var self = this;
// 设置页面标签为房间名
wx.setNavigationBarTitle({
title: self.data.roomName
})
},
// 检查房间是否还存在,存在恢复直播或播放
onShow: function () {
if (this.data.shouldExit) {
wx.showModal({
title: '提示',
content: "收到退房通知",
showCancel: false,
complete: function () {
wx.navigateBack({ delta: 1 });
}
});
}
var self = this;
self.component && self.component.resume();
},
// 观众点击连麦请求
onLinkClick: function () {
var self = this;
self.component && self.component.requestJoinAnchor();
self.setData({
phoneNum: 'phone-1'
})
self.setInternal();
},
// 接听动画
setInternal: function () {
var self = this;
setTimeout(() => {
if (!self.data.phoneNum) {
return;
}
var curPhoneNum = '';
switch (self.data.phoneNum) {
case 'phone-1':
curPhoneNum = 'phone-2';
break;
case 'phone-2':
curPhoneNum = 'phone-3';
break;
case 'phone-3':
curPhoneNum = 'phone-1';
break;
default:
break;
}
self.setData({
phoneNum: curPhoneNum
})
self.setInternal();
}, 500);
},
// 退出页面后停止直播或播放
onHide: function () {
var self = this;
self.component && self.component.pause();
},
// 调试模式开关
showLog: function () {
var self = this;
self.setData({
debug: !self.data.debug
})
},
// 麦克风开关
changeMute: function () {
var self = this;
self.setData({
muted: !self.data.muted
})
},
// 美颜开关
setBeauty: function () {
var self = this;
self.setData({
beauty: self.data.beauty == 5 ? 0 : 5
})
},
// 切换摄像头
changeCamera: function () {
var self = this;
self.component && self.component.switchCamera();
self.setData({
frontCamera: !self.data.frontCamera
})
},
// 返回键
onBack: function () {
wx.navigateBack({
delta: 1
});
},
})
wxml文件:
代码语言:html复制<view class='container-box'>
<!-- 直接调用 移动直播组件 -->
<mlvb-live-room id="id_liveroom" wx:if="{{showLiveRoom}}" roomid="{{roomID}}" role="{{role}}" roomname="{{roomName}}" pureaudio="{{pureAudio}}" debug="{{debug}}" muted="{{muted}}" beauty="{{beauty}}" template="float" bindRoomEvent="onRoomEvent">
<!-- 主播返回按钮 -->
<cover-view slot="casterBackButton" style='position:absolute;left:0;height:10%;width:10%;top:{{(headerHeight statusBarHeight) - 26}}rpx'>
<cover-image class='close' src="/pages/Resources/back.png" bindtap="onBack"></cover-image>
</cover-view>
<!-- 观众返回按钮 -->
<cover-view slot="audienceBackButton" style='position:absolute;left:0;height:10%;width:10%;top:{{(headerHeight statusBarHeight) - 26}}rpx'>
<cover-image class='close' src="/pages/Resources/back.png" bindtap="onBack"></cover-image>
</cover-view>
<!-- 主播推流界面上叠加的操作按钮 -->
<cover-view slot="caster" style='position:absolute;bottom:0;height:10%;width:100%'>
<cover-view class="operate">
<!-- 切换(前后置)摄像头 -->
<cover-view class='img-box'>
<cover-image class='img-view' src='/pages/Resources/camera{{frontCamera?"":"-gray"}}.png' bindtap="changeCamera"></cover-image>
</cover-view>
<!-- 美颜按钮 -->
<cover-view class='img-box'>
<cover-image class='img-view' src='/pages/Resources/{{beauty > 0? "beauty" : "beauty-dis"}}.png' bindtap="setBeauty"></cover-image>
</cover-view>
<!-- 调试模式开关 -->
<cover-view class='img-box'>
<cover-image class='img-view' src='/pages/Resources/{{debug? "log" : "log2"}}.png' bindtap="showLog"></cover-image>
</cover-view>
</cover-view>
</cover-view>
<!-- 观众播放界面上叠加的操作按钮 -->
<cover-view slot="audience" style='position:absolute;bottom:0;height:10%;width:100%'>
<cover-view class='{{!linked? "operate-nolink" : "operate"}}'>
<!-- 观众端连上麦后 切换摄像头按钮 -->
<cover-view wx:if="{{linked}}" class='img-box'>
<cover-image class='img-view' src='/pages/Resources/camera.png' bindtap="changeCamera"></cover-image>
</cover-view>
<!-- 观众端连上麦后 美颜开关 -->
<cover-view wx:if="{{linked}}" class='img-box'>
<cover-image class='img-view' src='/pages/Resources/{{beauty > 0? "beauty" : "beauty-dis"}}.png' bindtap="setBeauty"></cover-image>
</cover-view>
<!-- 没连上麦时 发起连麦请求的按钮 -->
<cover-view wx:if="{{!linked}}" class='img-box-big'>
<cover-image class='img-view-big' src='/pages/Resources/video-call.png' bindtap="onLinkClick"></cover-image>
</cover-view>
<!-- 调试模式开关 -->
<cover-view class='{{!linked? "img-box-small" : "img-box"}}'>
<cover-image class='{{!linked? "img-view-small" : "img-view"}}' src='/pages/Resources/{{debug? "log" : "log2"}}.png' bindtap="showLog"></cover-image>
</cover-view>
</cover-view>
<!-- 请求连麦动画 -->
<cover-image wx:if="{{phoneNum}}" class='center' src="/pages/Resources/{{phoneNum}}.png"></cover-image>
</cover-view>
</mlvb-live-room>
</view>