第三方平台选型:腾讯、阿里、即构、声网
由于即构直播平台支持uni-app故本文章选用的是即构sdk版讲解
一、效果
二、sdk集成
1、 首先注册即构平台账号,然后对照即构官网一步步集成
主要分为sdk插件集成与JS 封装层集成这两部分,具体步骤请移步官网查看。
uni-app实时音视频示例源码下载 - 开发者中心 - ZEGO即构科技
2、调试,即构官网提供了pc网页直播推流地址,如下
推拉流基础功能
需要到控制台获取自己创建的AppID、RoomID、UserID、Token、StreamID,填入其中,app直播端也需要填写一样的appid.有关app部分下一步再讲。需要注意的是,直播端与观众端的AppID、RoomID、StreamID三个需要一致才可看到对方的直播。
3、app端代码
需要修改 appSign、AppID,修改成自己的。
<template>
<view class="colum_layout">
<!-- 状态栏 -->
<view class="title" style="width: 100%;height: 70rpx;background-color: black;"></view>
<!-- <zego-local-view style="height: 300rpx;width: 400rpx;position: absolute;display: flex;z-index:999"></zego-local-view> -->
<!-- 拉流布局 -->
<view class="live_parent" @click="hide_edit">
<zego-remote-view class="live_play" :streamID="playStreamID" :viewMode="AspectFit=1">
</zego-remote-view>
</view>
<!-- 在线人数 -->
<view class="onlines">
<text class="user_name">昵称:医生</text>
<!-- @click="loginOut" -->
<view class="onlineMd">
<text class="onlineTag">在线</text>
<text class="online_num">{{online}}</text>
<image @click="closed" style="width: 40rpx;height: 40rpx; margin-right: 40rpx;" src="/static/cha.png">
</image>
</view>
</view>
<!-- 消息列表 -->
<view :class="isShowKeyboad?'msg_list_height':'msg_list_low'">
<scroll-view scroll-y="true" class="list-content" show-scrollbar="false" :enhanced="true" :bounces="false"
:scroll-with-animation="true" :scroll-top="commentScrollTop">
<view class="list-item" id="commentContent" v-for="(item,index) in msg" :key="index">
<!-- item.fromUser.userName -->
<text class="person_title">{{item.fromUser.userName}}</text>
<!-- item.message -->
<text class="content_body">{{item.message}}</text>
</view>
</scroll-view>
</view>
<!-- 消息输入 -->
<view class="enter_input" v-if="isShowKeyboad">
<textarea class="input_reply_msg" maxlength="50" :show-confirm-bar="false" v-model="msg_enter" auto-height
focus='true' placeholder="请输入消息" @input="inputHandle" />
<text class="send_msg" @click="sendMsg">发送</text>
</view>
<!-- 消息反馈模块 -->
<view class="interaction" v-if="!isShowKeyboad">
<!-- <view> -->
<!-- <zego-local-view class="push_live"></zego-local-view> -->
<!-- </view> -->
<text class="input_msg" @click="inputMsg">说点什么吧...</text>
<!-- <text class="like_click" @click="likeClick">点赞</text> -->
<likeButton :throttle="100" :large="2" :site="[60, 160]"
style="display: flex;position: fixed;bottom: 32rpx;right: 30rpx;">
</likeButton>
</view>
</view>
</template>
<script>
// 导入权限工具类
import permision from "@/pages/permission.js";
// 导入直播引擎
import ZegoExpressEngine from '@/components/zego-ZegoExpressUniApp-JS/lib/ZegoExpressEngine';
// 导入推流布局组件
import ZegoLocalView from '@/components/zego-ZegoExpressUniApp-JS/zego-view/ZegoLocalView';
// 导入拉流布局组件
import ZegoRemoteView from '@/components/zego-ZegoExpressUniApp-JS/zego-view/ZegoRemoteView';
//点赞按钮
import likeButton from '@/components/like-button/like-button.vue';
export default {
components: {
ZegoLocalView: ZegoLocalView,
ZegoRemoteView: ZegoRemoteView,
ZegoExpressEngine,
likeButton
},
data() {
return {
title: 'Hello',
isShowKeyboad: false,
msg_enter: "",
engine: undefined,
playStreamID: 'streamID123',
msg: [],
commentScrollTop: 999999, //评论区滚动高度
roomID: 'mghome123', //房间id
userID: '222222', //用户id
userName: '医生', //用户名
online: 0, //在线用户数
}
},
onLoad: function(option) { //option为object类型,会序列化上个页面传递的参数
console.log("=====打印出上个页面传递的参数=="); //打印出上个页面传递的参数。
this.userID = Math.floor(Math.random(10000) * 10000) + ""; //用户id
this.userName = option.usName; //用户名
},
//返回键
onBackPress() {
console.log("===onBackPress===");
this.loginOut();
},
created() {
this.playStreamID = 'streamID123';
},
onReady() {
this.playStreamID = 'streamID123';
var that = this;
this.initZegoExpressEngine();
},
//隐藏
onHide() {
that.logoutRoom(that.roomID);
},
mounted() {
},
methods: {
async initZegoExpressEngine() {
var that = this;
console.log("==mounted==");
//Android 权限请求
if (uni.getSystemInfoSync().platform === "android") {
await permision.requestAndroidPermission(
"android.permission.RECORD_AUDIO"
);
await permision.requestAndroidPermission(
"android.permission.CAMERA"
);
}
//异步按顺序执行
new Promise(res => {
that.createEngine(); //创建视频流引擎
setTimeout(() => {
console.log("===mogu===");
res();
}, 100);
}).then(res => {
console.log("===mogu11===");
//登录房间
that.loginRoom(that.roomID, that.userID, that.userName);
}).then(res => {
console.log("===mogu22===");
that.addListeners(); //添加监听
});
},
//关闭页面
closed() {
this.loginOut();
},
//登出sasaa
loginOut() {
var that = this;
that.engine.stopPlayingStream(that.playStreamID);
that.logoutRoom(that.roomID);
ZegoExpressEngine.destroyEngine(res => {
});
uni.reLaunch({
url: '/pages/login/login'
})
// plus.runtime.quit();
},
//发送消息
sendMsg: function(e) {
var that = this;
// console.log("====发送消息====" + JSON.stringify(this.engine));
if (this.msg_enter == '' || this.msg_enter.length == 0) {
uni.showToast({
title: '输入不能为空',
icon: "error",
duration: 2000
});
return 1;
}
let msgConn = [{
"sendTime": 1687763213632,
"message": "" + this.msg_enter,
"messageID": 1,
"fromUser": {
"userID": this.userID,
"userName": this.userName
}
}];
that.msgListDeal(msgConn);
// 发送广播消息,每个登录房间的用户都会通过 IMRecvBroadcastMessage 回调事件收到此消息
this.engine.sendBroadcastMessage('mghome123', "" + this.$data.msg_enter).then((
result) => {
// 获取消息发送结果
console.log("====按钮发送====" + JSON.stringify(result));
that.msg_enter = '';
//隐藏软键盘方法
that.hide_edit();
})
},
// 接收消息
receiverMsg: function(e) {
var that = this;
this.engine.on("IMRecvBroadcastMessage", (mghome, messageList) => {
console.log("==1111==" + JSON.stringify(that.$data.msg));
that.msgListDeal(messageList);
console.log("==2222==" + JSON.stringify(that.$data.msg));
});
this.engine.on('IMRecvBarrageMessage', (mghome, messageList) => {
console.log("===received barrage message==" + JSON.stringify(messageList))
});
// this.engine.on('IMRecvCustomCommand', (mghome, fromUser, command) => {
// console.log("("===received custom command=="+JSON.stringify(messageList))
// })
},
//消息列表显示处理e
msgListDeal: function(messageList) {
var that = this;
if (that.$data.msg.length >= 10) { //只显示最新10条消息
that.$data.msg.shift();
}
that.$data.msg.push(...messageList); //新消息添加
setTimeout(() => {
this.commentScrollTop = this.commentScrollTop + 1;
}, 100);
},
//键盘监听
keyBoardHeightListener: function(e) {
uni.onKeyboardHeightChange(res => {
if (res.height == '0') { //隐藏键盘
this.$data.isShowKeyboad = false;
}
})
},
//输入消息监听
inputHandle(e) {
this.$data.msg_enter = e.detail.value;
console.log("==输入消息监听===" + e.detail.value)
},
// 键盘弹出
inputMsg() {
this.$data.isShowKeyboad = true;
// console.log("===键盘弹出===");
},
//隐藏键盘
hide_edit(e) {
console.log("===隐藏软键盘方法===");
this.$data.isShowKeyboad = false;
uni.hideKeyboard() //隐藏软键盘方法
},
//创建引擎
async createEngine() {
// 采用通用场景
const profile = {
appID: ,//换成自己的appid
// AppSign 仅满足简单的鉴权需求,如果需要升级为更加安全的鉴权方式,请参考[如何从 AppSign 鉴权升级为 Token 鉴权](https://doc-zh.zego.im/faq/token_upgrade?product=ExpressVideo&platform=all)
// AppSign 可通过[控制台](https://console.zego.im/dashboard)获取,格式为 @"39011cbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
appSign: '',//换成自己的appSign
scenario: 0
};
this.engine = await ZegoExpressEngine.createEngineWithProfile(
profile
);
console.log("==创建引擎==" + JSON.stringify(this.engine));
//设置分辨率
let config = {
encodeWidth: 360,
encodeHeight: 640
}
ZegoExpressEngine.instance().setVideoConfig(config);
},
//登录房间
async loginRoom(roomID, userID, userName) {
this.logoutRoom(roomID);
let roomConfig = {};
// 只有传入 “isUserStatusNotify” 参数取值为 “true” 的 ZegoRoomConfig,才能收到 onRoomUserUpdate 回调。
roomConfig.isUserStatusNotify = true;
// 登录房间
// 开始登录房间
this.engine.loginRoom(roomID, {
'userID': userID,
'userName': userName
}, roomConfig);
this.receiverMsg(); //接收消息监听
},
//登出房间
logoutRoom(roomID) {
this.engine.logoutRoom(roomID);
},
//推流
async likeClick() {
// #需要使用$nextTick延时调用,否则没有效果
console.log("==推流==");
/** 1. 创建美颜环境 */
// await this.engine.startEffectsEnv();
/** 开关美颜效果 */
// await this.engine.enableEffectsBeauty(true);
//js部分:
/** 开始预览 */
// ZegoExpressEngine.instance().startPreview();
/** 开始推流 APP-PULL stream无值, Web有值,为MediaStream*/
// const stream = await ZegoExpressEngine.instance().startPublishingStream("streamID123");
// 推流视频流监听
// ZegoExpressEngine.instance().on("publisherStateUpdate", (streamID, state, errorCode, extendedData) => {
// 调用推流接口成功后,当推流器状态发生变更,如出现网络中断导致推流异常等情况,SDK在重试推流的同时,会通过该回调通知
//....
// });
},
//房间监听
addListeners() {
console.log("=======房间监听======");
var that = this;
// 以下为常用的房间相关回调
that.engine.on('roomStateUpdate', (roomID, state, errorCode,
extendedData) => {
// 房间状态更新回调,登录房间后,当房间连接状态发生变更(如出现房间断开,登录认证失败等情况),SDK会通过该回调通知
console.log("==房间状态更新回调==" + JSON.stringify(roomID));
});;
that.engine.on('roomUserUpdate', (roomID, updateType, userList) => {
// 用户状态更新,登录房间后,当房间内有用户新增或删除时,SDK会通过该回调通知
console.log("==用户状态更新==" + JSON.stringify(userList));
if (updateType == 0) {
that.online = that.online + userList.length;
} else {
if (that.online >= userList.length) {
that.online = that.online - userList.length;
}
console.log("==用户状态更新==" + that.online);
}
});
that.engine.on('roomStreamUpdate', (roomID, updateType, streamList) => {
// 流状态更新,登录房间后,当房间内有用户新推送或删除音视频流时,SDK会通过该回调通知
// 开始拉流
if (streamList.length > 0) {
if (streamList[0].streamID != '') {
that.playStreamID = streamList[0].streamID;
const streamss = that.engine.startPlayingStream(streamList[0]
.streamID);
console.log("==流状态更新==" + JSON.stringify(streamList[0].streamID));
}
}
});
},
recycleData: function() {
var that = this;
//异步按顺序执行
// new Promise(res => {
// console.log("===mogu===");
// // setTimeout(() => {
// // res();
// // }, 100);
// /** 停止本地预览 */
// // that.engine.stopPreview();
// /** 停止推流 */
// // that.engine.stopPublishingStream();
// /** 停止拉流 */
// that.engine.stopPlayingStream(that.playStreamID);
// /** 退出房间 */
// that.engine.logoutRoom(that.roomID);
// /** 销毁引擎 */
// ZegoExpressEngine.destroyEngine();
// //移除键盘高度监听
// uni.offKeyboardHeightChange(res => {
// });
// console.log("===销毁引擎==="+that.playStreamID+"=="+that.roomID);
// });
}
},
onUnload() {
this.recycleData();
// console.log("===销毁引擎===");
}
}
</script>
<style>
.page {
display: flex;
flex-direction: column;
height: 100%;
background: transparent
}
.colum_layout {
flex: 1;
height: 100px;
}
.play_screen {
display: flex;
height: 100vh;
width: 100vh;
flex-direction: column;
justify-content: flex-end;
}
/* 在线人数模块 */
.onlines {
height: 90rpx;
width: 750rpx;
background: transparent;
position: absolute;
top: 90rpx;
align-items: center;
flex-direction: row;
justify-content: space-between;
}
/* */
.user_name {
height: 70rpx;
background-color: #09c2fa;
margin-left: 30rpx;
padding-top: 20rpx;
padding-left: 40rpx;
padding-right: 40rpx;
border-radius: 30rpx;
align-items: center;
text-align: center;
font-size: 29rpx;
color: #fff;
background-color: rgba(128, 128, 128, 0.2);
}
/* 在线模块 */
.onlineMd {
background: transparent;
margin-right: 40rpx;
flex-direction: row;
align-items: center;
}
/*在线标记*/
.onlineTag {
color: white;
margin-right: 8rpx;
font-size: 28rpx;
}
/* 在线用户数 */
.online_num {
width: 80rpx;
background-color: red;
height: 80rpx;
margin-right: 40rpx;
border-radius: 40rpx;
align-items: center;
text-align: center;
padding-top: 25rpx;
font-size: 28rpx;
background-color: rgba(128, 128, 128, 0.2);
color: #fff;
}
/* 直播父布局 */
.live_parent {
flex: 1;
height: 100px;
position: sticky;
}
/* 直播组件 */
.live_play {
flex: 1;
position: relative;
height: 100px;
z-index: -1
}
/* 拉流 */
.push_live {
height: 300rpx;
width: 400rpx;
position: absolute;
bottom: 100rpx;
display: flex;
z-index: 999
}
/* 消息列表 */
.list-content {
max-height: 300px;
padding-bottom: 30rpx;
}
/* 键盘未弹起消息布局 */
.msg_list_low {
position: absolute;
width: 420rpx;
height: 600rpx;
background-color: red;
background: transparent;
bottom: 160rpx;
justify-content: flex-end;
}
/* 键盘弹起消息布局 */
.msg_list_height {
position: absolute;
width: 420rpx;
/* height: 600rpx; */
background: transparent;
bottom: 300rpx;
max-height: 600rpx;
justify-content: flex-end;
}
/* 水平内容 */
.list-item {
background-color: yellow;
margin-top: 20rpx;
margin-left: 20rpx;
border-radius: 10rpx;
flex-direction: row;
background: transparent;
background-color: rgba(128, 128, 128, 0.1);
padding: 10rpx;
}
.person_title {
margin-left: 5rpx;
color: #09c2fa;
margin-right: 10rpx;
font-size: 28rpx;
}
.content_body {
font-size: 28rpx;
width: 344rpx;
color: #fcfcfc;
padding-right: 10rpx;
}
/* */
/* 消息输入按钮模块 */
.enter_input {
width: 750rpx;
position: absolute;
display: flex;
flex-direction: row;
justify-content: space-between;
bottom: 120rpx;
align-items: center;
background-color: white;
border: transparent;
}
/* 消息输入框 */
.input_reply_msg {
border: transparent;
flex: 1;
min-height: 66rpx;
padding-top: 10rpx;
padding-bottom: 10rpx;
background-color: #f0f0f0;
margin-left: 10rpx;
padding-left: 20rpx;
border-radius: 40rpx;
margin-right: 30rpx;
}
/* 发送消息 */
.send_msg {
width: 100rpx;
height: 58rpx;
font-size: 28rpx;
text-align: center;
margin-right: 30rpx;
padding-top: 13rpx;
background-color: #FF9999;
border-radius: 30rpx;
color: white;
margin-top: 15rpx;
margin-bottom: 15rpx;
}
/* 底部浮动布局 */
.interaction {
background: transparent;
background-color: transparent;
position: fixed;
bottom: 20rpx;
display: flex;
flex: 1;
width: 750rpx;
flex-direction: row;
justify-content: space-between;
}
/* 点击输入消息 */
.input_msg {
width: 320rpx;
margin-left: 30rpx;
height: 78rpx;
background-color: rgba(128, 128, 128, 0.3);
border-radius: 40rpx;
color: #999999;
padding-top: 20rpx;
text-align: left;
padding-left: 30rpx;
}
.like_click {
height: 70rpx;
width: 160rpx;
display: flex;
background-color: #06c6ad;
padding-top: 20rpx;
color: white;
font-weight: 600;
border-radius: 20rpx;
font-size: 28rpx;
margin-right: 40rpx;
text-align: center;
}
</style>