How to Build Rtmp Service and Develop Live APP with uni-app

Preface: Because you have an IM application, ...
2. Code implementation

Preface:

Because you have an IM application, you decide to add live and short video capabilities in order to improve it.There are currently two ways to do live broadcasting, one is to directly connect with the third party's live service products, the other is to develop the services themselves.So here are also two ways to recommend simple implementation, Aliyun and Tencent cloud and other large-scale products will be unsafe.(Public number replies Live to get source code)

Selection:

1. Third party, PHP+Uni-App+LiveQing

2. Develop your own, PHP+Uni-app+Nginx-rtmp-module

Implementation process:

1. Client collects video stream.(turn on camera, video, etc.)

2. Client pushes to rtmp server.

3. rtmp is pushed to a specific port.

4. Other clients will pull the video stream to make it live.

1. Third-party methods

This time, the third party recommended a platform called LiveQing, which is a little fast, convenient and fully functional.Running their packages on the server provides on-demand service for short videos as well as live broadcasting of mainstream business scenarios.API calls are also included to create, delete, and statistics live broadcasts through interfaces.But for a fee, the package can only be tried out for one month on a physical machine or a cloud server for free.

1. Find the official website, select rtmp live on-demand streaming media, download a trial to unzip the corresponding system to your own server.

2. The directory is as follows, which willStart.shAuthorization is 777.Then. /Start.shRun the file.

3. Open before runningLiveqing.iniMake settings such as background login password, port number, etc.

4. By default, you need to turn on 10080 and 10085, so you need to use the firewall to let go as follows.

systemctl start firewalld.service // Open Firewall firewall-cmd add-port=10080/tcp --permanent firewall-cmd add-port=10082/tcp --permanent firewall-cmd --reload // restart firewall-cmd --list-ports // View all ports released

5. Port release, then runStart.shThe following icon appears to indicate success.

6. The browser enters the server's external IP:10080 to enter the control panel.

7.Create a live broadcast, set the name and ID, and select Edit to get the push address.

8. In order to test that you can download an OBS software push to this address locally, as long as the push, the live status will be displayed in the live broadcast and click Edit to get the address of the pull.

9. Also for convenience, you can use VLS software to pull streams or wowza online website to test live streaming.

2. Code implementation

Without a third party, you need to set up rtmp services, configure Nginx, APP video capture push, pull and so on.If it is a large platform, you need to divert clusters, etc.Streaming Media Server Dependent Services,1.nginx The server;2.nginx Server installation requires dependent services OpenSSL, pcre, zlib, c++, gcc, etc. The server environment is Centos7.364-bit.

1. Enter the root directory, where mkdir source #creates the source directory, followed by the source code.cd source enters the directory.

2. Download git, yum-y install git, and then download the required packages over the network.

git clone https://github.com/nginx/nginx.git #Download the source code for nginx from the github server git clone https://github.com/arut/nginx-rtmp-module.git#Download the source code for the RTMP module wget https://www.openssl.org/source/openssl-1.1.0.tar.gz#Download OpenSSL Source Package wget https://ftp.pcre.org/pub/pcre/pcre-8.39.tar.gz#Download the PCRE source package wget http://www.zlib.net/zlib-1.2.11.tar.gz#Download zlib package source

3. tar-zxvf package name #Unzip individual package sources

4. gcc needs to be installed before compiling nginx and required packages, which can be omitted.

yum -y install gcc #Ensure dependent gcc installation yum -y install gcc-c++ #Ensure that dependent c++ is installed

5. Then the cd command enters the nginx directory under source and enters the following command.

./auto/configure --prefix=/usr/local/nginx \ --with-pcre=../pcre-8.39 \ --with-openssl=../openssl-1.1.0 \ --with-zlib=../zlib-1.2.11 \ --with-http_v2_module \ --with-http_flv_module \ --with-http_mp4_module \ --add-module=../nginx-rtmp-module/

6. Check for success as follows, and make compiles it.

7. make install installation

8. This means that the Nginx compilation and installation is complete, then cd to the root directory, /usr/local/nginx/sbin, if you want to test whether Nginx is accessible.Release port 80 to restart the firewall and enter. /nginx under SBIN to start the Nginx service.Browser access IP address: 80, the following indicates success.

9. Configure the rtmp service in the nginx configuration file, remember that the rtmp service is level with the http service, so we need to start the rtmp service at the same location as the http configuration level.

vi /usr/local/nginx/conf/nginx.conf #Modify Profile
rtmp { server { listen 1935; chunk_size 4096; application live { live on; record off; } application live2 { live on; record off; } application vod { play /var/flvs; } application vod_http { play http://ip/vod of the server; } application hls { live on; hls on; hls_path /tmp/hls; } } }
/usr/local/nginx/sbin/nginx -s reload #Modify configuration file to restart nginx service

10. The port of the rtmp service above is 1935, so you also need to let port 1935 go as before, check if the security group of the cloud server will also let go, and restart the firewall.

11. The local computer tests whether 1935 is on or off, you can cmd command telnet server IP address port number, if an interface appears, the port is already open.

12. You can then push through OBS to the address and test with WOWZA pull.

rtmp://Your server ip:port (1935)/live#URL fills in the address of the stream

13. Next, we will demonstrate the push writing of uni-app.

<template> <view> <view> <view @click="back"> <image src="../../static/zhiwen-livepush/back2.png"></image> <view>Return</view> </view> <view @click="switchCamera"> <image src="../../static/zhiwen-livepush/reversal.png"></image> <view>Flip</view> </view> <view @click="startPusher"> <view></view> <view :class="begin==true?'givebegin':'give'" >{}</view> <view v-if="begin"></view> </view> <view> <image src="../../static/zhiwen-livepush/beautiful.png"></image> <view>Beautify</view> </view> <view v-if="begin==false"> <picker :value="index" @change="bindPickerChange" :range="array" range-key='cont'> <image src="../../static/zhiwen-livepush/countdown.png"></image> <view>Count down</view> </picker> </view> <view @click="upload" v-if="begin"> <image src="../../static/zhiwen-livepush/yes.png"></image> <view>complete</view> </view> </view> </view> </template> <script> export default { data() { return { begin:false,//start recording complete:false,//Recording Completed pause:false,//Pause Push Flow currentWebview:null, pusher:null, livepushurl:'rtmp://106.52.216.244:10089/hls/1', //Just modify your own push address here logininfokey:'',//Logon authentication encryption string, homeworkcont:'',//Job Information jiexititle:'',//Job Resolution Title index: 0,//timing indextu:0,//Whether to turn on the timer contTime:'', array: [{//Topic Label "id": 1, "cont": "10 second", "time": 10 }, { "id": 2, "cont": "20 second", "time": 20 }, { "id": 3, "cont": "30 second", "time": 30 }, { "id": 4, "cont": "40 second", "time": 40 },{ "id": 5, "cont": "50 second", "time": 50 }, { "id": 6, "cont": "60 second", "time": 60 }], } }, onShow() { uni.getNetworkType({ success: function (res) { console.log(res.networkType); if(res.networkType != 'wifi'){ uni.showModal({ //Remind users to update title: 'Reminder', content: 'Current Non Wifi Network, please note that your traffic is sufficient', success: (res) => { } }) } } }); uni.onNetworkStatusChange(function (res) { console.log(res.isConnected); console.log(res.networkType); if(res.networkType != '4g' && res.networkType != 'wifi'){ uni.showModal({ //Remind users to update title: 'Reminder', content: 'Current network quality is poor, please switch to 4 G Network or Wifi network', 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()//Get webview }, methods: { //Count down bindPickerChange: function(e) { console.log('picker Send selection changes with a carry value of', e.target.value) this.index = e.target.value // this.indexs = e.target.value this.contTime=this.array[e.target.value].time uni.showToast({ title: 'Please click the red button to start countdown', icon:'none', duration: 4000, }); }, /** * Return */ back(){ uni.showModal({ title: 'Tips', content: 'Video not uploaded after returning needs to be re-recorded', 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('User Click Cancel'); } } }); }, /** * Get the currently displayed 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()//Create LivePusher Object }, /** * Creating a LivePusher object is a push object */ plusReady(){ // Create Live Push Control this.pusher =new plus.video.LivePusher('pusher',{ url:'', top:'0', left:'0px', width: '100%', height: uni.getSystemInfoSync().windowHeight-15 + 'px', position: 'absolute',//Static static layout mode, scrolls with window content if page has scrollbars, absolute layout mode, does not scroll with window content if page has scrollbars; default value is "static" beauty:'0',//Beauty 0-off 1-on whiteness:'0',//0, 1, 2, 3, 4, 5, 0 do not use whitening, the higher the value, the whiter the degree. aspect:'9:16', }); console.log(JSON.stringify(this.pusher)) console.log(JSON.stringify(this.currentWebview)) //Append created objects to webview this.currentWebview.append(this.pusher); // Listen for state change events this.pusher.addEventListener('statechange',(e)=>{ console.log('statechange: '+JSON.stringify(e)); }, false); }, //Beauty beautiful(){ console.log(JSON.stringify(this.pusher)) this.pusher.options.beauty=1 this.plusReady()//Create LivePusher Object }, // Start pushing startPusher(){ //Determine whether the countdown starts if(this.contTime!=''){ if(this.indextu!=1){ this.conttimejs() } }else{ this.beginlivepush() } }, conttimejs(){ if(this.contTime!=''){ this.indextu=1;//Open Timing if(this.contTime==1){ console.log("start") this.contTime="" this.beginlivepush() return false } this.contTime-- setTimeout(()=>{ this.conttimejs() },1000) } }, beginlivepush() { this.indextu=0;//Close Timing if(this.begin==false){//Unopened Push Flow this.begin=true;//Show Video Brake Picture // Set up a Push Server***This needs to be retrieved back-end via ajax this.pusher.setOptions({ url:this.livepushurl //Push Address*******************************Set Push Address Here }); this.pusher.start();//Push Open uni.showToast({ title: 'start recording', icon:'none', duration: 2000, }); }else{ if(this.pause==true){//Pause Push State this.begin=true;//Show Video Brake Picture this.pause=false;//Push switch set to default state this.pusher.resume();//Restore Push Flow uni.showToast({ title: 'start recording', icon:'none', duration: 2000, }); }else{ this.begin=false;//Turn off recording of brake pictures this.pause=true;//Push pause this.pusher.pause();;//Pause Push Flow uni.showToast({ title: 'Pause recording', icon:'none', duration: 2000, }); //Prompt for upload this.upload() } } }, /** * Switch Camera */ switchCamera() { this.pusher.switchCamera(); }, /** * Complete recording */ upload(){ uni.showModal({ title: 'Tips', content: 'Are you sure you want to save it?', success:(res)=> { if (res.confirm) { console.log('User Click Finish'); this.pusher.pause();;//Pause Push Flow this.endlivepush() /* setTimeout(()=>{ this.endlivepush() },1000) */ } else if (res.cancel) { console.log('User Click Cancel'); } } }); }, //End the push flow, where you need to call the back-end interface to submit the end state to the cloud service provider endlivepush(){ uni.showToast({ icon:'loading', title: 'End...', 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: 'Video Uploading...', duration: 5000 }); setTimeout(()=>{ uni.showToast({ icon:'none', title: 'Upload complete', duration: 2000 }); },5000) setTimeout(()=>{ uni.redirectTo({ url: 'setvideotit?id='+this.homeworkcont.id, }); },7000) }, error: (data)=>{ //alert(JSON.stringify(data) +'error') } }); }, }, 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); } /* Circle that animates (expands outward) */ .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; } /** * animation */ @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. Draw demo code.

<template> <view> <view v-if="beCalling"> <view>You are invited to start a video chat</view> <view> <view @click="rejectCallHandler"> <image src="../../static/img/netcall-reject.png"></image> </view> <view @click="acceptCallHandler"> <image src="../../static/img/netcall-accept.png"></image> </view> </view> </view> <view v-else> <view @click="switchaudio"> <image src="../../static/img/netcall-call-voice.png"></image> </view> <view @click="switchCamera"> <image src="../../static/img/netcall-revert-camera.png"></image> </view> <view @click="close"> <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()//Get webview }, onUnload() { }, methods: { close(){ this.pusher.pause();//Pause Push Flow this.pusher.close()//Turn off the push control 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()//Create LivePusher Object }, plusReady(){ this.pushers =new plus.video.VideoPlayer('video',{ // src:self.userlist[0].url, src:"rtmp://58.200.131.2:1935/livetv/hunantv", //Replace your own pull address here top:'0px', left:'0px', controls:false, width: '100%', height: uni.getSystemInfoSync().windowHeight-150 + 'px', position: 'static' }); this.currentWebview.append(this.pushers); this.pushers.play() }, /** * Switch Camera */ switchCamera() { this.pusher.switchCamera(); }, switchaudio() { console.log('Clicked'); } } } </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%; } /* called */ .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 Container */ .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. The uni-app module permissions are as follows.

19 May 2020, 13:06 | Views: 2368

Add new comment

For adding a comment, please log in
or create account

0 comments