1. 创建自己的语音直播间
2. 查询所有直播间列表
3.加入房间
4.申请上位
5.麦克风控制
6.声音控制
7.赠送礼物(特效 + 批量移动动画)
8.退出房间
1.uniapp 实现客户端H5、安卓、苹果
2.webRTC实现语音直播间(具体原理网上有很多文章我就不讲了,贴个图)
3.使用node.js搭建信令服务器(我用的是socket)
4.礼物及特效使用svga
1. 客户端(这里重点在于app端)一定要在视图层创建webRTC!!!不要在逻辑层创建!!!因为会要求使用安全连接,也就是说要用到SSL证书,这个很多人都没有,有的话当我没说。如何在视图层创建RTC呢?在uniapp中使用renderjs!
2. (这里重点也在于app)客户端创建和信令服务器进行通信的socket时app端在页面跳转后socket状态消失无法响应信令服务器消息。解决方案是:一定不要在客户端视图层创建socket!!!也就是说socket不要创建在renderjs里,要在逻辑层用uniapp提供的api进行创建,然后使用uniapp文档中说明的逻辑层和视图层的通信方式进行通信,这样虽然在开发中有些繁琐,但是能解决问题。
onShow(){ // socketTask是使用uniapp提供的uni.connectSocket创建出来的socket实例 // watchSocketMessage代理了socket实例的onMessage方法 socketTask.watchSocketMessage = (data) => { this.watchSocketMessage(data) } } methed:{ watchSocketMessage(){ // 这里是收到信令服务器socket后的逻辑 } }
// 这里是逻辑层和renderjs通信的方式,通过监听状态的改变从而触发renderjs的对应的方法 // 注意在页面刚加载完成后这些方法会被默认触发一边,所以要在这些放方法做好判断return出去
3.连接顺序的问题,一定是:新进入的用户通过信令服务器给房间已有用户发送Offer,用户接收到Offer回应Answer,记住这个逻辑!
4.因为webRTC是运行在视图层的(也就是浏览器),而苹果默认浏览器是Safari,Safari浏览器默认机制是在用户主动和页面进行交互后,自动播放声音才会生效(也就是才有声音),所以在IOS端所有用户进入直播房间后默认都是静音的,用户主动开启音频才会受到直播间的声音(这是目前我发现的最好的解决办法)
1. 客户端socket
const socketTask = { socket: null, connect: () => { getApp().globalData.socket = uni.connectSocket({ url:'ws://180.76.158.110:9000/socket/websocketv', // url: 'ws://192.168.3.254:9000/socket/websocketv', complete: (e) => { console.log(e); }, }); getApp().globalData.socket.onOpen((data) => { console.log("111111111"); getApp().globalData.socket.send({ data: JSON.stringify({ type: "newConnect", userId: uni.getStorageSync('user').id, }) }) }) getApp().globalData.socket.onClose((res) => { console.log("连接关闭", res); getApp().globalData.socket = null; setTimeout(() => { socketTask.connect() }, 3000) }) getApp().globalData.socket.onError((err) => { console.log("连接异常", err); getApp().globalData.socket = null; setTimeout(() => { socketTask.connect() }, 1) }) getApp().globalData.socket.onMessage((data) => { socketTask.watchSocketMessage(data) }) }, start: function() { this.connect() }, watchSocketMessage: function() { // 这里实现自己的业务逻辑 } } export default socketTask
2.客户端房间列表页
async onShow() { if (!getApp().globalData.socket) { await socketTask.start(); } socketTask.watchSocketMessage = (data) => { console.log("===========收到新消息==========",data); this.watchSocketMessages(data) } }, methed:{ // 监听socket消息 watchSocketMessages(res) { try { const socket_msg = JSON.parse(res.data); console.log("收到新消息", socket_msg); switch (socket_msg.type) { case "homeList": if (socket_msg.data.length == 0) { this.homeList = []; uni.showToast({ title: "暂无房间,快去创建一个吧", icon: "none" }) } else { this.homeList = socket_msg.data; } break case "leave": getApp().globalData.socket.send({ data: JSON.stringify({ type: "homeList", userId: this.userInfo.userId, }) }) break case "createSuccess": uni.redirectTo({ url: `broadRoom?rid=${socket_msg.data.groupId}&&userId=${this.userInfo.id}&&groupInfo=${JSON.stringify(socket_msg.data)}` }) break } } catch (e) { } }, }
3.客户端直播间
逻辑层:
async onShow() { const that = this; if (!getApp().globalData.socket) { console.log("socket不存在,重新连接"); await socketTask.start(); } socketTask.watchSocketMessage = (data) => { this.watchSocketMessage(data) } // 编译平台信息 uni.getSystemInfo({ success(res) { console.log("当前平台是", res); if (res.osName == 'ios') { console.log("我是ios", res) that.isMedia = 'ios'; } else { console.log("我是安卓", res) that.isMedia = 'android'; } } }) } methed:{ async watchSocketMessage(date) { const data = JSON.parse(date.data); switch (data.type) { case "join": console.log("join成功", data); this.newMessaGes(data); this.setUserList(data.admin); this.updataNewMic(data) // 找出自己以外的其他用户 const arr = this.userList.filter((item, index) => { return item.userId !== this.userId }) console.log("找出自己以外的其他用户", arr) // 通知renderjs层创建RTC this.RTCJoin = arr; this.updataIsShow() break case "newjoin": this.newMessaGes(data); this.setUserList(data.admin); break case "offer": //通知renderjs层有新人进入创建answer console.log("收到offer", data) this.RTCOffier = data; break case "answer": // 找到对应peer,设置answer console.log("收到offer", data) this.RTCAnswer = data; break case "candidate": // 找到对应的peer,将candidate添加进去 this.RTCCandidate = data; break case "leave": if (data.data == "房主已解散房间") { this.closesAdmin() } else { const datas = { data, } this.newMessaGes(datas) this.setUserList(data.admin); this.updataNewMic(data); } break case "apply-admin": this.updataIsApply(data.data) break case "newMic": this.updataNewMic(data) break case "uplMicro": this.updataNewMic(data) break case "newMessage": this.newMess = data; break } }, }
视图层:
4.信令服务器
略(就是socket,里面写swich,不会私信,小额收费)