Vue+Uniapp realizes live broadcast function (push and pull stream)

hint:


foreword

There is currently a project that requires an APP and a PC-side webpage. The main function is similar to live broadcast. After consulting the information, the technology of uniapp+vue+push and pull streaming can be used. The front-end uses uniapp to open the camera and push the video stream to the back-end. The back-end processes the video stream to form a video in m3u6 or flv format, and the front-end uses flv.


It is divided into two parts in total, one is for uniapp+vue (front end), and the other is for the construction of ngxin live server (back end).

1. Core plug-in

uniapp push stream: (self-labeled) live-pusher push stream

Vue pull stream: flv.js video player

1. uniapp push flow

Official code reference: Realize streaming

live-player | uni-app official website https://uniapp.dcloud.io/component/live-player

  • Create uniapp project
  • In pages–>index–>new live.nvue page

Must be nvue suffix

 In the manifest.json file in uniapp, find the App module configuration in the left menu, and check LivePusher (live streaming push) 

Page code:

<template>
    <view>
        <view class="account-form">
            <view class="uni-form-item">
                <view class="uni-input-wrapper">
                    <view class="uni-label uni-label-must">直播间标题</view>
                    <input class="uni-input" placeholder="请输入直播间标题" :value="form.liveroomTitle" @input="changeInput($event,'liveroomTitle')"/>
                </view>
            </view>
        </view>
        <button class="cu-btn bg-red margin-tb-sm lg" v-if="!startState" @click="saveForm">开始直播</button>
        <button class="cu-btn bg-red margin-tb-sm lg" v-if="startState" @click="downcast">关闭直播</button>
        <button class="cu-btn bg-red margin-tb-sm lg" @click="switchCamera">切换摄像头</button>
        <live-pusher
            id="livePusher"
            ref="livePusher"
            :style="pusherCalss"
            class="livePusher"
            url="rtmp://192.168.1.171:17002/live/huahua"
            mode="FHD"
            :muted="false"
            :enable-camera="true"
            :auto-focus="true"
            :beauty="1"
            whiteness="2"
            aspect="9:16"
            @statechange="statechange"
            @netstatus="netstatus"
            @error="error"
        ></live-pusher>
        // 官方给的一些按钮,具体调用在下面
        //<button class="btn" @click="start">开始推流</button>
        //<button class="btn" @click="pause">暂停推流</button>
        //<button class="btn" @click="resume">resume</button>
        //<button class="btn" @click="stop">停止推流</button>
        //<button class="btn" @click="snapshot">快照</button>
        //<button class="btn" @click="startPreview">开启摄像头预览</button>
        //<button class="btn" @click="stopPreview">关闭摄像头预览</button>
        //<button class="btn" @click="switchCamera">切换摄像头</button> -->
    </view>
</template>

<script>
export default {
    data() {
        return {
            // 视频宽高
            pusherCalss: {
                width: '200px',
                height: '300px'
            },
            // form本人测试请求的参数,不发请求可以不加
            form: {
                liveroomTitle: null, // 标题
                equipmentType: 1 // 设备类型(1.手机 2.电脑)
            },
            // 控制开启,关闭直播按钮的显示
            startState: false,// 直播状态(false 关闭)
        };
    },
    onReady() {
        // 注意:需要在onReady中 或 onLoad 延时
        this.context = uni.createLivePusherContext('livePusher', this);
    },
    onLoad() {
        // 获取可视区域高度,减去固定高度
        this.pusherCalss.width = wx.getSystemInfoSync().windowWidth;
        this.pusherCalss.height = wx.getSystemInfoSync().windowHeight;
    },
    mounted() {
        // 一进页面,先调用摄像头,保证摄像头是打开状态,不加也可以,手动开启,参考上面官方给出的那些按钮
        this.startPreview();
    },
    methods: {
        // 下面的这些方法,可以参考官网,具体查看每个方法的意义
        statechange(e) {
            console.log('statechange:' + JSON.stringify(e));
        },
        netstatus(e) {
            console.log('netstatus:' + JSON.stringify(e));
        },
        error(e) {
            console.log('error:' + JSON.stringify(e));
        },
        start: function() {
            this.context.start({
                success: a => {
                    this.startState = true;
                    console.log('livePusher.start:' + JSON.stringify(a));
                }
            });
        },
        close: function() {
            this.context.close({
                success: a => {
                    console.log('livePusher.close:' + JSON.stringify(a));
                }
            });
        },
        snapshot: function() {
            this.context.snapshot({
                success: e => {
                    console.log(JSON.stringify(e));
                }
            });
        },
        resume: function() {
            this.context.resume({
                success: a => {
                    console.log('livePusher.resume:' + JSON.stringify(a));
                }
            });
        },
        pause: function() {
            this.context.pause({
                success: a => {
                    console.log('livePusher.pause:' + JSON.stringify(a));
                }
            });
        },
        stop: function() {
            this.context.stop({
                success: a => {
                    this.startState = false;
                    console.log(JSON.stringify(a));
                }
            });
        },
        switchCamera: function() {
            this.context.switchCamera({
                success: a => {
                    console.log('livePusher.switchCamera:' + JSON.stringify(a));
                }
            });
        },
        startPreview: function() {
            this.context.startPreview({
                success: a => {
                    console.log('livePusher.startPreview:' + JSON.stringify(a));
                }
            });
        },
        stopPreview: function() {
            this.context.stopPreview({
                success: a => {
                    console.log('livePusher.stopPreview:' + JSON.stringify(a));
                }
            });
        },
        // 输入框改变(没有接口可以忽略,这里就是发送请求时起一个直播间的名字)
        changeInput: function(e, name) {
            this.form[name] = e.detail.value;
        },
        // 开始直播
        saveForm() {
            if(this.form.liveroomTitle == null || this.form.liveroomTitle == ""){
                return;
            }
            // 这里是我自己的测试请求,因为需要和vue数据同步,做一个开播关播的数据交互,大家如果用不到的话可以直接调用 this.start();
            getApp().$util.requestUrl(`/test/live/createLiveRecord?anchorId=2&liveroomTitle=${this.form.liveroomTitle}&equipmentType=${this.form.equipmentType}`).then(res => {
                console.log(res)
                if (res.status == 200) {
                    this.start();
                }
            });
        },
        // 关闭直播
        downcast(){
            // 同样,用不到接口只简单测试,调用this.stop();
            // vue的工具类等调用是this.$util;
            // uniapp使用getApp().$util
            getApp().$util.requestUrl(`/test/live/downcast?anchorId=2`).then(res => {
                console.log(res)
                if (res.status == 200) {
                    this.stop();
                }
            });
        }
    }
};
</script>

2. Vue pull flow

There are two ways to choose the video player: (file format m3u8/flv)

video.js (vlc player is a pc application, it may have local decoding function, general js player does not have decoding function)

flv.js (Generally, the video stream format h264 can be played normally on the flv.js player)--- recommended

Question 1: Cross-domain (the front-end configuration is invalid, and the back-end configuration agent is required. The front-end configuration agent is self-deceiving. When the deployment goes online, the front-end agent has no effect, and the back-end needs to be done)

Question 2: Return 304 status code (the video stream address is a static fixed address, and it will return 200 after receiving it for the first time, but the browser will automatically cache it. If the address of the second request remains unchanged, and the obtained data is always cached before, it will return 304. It should be set to a dynamic address. If you only use a static fixed address for testing, it does not matter if you report 304)

insert image description here


Summarize

Thank you for the articles below!

[Dry Articles] Vue+UniApp Realizes Live Streaming_Battered Code Farmer's Blog-CSDN Blog_uniapp Live Streaming uniapp( push stream)+vue(pull stream)+nginx+OBS Realize Live Streaming 1. First look at the effect of vue pulling stream uniapp push stream https://blog.csdn.net/weixin_43848873/article/details/118968726 Handsome_Mr_Wang's blog-CSDN blog_nginx pulls stream rtmp for hls [nginx streaming media server] nginx+vue uses rtmp to realize hls live broadcasting project which needs to use live broadcasting technology. After reading some articles on the Internet, the test is finally successful. Let me record it here . This article is borrowed from https://blog.csdn.net/Ricardo18/article/details/89359623. I am not going to go into details about the introduction of RTMP, HLS, and HTTP-FLV. The referenced articles are very detailed. Because I actually deployed and tested under win10 (development environment), so I will record how to deploy under windows NginxNginx download address I downloaded n https://blog.csdn.net/weixin_40758229/article/details/118968546

Guess you like

Origin blog.csdn.net/weixin_51258044/article/details/123884057