如何结合uni-app开发直播APP,搭建个人专属的RTMP流媒体服务?

2026-04-02 03:221阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计3596个文字,预计阅读时间需要15分钟。

如何结合uni-app开发直播APP,搭建个人专属的RTMP流媒体服务?

前言:由于自己有一个IM类应用,为了完善它,也决定增加直播和短视频功能。在制作直播功能之前,已有两种方法,一是直接对接第三方的直播服务产品,二是自己搭建服务再进行开发。

前言:

由于自己有一个IM类的应用,为了完善它所以决定也加上直播和短视频功能。做直播目前有两种方法,一是直接对接第三方的直播服务产品,二是自己搭服务再开发。所以这里也从这两个方法推荐简单的实现方式,阿里云和腾讯云之类的大厂产品就不安利了。(公众号回复“直播”获取源码)

选型:

1. 第三方,PHP+Uni-App+LiveQing

2. 自己开发,PHP+Uni-app+Nginx-rtmp-module

实现流程:

1. 客户端采集视频流。(开摄像头,录屏等)

2. 客户端推流到rtmp服务器上。

3. rtmp推流到某个特定端口。

4. 其他客户端再对该视频流进行拉流,实现直播。

第三方方式

第三方这次推荐的是一个叫LiveQing的平台,有点是搭建快捷方便,功能完善。在服务器上运行了他们的包后除了能实现主流业务场景的直播,而且还提供短视频的点播服务。还包括API调用,通过接口实现直播的创建,删除,直播数据统计。但是是要收费,该软件包在一台物理机或云服务器上只能免费试用一个月。

1. 找到该官网,选择rtmp直播点播流媒体,下载试用把对应系统解压到自己服务器。

2. 目录如下,将start.sh授权为777。然后./start.sh 运行该文件。

3. 运行前可以打开liveqing.ini进行设置,比如后台登录密码,端口号等。

4. 默认需要开启10080和10085,所以需要用防火墙放行,操作如下。

systemctl start firewalld.service // 开启防火墙 firewall-cmd add-port=10080/tcp --permanent firewall-cmd add-port=10082/tcp --permanent firewall-cmd --reload // 重启 firewall-cmd --list-ports // 查看放行的所有端口

5. 端口放行,然后在运行start.sh出现下面图标表示成功。

6. IP:10080,就可以进入控制面板了。

如何结合uni-app开发直播APP,搭建个人专属的RTMP流媒体服务?

7. 创建一个直播,设置名称和ID,然后选择编辑获取推流地址。

8. 为了测试可以本地下载一个OBS软件推流到该地址,只要一推流,直播状态就会显示直播中并且点击编辑可以获取拉流的地址。

9. 同样为了方便可以使用VLS软件进行拉流或者wowza在线网站测试直播。

代码实现

不使用第三方的话,就需要搭建rtmp服务,配置Nginx,APP视频采集推流,拉流等等。如果是大型平台,需要进行分流集群等。流媒体服务器依赖的服务,1.nginx 服务器;2.nginx服务器安装需要依赖的服务 OpenSSL、pcre、zlib、 c++、gcc等,服务器环境是Centos 7.3 64 位。

1. 进入根目录,mkdir source #创建源码目录,后面的源码都放在这个目录。cd source进入该目录。

2. 下载git,yum -y install git,然后通过网络下载需要的包。

git clone github.com/nginx/nginx.git #从github服务器上将nginx的源代码下载下来 git clone github.com/arut/nginx-rtmp-module.git #将rtmp模块的源码下载下来 wget www.openssl.org/source/openssl-1.1.0.tar.gz #下载OpenSSL源码包 wget ftp.pcre.org/pub/pcre/pcre-8.39.tar.gz #下载pcre源码包 wget www.zlib.net/zlib-1.2.11.tar.gz #下载zlib包源码

3.tar -zxvf 包名 #解压各个包源码

4. 在将nginx和需要的包编译前需要先安装gcc,安装过可以省过。

yum -y install gcc #确保依赖的gcc安装 yum -y install gcc-c++ #确保依赖的c++已经安装

5. 然后cd命令进入source下的nginx目录,输入下面命令。

./auto/configure --prefix=/usr/local/nginx \ --with-pcre=../pcre-8.39 \ --with-openssl=../openssl-1.1.0 \ --with-zlib=../zlib-1.2.11 \ --with-服务器的ip/vod; } application hls { live on; hls on; hls_path /tmp/hls; } } }

/usr/local/nginx/sbin/nginx -s reload #修改配置文件重启nginx服务

10. 上面rtmp服务的端口是1935,所以也需要按之前方法给1935端口放行,检查云服务器的安全组是否也放行,然后再重启防火墙。

11. 本地电脑测试1935是否开启,可以cmd命令telnet 服务器IP地址 端口号,如果出现一下界面说明端口已经通了 。

12. 接下来也可以通过OBS推流到该地址,然后用WOWZA拉流进行测试。

rtmp://你的服务器ip:端口(1935)/live #URL填写流的地址

13. 接下来演示uni-app的推流写法。

<template> <view class="content"> <view class="butlist"> <view @click="back" class="buticon martp10"> <image src="../../static/zhiwen-livepush/back2.png"></image> <view class="mar10">返回</view> </view> <view @click="switchCamera" class="buticon martp10"> <image src="../../static/zhiwen-livepush/reversal.png"></image> <view class="mar10">翻转</view> </view> <view class=" buticon" @click="startPusher"> <view class="x_f"></view> <view :class="begin==true?'givebegin':'give'" >{{contTime}}</view> <view class="pulse" v-if="begin"></view> </view> <view class="buticon martp10"> <image src="../../static/zhiwen-livepush/beautiful.png"></image> <view class="mar10">美化</view> </view> <view class="buticon martp10" v-if="begin==false"> <picker :value="index" @change="bindPickerChange" :range="array" range-key='cont'> <image src="../../static/zhiwen-livepush/countdown.png"></image> <view class="mar10">倒计时</view> </picker> </view> <view @click="upload" class="buticon martp10" v-if="begin"> <image src="../../static/zhiwen-livepush/yes.png"></image> <view class="mar10">完成</view> </view> </view> </view> </template> <script> export default { data() { return { begin:false,//开始录制 complete:false,//录制完毕 pause:false,//暂停推流 currentWebview:null, pusher:null, livepushurl:'rtmp://106.52.216.244:10089/hls/1', //这里修改自己的推流地址就可以了 logininfokey:'',//登录验证加密串, homeworkcont:'',//作业信息 jiexititle:'',//作业解析标题 index: 0,//定时 indextu:0,//是否开启定时 contTime:'', array: [{//话题标签 "id": 1, "cont": "10秒", "time": 10 }, { "id": 2, "cont": "20秒", "time": 20 }, { "id": 3, "cont": "30秒", "time": 30 }, { "id": 4, "cont": "40秒", "time": 40 },{ "id": 5, "cont": "50秒", "time": 50 }, { "id": 6, "cont": "60秒", "time": 60 }], } }, onShow() { uni.getNetworkType({ success: function (res) { console.log(res.networkType); if(res.networkType != 'wifi'){ uni.showModal({ //提醒用户更新 title: '温馨提示', content: '当前非Wifi网络,请注意您的流量是否够用', success: (res) => { } }) } } }); uni.onNetworkStatusChange(function (res) { console.log(res.isConnected); console.log(res.networkType); if(res.networkType != '4g' && res.networkType != 'wifi'){ uni.showModal({ //提醒用户更新 title: '温馨提示', content: '当前网络质量差,请切换为4G网络或Wifi网络', success: (res) => { } }) } }); /* plus.key.addEventListener("backbutton",()=>{ console.log("BackButton Key pressed!" ); //this.back() return false }); */ }, onBackPress(){ this.back() console.log("BackButton Key pressed!" ); return true; }, onLoad(res) { console.log(res) this.jiexititle=res.title uni.getStorage({ key: 'logininfokey', success:(res) =>{ console.log(res.data); this.logininfokey=res.data console.log(this.logininfokey) } }); uni.getStorage({ key: 'clickworkcont', success:(res) =>{ console.log(res.data); this.homeworkcont=res.data //console.log(this.logininfokey) } }); uni.getStorage({ key: 'livepushurl', success:(res) =>{ console.log(res.data); this.livepushurl=res.data } }); console.log(this.livepushurl) this.getwebview()//获取webview }, methods: { //倒计时 bindPickerChange: function(e) { console.log('picker发送选择改变,携带值为', e.target.value) this.index = e.target.value // this.indexs = e.target.value this.contTime=this.array[e.target.value].time uni.showToast({ title: '请点击红色按钮,开始进入倒计时', icon:'none', duration: 4000, }); }, /** * 返回 */ back(){ uni.showModal({ title: '提示', content: '返回后未上传的视频需要重新录制哦', success: function (res) { if (res.confirm) { /* this.currentWebview=null; this.pusher=null */ uni.redirectTo({ url:'../user/issue' }) //this.currentWebview=null } else if (res.cancel) { console.log('用户点击取消'); } } }); }, /** * 获取当前显示的webview */ getwebview(){ var pages = getCurrentPages(); var page = pages[pages.length - 1]; // #ifdef APP-PLUS var getcurrentWebview = page.$getAppWebview(); console.log(this.pages) console.log(this.page) console.log(JSON.stringify(page.$getAppWebview())) this.currentWebview=getcurrentWebview; // #endif this.plusReady()//创建LivePusher对象 }, /** * 创建LivePusher对象 即推流对象 */ plusReady(){ // 创建直播推流控件 this.pusher =new plus.video.LivePusher('pusher',{ url:'', top:'0', left:'0px', width: '100%', height: uni.getSystemInfoSync().windowHeight-15 + 'px', position: 'absolute',//static静态布局模式,如果页面存在滚动条则随窗口内容滚动,absolute绝对布局模式,如果页面存在滚动条不随窗口内容滚动; 默认值为"static" beauty:'0',//美颜 0-off 1-on whiteness:'0',//0、1、2、3、4、5,0不使用美白,值越大美白程度越大。 aspect:'9:16', }); console.log(JSON.stringify(this.pusher)) console.log(JSON.stringify(this.currentWebview)) //将创建的对象 追加到webview中 this.currentWebview.append(this.pusher); // 监听状态变化事件 this.pusher.addEventListener('statechange',(e)=>{ console.log('statechange: '+JSON.stringify(e)); }, false); }, //美颜 beautiful(){ console.log(JSON.stringify(this.pusher)) this.pusher.options.beauty=1 this.plusReady()//创建LivePusher对象 }, // 开始推流 startPusher(){ //判断是否倒计时开始 if(this.contTime!=''){ if(this.indextu!=1){ this.conttimejs() } }else{ this.beginlivepush() } }, conttimejs(){ if(this.contTime!=''){ this.indextu=1;//开启计时 if(this.contTime==1){ console.log("开始") this.contTime="" this.beginlivepush() return false } this.contTime-- setTimeout(()=>{ this.conttimejs() },1000) } }, beginlivepush() { this.indextu=0;//关闭计时 if(this.begin==false){//未开启推流 this.begin=true;//显示录制动画 // 设置推流服务器 ***此处需要通过ajax向后端获取 this.pusher.setOptions({ url:this.livepushurl //推流地址********************************* 此处设置推流地址 }); this.pusher.start();//推流开启 uni.showToast({ title: '开始录制', icon:'none', duration: 2000, }); }else{ if(this.pause==true){//暂停推流状态 this.begin=true;//显示录制动画 this.pause=false;//推流开关置为默认状态 this.pusher.resume();//恢复推流 uni.showToast({ title: '开始录制', icon:'none', duration: 2000, }); }else{ this.begin=false;//关闭录制动画 this.pause=true;//推流暂停 this.pusher.pause();;//暂停推流 uni.showToast({ title: '暂停录制', icon:'none', duration: 2000, }); //提示是否上传 this.upload() } } }, /** * 切换摄像头 */ switchCamera() { this.pusher.switchCamera(); }, /** * 完成录制 */ upload(){ uni.showModal({ title: '提示', content: '确定保存吗', success:(res)=> { if (res.confirm) { console.log('用户点击完成'); this.pusher.pause();;//暂停推流 this.endlivepush() /* setTimeout(()=>{ this.endlivepush() },1000) */ } else if (res.cancel) { console.log('用户点击取消'); } } }); }, //结束推流,此处需要调用后台接口向云服务商提交结束状态 endlivepush(){ uni.showToast({ icon:'loading', title: '结束...', duration: 5000 }); return false uni.request({ url: "", method: 'POST', // dataType:'JSON', data:{}, success:(res)=>{ console.log(JSON.parse(res.data)) console.log(JSON.stringify(res.data)) uni.showToast({ icon:'loading', title: '视频上传中...', duration: 5000 }); setTimeout(()=>{ uni.showToast({ icon:'none', title: '上传完成', duration: 2000 }); },5000) setTimeout(()=>{ uni.redirectTo({ url: 'setvideotit?id='+this.homeworkcont.id, }); },7000) }, error: (data)=>{ //alert(JSON.stringify(data)+'错误') } }); }, }, components:{ } } </script> <style>.content{ background: #000; overflow: hidden; } .butlist{ height: 140upx; position: absolute; bottom: 0; display: flex; width: 100%; justify-content: space-around; padding-top: 20upx; border-top: 1px solid #fff; background: #000; } .buticon{ height: 120upx; width: 120upx; color: #fff; position: relative; text-align: center; margin-bottom: 20upx; } .buticon image{ height: 64upx; width: 64upx; } .buticon .mar10{ margin-top: -20upx; } .martp10{ margin-top: 10upx; } .give { width: 90upx; height: 90upx; background: #F44336; border-radius: 50%; box-shadow: 0 0 22upx 0 rgb(252, 94, 20); position: absolute; left:15upx; top:15upx; font-size: 44upx; line-height: 90upx; } .givebegin { width: 60upx; height: 60upx; background: #F44336; border-radius: 20%; box-shadow: 0 0 22upx 0 rgb(252, 94, 20); position: absolute; left:30upx; top:30upx; } .x_f{ /* border: 6upx solid #F44336; */ width: 120upx; height: 120upx; background: #fff; border-radius: 50%; position: absolute; text-align: center; top:0; left: 0; box-shadow: 0 0 28upx 0 rgb(251, 99, 24); } /* 产生动画(向外扩散变大)的圆圈 */ .pulse { width: 160upx; height: 160upx; position: absolute; border: 12upx solid #F44336; border-radius: 100%; z-index: 1; opacity: 0; -webkit-animation: warn 2s ease-out; animation: warn 2s ease-out; -webkit-animation-iteration-count: infinite; animation-iteration-count: infinite; left: -28upx; top: -28upx; } /** * 动画 */ @keyframes warn { 0% { transform: scale(0); opacity: 0.0; } 25% { transform: scale(0); opacity: 0.1; } 50% { transform: scale(0.1); opacity: 0.3; } 75% { transform: scale(0.5); opacity: 0.5; } 100% { transform: scale(1); opacity: 0.0; } } </style>

14. 拉流演示代码。

<template class='fullscreen'> <view class='fullscreen'> <view v-if="beCalling" class="backols"> <view class='becalling-text'>对方邀请你开始视频聊天</view> <view class="butlist2"> <view @click="rejectCallHandler" class="buticon2 martp10"> <image src="../../static/img/netcall-reject.png"></image> </view> <view @click="acceptCallHandler" class="buticon2 martp10"> <image src="../../static/img/netcall-accept.png"></image> </view> </view> </view> <view v-else class="butlist"> <view @click="switchaudio" class="buticon martp10"> <image src="../../static/img/netcall-call-voice.png"></image> </view> <view @click="switchCamera" class="buticon martp10"> <image src="../../static/img/netcall-revert-camera.png"></image> </view> <view @click="close" class="buticon martp10"> <image src="../../static/img/netcall-reject.png"></image> </view> </view> </view> </template> <script> export default { data() { return{ beCalling: true, videourl:'', width:'', currentWebview:null, pushers:'', video :'' } }, onLoad: function (options) { this.getwebview()//获取webview }, onUnload() { }, methods: { close(){ this.pusher.pause();//暂停推流 this.pusher.close()//关闭推流控件 uni.switchTab({ url:'' }) }, getwebview(){ var pages = getCurrentPages(); var page = pages[pages.length - 1]; // #ifdef APP-PLUS var getcurrentWebview = page.$getAppWebview(); console.log(this.pages) console.log(this.page) console.log(JSON.stringify(page.$getAppWebview())) this.currentWebview=getcurrentWebview; // #endif this.plusReady()//创建LivePusher对象 }, plusReady(){ this.pushers =new plus.video.VideoPlayer('video',{ // src:self.userlist[0].url, src:"rtmp://58.200.131.2:1935/livetv/hunantv", //这里替换自己的拉流地址 top:'0px', left:'0px', controls:false, width: '100%', height: uni.getSystemInfoSync().windowHeight-150 + 'px', position: 'static' }); this.currentWebview.append(this.pushers); this.pushers.play() }, /** * 切换摄像头 */ switchCamera() { this.pusher.switchCamera(); }, switchaudio() { console.log('点击了'); } } } </script> <style> .backols{ background: rgba(0, 0, 0, 0.74); height: 100%; position: absolute; width: 100%; } uni-page{ background:#000000; } .butlist{ height: 140upx; position: absolute; bottom: 0; display: flex; width: 100%; justify-content: space-around; padding-top: 20upx; border-top: 1px solid #fff; } .buticon{ height: 120upx; width: 120upx; color: #fff; position: relative; text-align: center; margin-bottom: 20upx; } .buticon image{ height: 90upx; width: 90upx; } .buticon .mar10{ margin-top: -20upx; } .martp10{ margin-top: 10upx; } .becalling-text{ text-align: center; color: #FFFFFF; font-size: 28upx; padding: 60upx; margin-top: 40%; } .butlist2{ height: 140upx; position: absolute; bottom: 5%; display: flex; width: 100%; justify-content: space-around; padding-top: 20upx; } .buticon2{ height: 120upx; width: 120upx; color: #fff; position: relative; text-align: center; margin-bottom: 20upx; } .buticon2 image{ height: 110upx; width: 110upx; } .container { width: 100%; height: 100%; } /* 被叫 */ .becalling-wrapper { position: relative; width:100%; height:800upx; background-color:#777; color:#fff; font-size:40rpx; } .becalling-wrapper .becalling-text { position: absolute; top:400rpx; left:50%; margin-left:-220rpx; } .becalling-wrapper .becalling-button-group { position: absolute; width:100%; box-sizing:border-box; bottom: 100rpx; padding: 0 40rpx; display: flex; flex-direction: row; justify-content: space-between; } .becalling-button-group .button { width:220rpx; height:80rpx; border-radius:10rpx; justify-content:center; display:flex; align-items:center; font-size:33rpx; color:#000; } .becalling-button-group .reject-button { background-color:#f00; } .becalling-button-group .accept-button { background-color:rgb(26, 155, 252); } .calling-coverview { width:100%; height:100rpx; background-color:#ccc; color:#fff; font-size:40rpx; text-align:center; line-height:100rpx; } /* 视频容器 */ .video-wrapper { width: 100%; height: 100%; padding-bottom: 100rpx; box-sizing: border-box; position: relative; background-color: #000; } .control-wrapper { width: 100%; box-sizing: border-box; position: absolute; bottom: 0; } .calling-voerview { background-color:#ccc; color:#fff; height: 160rpx; font-size: 40rpx; text-align: center; line-height: 160rpx; } .control-wrapper { position: fixed; bottom: 18px; left:0; display: flex; width: 100%; box-sizing: border-box; flex-direction:row; justify-content: space-between; padding: 0 42rpx; height: 200rpx; } .control-wrapper .item{ width: 92rpx; height: 92rpx; margin-top: 100rpx; } .netcall-time-text { position:absolute; bottom:160rpx; width:100%; height: 40rpx; color:#fff; font-size:40rpx; text-align:center; left:0; } .fullscreen{ display: flex; background: #000000; height: 100%; width: 100%; position: absolute; } </style>

15. uni-app模块权限如下。

本文共计3596个文字,预计阅读时间需要15分钟。

如何结合uni-app开发直播APP,搭建个人专属的RTMP流媒体服务?

前言:由于自己有一个IM类应用,为了完善它,也决定增加直播和短视频功能。在制作直播功能之前,已有两种方法,一是直接对接第三方的直播服务产品,二是自己搭建服务再进行开发。

前言:

由于自己有一个IM类的应用,为了完善它所以决定也加上直播和短视频功能。做直播目前有两种方法,一是直接对接第三方的直播服务产品,二是自己搭服务再开发。所以这里也从这两个方法推荐简单的实现方式,阿里云和腾讯云之类的大厂产品就不安利了。(公众号回复“直播”获取源码)

选型:

1. 第三方,PHP+Uni-App+LiveQing

2. 自己开发,PHP+Uni-app+Nginx-rtmp-module

实现流程:

1. 客户端采集视频流。(开摄像头,录屏等)

2. 客户端推流到rtmp服务器上。

3. rtmp推流到某个特定端口。

4. 其他客户端再对该视频流进行拉流,实现直播。

第三方方式

第三方这次推荐的是一个叫LiveQing的平台,有点是搭建快捷方便,功能完善。在服务器上运行了他们的包后除了能实现主流业务场景的直播,而且还提供短视频的点播服务。还包括API调用,通过接口实现直播的创建,删除,直播数据统计。但是是要收费,该软件包在一台物理机或云服务器上只能免费试用一个月。

1. 找到该官网,选择rtmp直播点播流媒体,下载试用把对应系统解压到自己服务器。

2. 目录如下,将start.sh授权为777。然后./start.sh 运行该文件。

3. 运行前可以打开liveqing.ini进行设置,比如后台登录密码,端口号等。

4. 默认需要开启10080和10085,所以需要用防火墙放行,操作如下。

systemctl start firewalld.service // 开启防火墙 firewall-cmd add-port=10080/tcp --permanent firewall-cmd add-port=10082/tcp --permanent firewall-cmd --reload // 重启 firewall-cmd --list-ports // 查看放行的所有端口

5. 端口放行,然后在运行start.sh出现下面图标表示成功。

6. IP:10080,就可以进入控制面板了。

如何结合uni-app开发直播APP,搭建个人专属的RTMP流媒体服务?

7. 创建一个直播,设置名称和ID,然后选择编辑获取推流地址。

8. 为了测试可以本地下载一个OBS软件推流到该地址,只要一推流,直播状态就会显示直播中并且点击编辑可以获取拉流的地址。

9. 同样为了方便可以使用VLS软件进行拉流或者wowza在线网站测试直播。

代码实现

不使用第三方的话,就需要搭建rtmp服务,配置Nginx,APP视频采集推流,拉流等等。如果是大型平台,需要进行分流集群等。流媒体服务器依赖的服务,1.nginx 服务器;2.nginx服务器安装需要依赖的服务 OpenSSL、pcre、zlib、 c++、gcc等,服务器环境是Centos 7.3 64 位。

1. 进入根目录,mkdir source #创建源码目录,后面的源码都放在这个目录。cd source进入该目录。

2. 下载git,yum -y install git,然后通过网络下载需要的包。

git clone github.com/nginx/nginx.git #从github服务器上将nginx的源代码下载下来 git clone github.com/arut/nginx-rtmp-module.git #将rtmp模块的源码下载下来 wget www.openssl.org/source/openssl-1.1.0.tar.gz #下载OpenSSL源码包 wget ftp.pcre.org/pub/pcre/pcre-8.39.tar.gz #下载pcre源码包 wget www.zlib.net/zlib-1.2.11.tar.gz #下载zlib包源码

3.tar -zxvf 包名 #解压各个包源码

4. 在将nginx和需要的包编译前需要先安装gcc,安装过可以省过。

yum -y install gcc #确保依赖的gcc安装 yum -y install gcc-c++ #确保依赖的c++已经安装

5. 然后cd命令进入source下的nginx目录,输入下面命令。

./auto/configure --prefix=/usr/local/nginx \ --with-pcre=../pcre-8.39 \ --with-openssl=../openssl-1.1.0 \ --with-zlib=../zlib-1.2.11 \ --with-服务器的ip/vod; } application hls { live on; hls on; hls_path /tmp/hls; } } }

/usr/local/nginx/sbin/nginx -s reload #修改配置文件重启nginx服务

10. 上面rtmp服务的端口是1935,所以也需要按之前方法给1935端口放行,检查云服务器的安全组是否也放行,然后再重启防火墙。

11. 本地电脑测试1935是否开启,可以cmd命令telnet 服务器IP地址 端口号,如果出现一下界面说明端口已经通了 。

12. 接下来也可以通过OBS推流到该地址,然后用WOWZA拉流进行测试。

rtmp://你的服务器ip:端口(1935)/live #URL填写流的地址

13. 接下来演示uni-app的推流写法。

<template> <view class="content"> <view class="butlist"> <view @click="back" class="buticon martp10"> <image src="../../static/zhiwen-livepush/back2.png"></image> <view class="mar10">返回</view> </view> <view @click="switchCamera" class="buticon martp10"> <image src="../../static/zhiwen-livepush/reversal.png"></image> <view class="mar10">翻转</view> </view> <view class=" buticon" @click="startPusher"> <view class="x_f"></view> <view :class="begin==true?'givebegin':'give'" >{{contTime}}</view> <view class="pulse" v-if="begin"></view> </view> <view class="buticon martp10"> <image src="../../static/zhiwen-livepush/beautiful.png"></image> <view class="mar10">美化</view> </view> <view class="buticon martp10" v-if="begin==false"> <picker :value="index" @change="bindPickerChange" :range="array" range-key='cont'> <image src="../../static/zhiwen-livepush/countdown.png"></image> <view class="mar10">倒计时</view> </picker> </view> <view @click="upload" class="buticon martp10" v-if="begin"> <image src="../../static/zhiwen-livepush/yes.png"></image> <view class="mar10">完成</view> </view> </view> </view> </template> <script> export default { data() { return { begin:false,//开始录制 complete:false,//录制完毕 pause:false,//暂停推流 currentWebview:null, pusher:null, livepushurl:'rtmp://106.52.216.244:10089/hls/1', //这里修改自己的推流地址就可以了 logininfokey:'',//登录验证加密串, homeworkcont:'',//作业信息 jiexititle:'',//作业解析标题 index: 0,//定时 indextu:0,//是否开启定时 contTime:'', array: [{//话题标签 "id": 1, "cont": "10秒", "time": 10 }, { "id": 2, "cont": "20秒", "time": 20 }, { "id": 3, "cont": "30秒", "time": 30 }, { "id": 4, "cont": "40秒", "time": 40 },{ "id": 5, "cont": "50秒", "time": 50 }, { "id": 6, "cont": "60秒", "time": 60 }], } }, onShow() { uni.getNetworkType({ success: function (res) { console.log(res.networkType); if(res.networkType != 'wifi'){ uni.showModal({ //提醒用户更新 title: '温馨提示', content: '当前非Wifi网络,请注意您的流量是否够用', success: (res) => { } }) } } }); uni.onNetworkStatusChange(function (res) { console.log(res.isConnected); console.log(res.networkType); if(res.networkType != '4g' && res.networkType != 'wifi'){ uni.showModal({ //提醒用户更新 title: '温馨提示', content: '当前网络质量差,请切换为4G网络或Wifi网络', success: (res) => { } }) } }); /* plus.key.addEventListener("backbutton",()=>{ console.log("BackButton Key pressed!" ); //this.back() return false }); */ }, onBackPress(){ this.back() console.log("BackButton Key pressed!" ); return true; }, onLoad(res) { console.log(res) this.jiexititle=res.title uni.getStorage({ key: 'logininfokey', success:(res) =>{ console.log(res.data); this.logininfokey=res.data console.log(this.logininfokey) } }); uni.getStorage({ key: 'clickworkcont', success:(res) =>{ console.log(res.data); this.homeworkcont=res.data //console.log(this.logininfokey) } }); uni.getStorage({ key: 'livepushurl', success:(res) =>{ console.log(res.data); this.livepushurl=res.data } }); console.log(this.livepushurl) this.getwebview()//获取webview }, methods: { //倒计时 bindPickerChange: function(e) { console.log('picker发送选择改变,携带值为', e.target.value) this.index = e.target.value // this.indexs = e.target.value this.contTime=this.array[e.target.value].time uni.showToast({ title: '请点击红色按钮,开始进入倒计时', icon:'none', duration: 4000, }); }, /** * 返回 */ back(){ uni.showModal({ title: '提示', content: '返回后未上传的视频需要重新录制哦', success: function (res) { if (res.confirm) { /* this.currentWebview=null; this.pusher=null */ uni.redirectTo({ url:'../user/issue' }) //this.currentWebview=null } else if (res.cancel) { console.log('用户点击取消'); } } }); }, /** * 获取当前显示的webview */ getwebview(){ var pages = getCurrentPages(); var page = pages[pages.length - 1]; // #ifdef APP-PLUS var getcurrentWebview = page.$getAppWebview(); console.log(this.pages) console.log(this.page) console.log(JSON.stringify(page.$getAppWebview())) this.currentWebview=getcurrentWebview; // #endif this.plusReady()//创建LivePusher对象 }, /** * 创建LivePusher对象 即推流对象 */ plusReady(){ // 创建直播推流控件 this.pusher =new plus.video.LivePusher('pusher',{ url:'', top:'0', left:'0px', width: '100%', height: uni.getSystemInfoSync().windowHeight-15 + 'px', position: 'absolute',//static静态布局模式,如果页面存在滚动条则随窗口内容滚动,absolute绝对布局模式,如果页面存在滚动条不随窗口内容滚动; 默认值为"static" beauty:'0',//美颜 0-off 1-on whiteness:'0',//0、1、2、3、4、5,0不使用美白,值越大美白程度越大。 aspect:'9:16', }); console.log(JSON.stringify(this.pusher)) console.log(JSON.stringify(this.currentWebview)) //将创建的对象 追加到webview中 this.currentWebview.append(this.pusher); // 监听状态变化事件 this.pusher.addEventListener('statechange',(e)=>{ console.log('statechange: '+JSON.stringify(e)); }, false); }, //美颜 beautiful(){ console.log(JSON.stringify(this.pusher)) this.pusher.options.beauty=1 this.plusReady()//创建LivePusher对象 }, // 开始推流 startPusher(){ //判断是否倒计时开始 if(this.contTime!=''){ if(this.indextu!=1){ this.conttimejs() } }else{ this.beginlivepush() } }, conttimejs(){ if(this.contTime!=''){ this.indextu=1;//开启计时 if(this.contTime==1){ console.log("开始") this.contTime="" this.beginlivepush() return false } this.contTime-- setTimeout(()=>{ this.conttimejs() },1000) } }, beginlivepush() { this.indextu=0;//关闭计时 if(this.begin==false){//未开启推流 this.begin=true;//显示录制动画 // 设置推流服务器 ***此处需要通过ajax向后端获取 this.pusher.setOptions({ url:this.livepushurl //推流地址********************************* 此处设置推流地址 }); this.pusher.start();//推流开启 uni.showToast({ title: '开始录制', icon:'none', duration: 2000, }); }else{ if(this.pause==true){//暂停推流状态 this.begin=true;//显示录制动画 this.pause=false;//推流开关置为默认状态 this.pusher.resume();//恢复推流 uni.showToast({ title: '开始录制', icon:'none', duration: 2000, }); }else{ this.begin=false;//关闭录制动画 this.pause=true;//推流暂停 this.pusher.pause();;//暂停推流 uni.showToast({ title: '暂停录制', icon:'none', duration: 2000, }); //提示是否上传 this.upload() } } }, /** * 切换摄像头 */ switchCamera() { this.pusher.switchCamera(); }, /** * 完成录制 */ upload(){ uni.showModal({ title: '提示', content: '确定保存吗', success:(res)=> { if (res.confirm) { console.log('用户点击完成'); this.pusher.pause();;//暂停推流 this.endlivepush() /* setTimeout(()=>{ this.endlivepush() },1000) */ } else if (res.cancel) { console.log('用户点击取消'); } } }); }, //结束推流,此处需要调用后台接口向云服务商提交结束状态 endlivepush(){ uni.showToast({ icon:'loading', title: '结束...', duration: 5000 }); return false uni.request({ url: "", method: 'POST', // dataType:'JSON', data:{}, success:(res)=>{ console.log(JSON.parse(res.data)) console.log(JSON.stringify(res.data)) uni.showToast({ icon:'loading', title: '视频上传中...', duration: 5000 }); setTimeout(()=>{ uni.showToast({ icon:'none', title: '上传完成', duration: 2000 }); },5000) setTimeout(()=>{ uni.redirectTo({ url: 'setvideotit?id='+this.homeworkcont.id, }); },7000) }, error: (data)=>{ //alert(JSON.stringify(data)+'错误') } }); }, }, components:{ } } </script> <style>.content{ background: #000; overflow: hidden; } .butlist{ height: 140upx; position: absolute; bottom: 0; display: flex; width: 100%; justify-content: space-around; padding-top: 20upx; border-top: 1px solid #fff; background: #000; } .buticon{ height: 120upx; width: 120upx; color: #fff; position: relative; text-align: center; margin-bottom: 20upx; } .buticon image{ height: 64upx; width: 64upx; } .buticon .mar10{ margin-top: -20upx; } .martp10{ margin-top: 10upx; } .give { width: 90upx; height: 90upx; background: #F44336; border-radius: 50%; box-shadow: 0 0 22upx 0 rgb(252, 94, 20); position: absolute; left:15upx; top:15upx; font-size: 44upx; line-height: 90upx; } .givebegin { width: 60upx; height: 60upx; background: #F44336; border-radius: 20%; box-shadow: 0 0 22upx 0 rgb(252, 94, 20); position: absolute; left:30upx; top:30upx; } .x_f{ /* border: 6upx solid #F44336; */ width: 120upx; height: 120upx; background: #fff; border-radius: 50%; position: absolute; text-align: center; top:0; left: 0; box-shadow: 0 0 28upx 0 rgb(251, 99, 24); } /* 产生动画(向外扩散变大)的圆圈 */ .pulse { width: 160upx; height: 160upx; position: absolute; border: 12upx solid #F44336; border-radius: 100%; z-index: 1; opacity: 0; -webkit-animation: warn 2s ease-out; animation: warn 2s ease-out; -webkit-animation-iteration-count: infinite; animation-iteration-count: infinite; left: -28upx; top: -28upx; } /** * 动画 */ @keyframes warn { 0% { transform: scale(0); opacity: 0.0; } 25% { transform: scale(0); opacity: 0.1; } 50% { transform: scale(0.1); opacity: 0.3; } 75% { transform: scale(0.5); opacity: 0.5; } 100% { transform: scale(1); opacity: 0.0; } } </style>

14. 拉流演示代码。

<template class='fullscreen'> <view class='fullscreen'> <view v-if="beCalling" class="backols"> <view class='becalling-text'>对方邀请你开始视频聊天</view> <view class="butlist2"> <view @click="rejectCallHandler" class="buticon2 martp10"> <image src="../../static/img/netcall-reject.png"></image> </view> <view @click="acceptCallHandler" class="buticon2 martp10"> <image src="../../static/img/netcall-accept.png"></image> </view> </view> </view> <view v-else class="butlist"> <view @click="switchaudio" class="buticon martp10"> <image src="../../static/img/netcall-call-voice.png"></image> </view> <view @click="switchCamera" class="buticon martp10"> <image src="../../static/img/netcall-revert-camera.png"></image> </view> <view @click="close" class="buticon martp10"> <image src="../../static/img/netcall-reject.png"></image> </view> </view> </view> </template> <script> export default { data() { return{ beCalling: true, videourl:'', width:'', currentWebview:null, pushers:'', video :'' } }, onLoad: function (options) { this.getwebview()//获取webview }, onUnload() { }, methods: { close(){ this.pusher.pause();//暂停推流 this.pusher.close()//关闭推流控件 uni.switchTab({ url:'' }) }, getwebview(){ var pages = getCurrentPages(); var page = pages[pages.length - 1]; // #ifdef APP-PLUS var getcurrentWebview = page.$getAppWebview(); console.log(this.pages) console.log(this.page) console.log(JSON.stringify(page.$getAppWebview())) this.currentWebview=getcurrentWebview; // #endif this.plusReady()//创建LivePusher对象 }, plusReady(){ this.pushers =new plus.video.VideoPlayer('video',{ // src:self.userlist[0].url, src:"rtmp://58.200.131.2:1935/livetv/hunantv", //这里替换自己的拉流地址 top:'0px', left:'0px', controls:false, width: '100%', height: uni.getSystemInfoSync().windowHeight-150 + 'px', position: 'static' }); this.currentWebview.append(this.pushers); this.pushers.play() }, /** * 切换摄像头 */ switchCamera() { this.pusher.switchCamera(); }, switchaudio() { console.log('点击了'); } } } </script> <style> .backols{ background: rgba(0, 0, 0, 0.74); height: 100%; position: absolute; width: 100%; } uni-page{ background:#000000; } .butlist{ height: 140upx; position: absolute; bottom: 0; display: flex; width: 100%; justify-content: space-around; padding-top: 20upx; border-top: 1px solid #fff; } .buticon{ height: 120upx; width: 120upx; color: #fff; position: relative; text-align: center; margin-bottom: 20upx; } .buticon image{ height: 90upx; width: 90upx; } .buticon .mar10{ margin-top: -20upx; } .martp10{ margin-top: 10upx; } .becalling-text{ text-align: center; color: #FFFFFF; font-size: 28upx; padding: 60upx; margin-top: 40%; } .butlist2{ height: 140upx; position: absolute; bottom: 5%; display: flex; width: 100%; justify-content: space-around; padding-top: 20upx; } .buticon2{ height: 120upx; width: 120upx; color: #fff; position: relative; text-align: center; margin-bottom: 20upx; } .buticon2 image{ height: 110upx; width: 110upx; } .container { width: 100%; height: 100%; } /* 被叫 */ .becalling-wrapper { position: relative; width:100%; height:800upx; background-color:#777; color:#fff; font-size:40rpx; } .becalling-wrapper .becalling-text { position: absolute; top:400rpx; left:50%; margin-left:-220rpx; } .becalling-wrapper .becalling-button-group { position: absolute; width:100%; box-sizing:border-box; bottom: 100rpx; padding: 0 40rpx; display: flex; flex-direction: row; justify-content: space-between; } .becalling-button-group .button { width:220rpx; height:80rpx; border-radius:10rpx; justify-content:center; display:flex; align-items:center; font-size:33rpx; color:#000; } .becalling-button-group .reject-button { background-color:#f00; } .becalling-button-group .accept-button { background-color:rgb(26, 155, 252); } .calling-coverview { width:100%; height:100rpx; background-color:#ccc; color:#fff; font-size:40rpx; text-align:center; line-height:100rpx; } /* 视频容器 */ .video-wrapper { width: 100%; height: 100%; padding-bottom: 100rpx; box-sizing: border-box; position: relative; background-color: #000; } .control-wrapper { width: 100%; box-sizing: border-box; position: absolute; bottom: 0; } .calling-voerview { background-color:#ccc; color:#fff; height: 160rpx; font-size: 40rpx; text-align: center; line-height: 160rpx; } .control-wrapper { position: fixed; bottom: 18px; left:0; display: flex; width: 100%; box-sizing: border-box; flex-direction:row; justify-content: space-between; padding: 0 42rpx; height: 200rpx; } .control-wrapper .item{ width: 92rpx; height: 92rpx; margin-top: 100rpx; } .netcall-time-text { position:absolute; bottom:160rpx; width:100%; height: 40rpx; color:#fff; font-size:40rpx; text-align:center; left:0; } .fullscreen{ display: flex; background: #000000; height: 100%; width: 100%; position: absolute; } </style>

15. uni-app模块权限如下。