前言

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

选型

  1. 第三方,PHP+Uni-App+LiveQing
  2. 自己开发,PHP+Uni-app+Nginx-rtmp-module
    image.png

实现流程

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

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

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

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

第三方方式

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

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

2.目录如下,将start.sh授权为777。然后./start.sh 运行该文件。9dca62386839171521a2f85bc56bd53d_up-3f36fac82183eab4bf3a729d69433f79540.png

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

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出现下面图标表示成功。

a62ce9d4a06d4463c45b05f4c1660ee5_up-035ed0813a3c041dafaf93d7db49b6d0aa9.png

6.浏览器输入服务器的外网IP:10080,就可以进入控制面板了。

a54e8aed805bc3c85054b60dfee27290_up-8fd5a8302218bb7b9e766a1bde29a90a504.png

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

415ad21a95699eeeb3404be2544873c6_up-82d120345ad3d4f98b29ce5e564d7e4b716.png

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

f2f7f795387e0148000bce69378edbcf_up-bad6805998f014740e7ceecf6963f8b9cb4.png

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

9aa37e4e35c1152a066da552a0ec6724_up-9ddcf473aee2f997e9f1424d242d1c3b3bd.png

代码实现

不使用第三方的话,就需要搭建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 https://github.com/nginx/nginx.git                 #从github服务器上将nginx的源代码下载下来
git clone https://github.com/arut/nginx-rtmp-module.git     #将rtmp模块的源码下载下来
wget https://www.openssl.org/source/openssl-1.1.0.tar.gz     #下载OpenSSL源码包
wget https://ftp.pcre.org/pub/pcre/pcre-8.39.tar.gz         #下载pcre源码包
wget http://www.zlib.net/zlib-1.2.11.tar.gz                 #下载zlib包源码

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

399ff4757193df09c8a3bcb9d5df3fb0_up-07266295e531623c5335fad5d9b3ff3226f.png

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-http_v2_module \
        --with-http_flv_module \
        --with-http_mp4_module \
        --add-module=../nginx-rtmp-module/

a69fd2055fd351984da013e24721c340_up-7ef6df08788a6bec5ef7993fd24b8a31799.png

6.检查成功会出现如下,然后make编译一下。

d108b62fde4aedc753a11f31229a0a01_up-e1142e28e155c7c62c3c9a9940a57d2f2cd.png

7.make install 安装

f7e3fe5d8f64df16739d3c506e4668db_up-fef50613dc0717880ea14eb23594b56e3c7.png

8.以上操作后表示Nginx编译安装完成,然后cd到根目录,/usr/local/nginx/sbin,如果要测试Nginx是否可以访问。先放行80端口重启防火墙,在sbin下输入./nginx启动Nginx服务。浏览器访问IP地址:80,出现以下表示成功。

图片

9.在nginx配置文件中配置rtmp服务,记住rtmp服务是和http服务是平级,所以我们需要在和http配置平级的位置另起rtmp服务。

vi /usr/local/nginx/conf/nginx.conf #修改配置文件
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;
        }
        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填写流的地址
28e43df99ab982b931d467683a9108dc_up-fe1ac42f16129141d443f2e367d6ae85ff4.png

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模块权限如下。

图片

图片

图片

图片

图片

图片
 
图片


北桥苏
21 声望0 粉丝