vue下实现WebRTC

摘要:
参考定义:谷歌的开放实时通信框架在上一篇博客Vue+WebSocket+WaveSurferJS中使用了WebRTC接口,以实现H5聊天对话交互。它使用getUserMedia方法通过浏览器获取设备麦克风,从而收集音频。

1.1 WebRTC简介

WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的实时通信框架,提供了一系列页面可调用API。

参考定义: 谷歌开放实时通信框架

在上一篇博客Vue +WebSocket + WaveSurferJS 实现H5聊天对话交互 中,已经涉及到WebRTC接口的使用,使用到了getUserMedia方法,用于通过浏览器获取设备麦克风,从而采集音频。

最近项目中的需求则是与服务端建立即时通信,实现低延迟音视频直播。

RTC的特征是(参考来源:https://www.zhihu.com/question/22301898)

  • 复杂度较高
  • 半可靠传输,对于特定情境(比如网络环境较差时)可以对音视频进行有损传输,降低延迟
  • 音视频友好:可针对音视频做定制化优化
  • 提供端对端优化方案。 对于传统连接模式,使用C/S架构,A=>服务端=>B,而WebRTC使用的是peer-to-peer模式,A=>B,一旦点和点之间的连接形成,它们之间的数据传输是不经过服务端的,大大降低了服务端的压力。
  • 理论延迟较低,能应用在各种低延迟场景。
2. 业务描述

功能描述
实现对摄像设备的管理列表,在设备列表点击查看视频时,弹出页面浮窗,进行摄像机摄像的视频和音频实时转播。
视频弹窗下方有自己实现的控制条,实现播放/暂停控制,能显示播放时间、切换分辨率、是否全屏等。

效果如图
视频浮窗 - hover状态,显示控制条

视频浮窗 - 非hover状态,隐藏控制条

3. 代码实现

3.1 Html模板代码

<el-dialog ref="videoDialog" title="视频播放" :visible.sync="dialogShowed" :close-on-click-modal="false">
        <div id="dialog-wrap">
            <div id="video-wrap" v-if="isSuccess" v-loading="isLoading" element-loading-text="视频加载中" element-loading-spinner="el-icon-loading"
                element-loading-background="rgba(0, 0, 0, 0.8)" />
            <div class="video-onloading" v-else v-loading="isLoading" element-loading-text="视频加载中" element-loading-spinner="el-icon-loading"
                element-loading-background="rgba(0, 0, 0, 0.8)">
                <span><i class="el-icon-error" v-if="!isLoading" />{{errorMessage}}</span>
            </div>
            <!-- 遮罩层 -->
            <div class="cover" v-if="isSuccess">
                <div class="controls">
                  
                    <i class="el-icon-video-play" v-if="!isPlaying" @click="playOrPauseVideo" />
                    <i class="el-icon-video-pause" v-else @click="playOrPauseVideo" />
                    <div id="currentTime">播放时长:{{currentTime}}</div>
                    <div class="control-resolution">
                        分辨率:
                        <el-select v-model="selectResolution" @change="changeResolution">
                            <el-option v-for="item in resolutions" :key="item" :value="item">
                                {{item}}
                            </el-option>
                        </el-select>
                    </div>
                    <i class="el-icon-full-screen" @click="onClickFullScreen"></i>
                </div>
            </div>
        </div>
    </el-dialog>

  • 使用了Element-UI框架提供的v-loading指令,该指令根据isLoading属性决定是否在区域内加载loading动画
    视频加载状态

  • 若视频加载失败,则显示错误信息
    显示错误信息

  • 预留标签,用于挂载`video和audio DOM元素
    <div ></div>
    注意该标签内最好不要再加其他元素,这样后续判断比较简单。

3.2 建立连接、接收音频

       getVideo() {
                let that = this;
                that.isLoading = true;
                that.pc = new RTCPeerConnection();
                that.pc.addTransceiver("video");
                that.pc.addTransceiver("audio");
                that.pc.ontrack = function (event) {
                    var el = document.createElement(event.track.kind);
                    el.srcObject = event.streams[0];
                    el.autoplay = true;
                    document.getElementById("video-wrap").appendChild(el);
                    if (el.nodeName === "VIDEO") {
                        el.oncanplay = () => {
                            that.isLoading = false;
                            // 播放状态设置为true
                            that.isPlaying = true;
                            that.getVideoDuration();
                        };
                    } else if (el.nodeName === "AUDIO") {
                        el.oncanplay = () => {
   
                        };
                    }
                };
                that.pc
                    .createOffer()
                    .then((offer) => {
                        that.pc.setLocalDescription(offer);
                        let req = {
                            webrtc: offer,
                        };
                        console.log(offer);
                        return that.$api.device.getSignaling(
                            that.deviceData.id,
                            that.origin,
                            that.selectResolution,
                            req
                        );
                    })
                    .then((res) => {
                        if (res.code === 0) {
                            that.isSuccess = true;
                            that.pc.setRemoteDescription(res.body.webrtc);
                            that.connId = res.body.connId;
                        } else {
                        
                            that.errorMessage = res.message || "视频加载错误";
                        }
                    })
                    .catch(alert);
            }

参考https://www.jianshu.com/p/43957ee18f1a,查看Peer Connection建立连接的流程。
参考 https://developer.mozilla.org/zh-CN/docs/Web/API/RTCPeerConnection 查看RTCPeerConnection 支持的接口

createOffer() 方法: 主动与其他peer建立P2P连接,把自己的SDP信息整理好,通过signaling server转发给其他peer。
在上面的代码中,通过向后端发送POST请求,实现信令交换。

 that.pc.addTransceiver("video");
 that.pc.addTransceiver("audio");

指明同时接收音频和视频。

 that.pc.ontrack = function(event){
}

该方法进行音视频的接收,使用接收到的数据创建video和audio元素。
只对pc状态进行监听无法监听到实际视频可以播放的状态,因此需要对video添加监听方法:

  el.oncanplay = () => {
     that.isLoading = false;
     // 播放状态设置为true
    that.isPlaying = true;
    that.getVideoDuration();
};

在video可以播放时,才将loading状态取消,并开始获取video时长。

3.3 控制音视频的JS代码

获取视频播放时长方法:

getVideoDuration() {
    var video = document.getElementsByTagName("video")[0];
    //  如果没有获取到视频元素
    if (!video) {
        return;
    }
    let that = this;

    video.addEventListener("timeupdate", () => {
        that.currentTime = getTime(video.currentTime);
    });

    var getTime = function (time) {
        let hour =
            Math.floor(time / 3600) < 10
                ? "0" + Math.floor(time / 3600)
                : Math.floor(time / 3600);
        let min =
            Math.floor((time % 3600) / 60) < 10
                ? "0" + Math.floor((time % 3600) / 60)
                : Math.floor((time % 3600) / 60);
        var sec =
            Math.floor(time % 60) < 10
                ? "0" + Math.floor(time % 60)
                : Math.floor(time % 60);
        return hour + ":" + min + ":" + sec;
    };
}

控制音频/视频同步暂停的方法:

  playOrPauseVideo() {
    var video = document.getElementsByTagName("video")[0];
    var audio = document.getElementsByTagName("audio")[0];
    if (this.isPlaying) {
        video.pause();
        audio.pause();
    } else {
        // audio
        video.play();
        audio.play();
    }
    this.isPlaying = !this.isPlaying;
}

全屏方法

onClickFullScreen() {
    let dialogElement = document.getElementById("dialog-wrap");
    dialogElement.webkitRequestFullScreen();
}

3.4 样式表

样式部分较为简单,值得注意的有以下几点:

  • 隐藏原有视频控制条,便于对控制条进行自定义
video::-webkit-media-controls {
    /* 去掉全屏时显示的自带控制条 */
    display: none !important;
}
  • 扩大hover热区,视频下半部分(高度为400px部分)悬浮显示控制条
    (不设置为全部部分是因为如果设置为全部部分,则全屏状态无法隐藏控制条)
    以下完整样式表(scss):
    $controlFontColor: rgb(136 141 150);
    $backgroundColor: rgba(0, 0, 0, 0.8);
    $height: 60px;

    .el-dialog .el-dialog__body {
        padding: 0 !important;
        margin-bottom: 0 !important;
         unset !important;
    }

    .video-onloading {
        min-height: 500px;
        background-color: $backgroundColor;

        span {
             100%;
            display: block;

            line-height: 500px;
            text-align: center;
            color: $controlFontColor;
            i {
                margin-right: 5px;
            }

            i::before {
                font-size: 17px;
            }
        }
    }

  .cover {
        bottom: 0px;
        height: 300px;
        position: absolute;
         100%;
        z-index: 2;
        &:hover,
        &:focus,
        &:focus-within {
            .controls {
                display: flex;
            }
        }
    }
  .controls {
         100%;
        height: $height;
        line-height: $height;
        font-size: 15px;
        display: none;
        z-index: 2;
        background-color: $backgroundColor;
        color: $controlFontColor;
        position: absolute;
        bottom: 0
        justify-content: space-between;

        & > [class^="el-icon-"] {
            &::before {
                font-size: 26px;
                line-height: $height;
                padding: 0 15px;
                cursor: pointer;
            }
        }

        .playStatus {
             64px;
            height: $height;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
        #currentTime {
             140px;
            height: $height;
            text-align: center;
        }

        .control-resolution {
            line-height: $height;
            .el-input__inner {
                background: $backgroundColor;
            }
            .el-input {
                 95px;
            }
            input {
                border: none;
                font-size: 15px !important;
                color: $controlFontColor;
                &::-webkit-input-placeholder {
                    color: $controlFontColor;
                }
            }
        }
        #fullScreen {
             32px;
            height: 32px;
            position: relative;
            top: 16px;
         
        }
    }

总结

本次的前端业务WebRTC只做了浅显的了解和应用,只应用了接收流,还没有用到推流,WebRTC还有更多用法,比如实现实时视频通话、语音通话等,也许以后的业务中会用到,所以以这篇博客做一个入门记录~

 
 转自:https://www.cnblogs.com/ygria/p/13995793.html

免责声明:文章转载自《vue下实现WebRTC》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇java poi 设置边框STM32学习笔记——AFIO时钟的配置问题下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

vue2 设置网页title的问题

好东东,没个标题看着多难受 看到1文章  http://blog.csdn.net/qishuixian/article/details/72912368 推荐使用 vue-wechat-title插件 npm install vue-wechat-title --save  下 看后我的使用方式 1. 在 main.js 中 import VueWech...

vue针对搜索引擎做SEO优化

先把几个方法放出来: 1.SSR服务器渲染;2.静态化;3.预渲染prerender-spa-plugin;4.使用Phantomjs针对爬虫做处理。 首先复习一下seo: 搜索引擎优化(Search engine optimization,简称seo),指为了提升网页在搜索引擎自然搜索结果中(非商业性推广结果)的收录数量以及排序位置而做的优化行为,是为了...

vue项目中安装cnpm和node_modules

1.安装cnpm的nodejs包管理工具,命令行: npm install -g cnpm --registry=https://registry.npm.taobao.org   2. 每个vue项目需要配置自己单独的node_modules模块,具体构建过程如下: step1: cd到你的项目根目录下 step1: 执行 “cnpm insta...

Vue 开发规范目录及说明

Vue 开发规范目录及说明 Vue 开发规范目录及说明 命名规范 普通变量命名规范 常量 组件命名规范 views 下的文件命名 结构化规范 目录文件夹及子文件规范 vue 文件基本结构 多个特性的元素规范 元素特性的顺序 组件选项顺序 为组件样式设置作用域 注释规范 务必添加注释列表 单行注释 多行注释 模块 指令规范 Props 规...

vue 手指长按触发事件

按钮 <span @touchstart="gtouchstart()" @touchmove="gtouchmove()" @touchend="gtouchend()">按住说话</span> data数据定义一个定时器 timeOutEvent:0,//定时器 方法         gtouchstart(){...

WebRTC通信流程

WebRTC是HTML5支持的重要特性之一,有了它,不再需要借助音视频相关的客户端,直接通过浏览器的Web页面就可以实现音视频对聊功能。而且WebRTC项目是开源的,我们可以借助WebRTC源码快速构建自己的音视频对聊功能。无论是使用前端JS的WebRTC API接口,还是在WebRTC源码上构建自己的对聊框架,都需要遵循以下执行流程: 上述序列中,We...