webrtc 入门第三章 建立连接

这篇具有很好参考价值的文章主要介绍了webrtc 入门第三章 建立连接。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

webrtc 入门第三章 建立连接

一、介绍
1、概述

​ 在前面的章节我们学习了通过webrtc的基本操作实现了获取本地媒体流、音视频的获取与操作。在得到本地的媒体流后我们就需要将本地媒体数据发送到远端,远端街道媒体流后渲染可视化,从而达到通话的目的。

​ RTCPeerConnection 连接的核心pai接口,使用它可以将本地流发送到远端,同时也可以将远端媒体流发送到本地从而实现连接。在使用过程中需要用到信令服务器中转信息和STUN服务器打桩服务。

二、实践
1、RTCPeerConnection 连接对象

1.RTCPeerConnection 后文简称pc连接对象。本地为Local对象,远端为Remote对象,在一对一音视频通话场景中pc对象总是成对出现。

方法名 参数 说明
RTCPeerConnection RTCConfiguration连接配置参数 RTCPeerConnection接口代表一个本地到远端的webrtc连接,这个连接提供了创建,保持,监控,关闭连接的方法实现,在创建时需要向其传入配置参数,及ICE配置信息
pc.createOffer RTCOfferOptions对象 创建提议Offer方法,此方法会返回SDP offer信息,即RTCSessionDescription对象
pc.setLocalDescription RTCSessionDescription 对象 设置本地SDP描述信息
pc.setRemoteDescription RTCSessionDescription 对象 设置远端SDP描述信息,接收到远端发来的SDP信息后使用本方法
pc.createAnswer RTCAnswerOptions 对象,可选 创建应答Answer方法,此方法会返回SDPAnswer信息,即RTCSessionDescription 对象
RTCPIceCandidate wevrtc网络信息,端口,ip等。
pc.addIceCandidate RTCPIceCandidate对象 pc连接添加对方法的IceCandidate对象,得到对放的网络地址等
2.流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u3UNWW3f-1646320648144)(C:\Users\Administrator\Desktop\liucheng.png)]

3、连接流程

WebRTC连接流程比较复杂,学习过程只考虑将本地Peer-A的流发送到远端Peer-B,具体流程如下(A-本地,B-远端)

1.A获取到本地媒体流 MediaStram:通过getUserMedia 方法获取到本地的音视频流数据

2.A生成本地连接对象PC-A:创建一个RTCPeerConnection接口,该接口提供创建、保持、关闭等方法,在设置前需要设置ICE服务器地址

varconfiguration={"iceServers": [{"url": "stun:stun.1.google.com:19302"}]},
that.peerConnA = new RTCPeerConnection(that.configuration)

3.A将本地视频流加入PC-A :

that.localStream.getTracks().forEach((track) => {
    that.peerConnA.addTrack(track, that.localStream)
})

4.A创建提议Offer: that.peerConnA.createOffer() 返回一个RTCPSessionDescription对象,主要是SDP信息是会话的描述信息,两个端连接过程中通过他来进行互相的信息交换,达到媒体协商。

5.A-设置本地描述:创建offer成功后设置本地的描述信息 that.peerConnA.setLocalDescription(event)

6.A将Offer发送给B:通常需要一个信令服务器例如websocket 来转发offer数据

7.B生成PC-B对象:同A端一样,B端也要生成一个RCTPeerConnection对象来应答A端发送的Answer,媒体流等

8.B端设置远端描述:当B端接收到来自A端的offer信息后使用setRemoteDescription() 方法设置来自远端A的描述信息

9.B端生成应答Answer信息:B端使用pc.ceateAnswer()方法生成一个应答A端RTCPSessionDescription对象,主要包括SDP信息,应答Answer和提议Offer是成对出现的。

10.B端设置本地描述:B端创建Answer后设置本地的描述信息 that.peerConnB.setLocalDescription(event)

11.B端返回Answer给A端:通过信令服务器将Answer发送给A端

12.A端设置来自B端的Answer描述信息:当A端通过websocket信令服务获得到的Answer信息后,调用that.peerConnA.setRemoteDescription()方法设置远端的描述

13.交换ICE候选地址信息:建立连接时,会回调onicecandidate事件,传递ice候选地址,同样也需要websocket信令服务来做消息转发,对方接受到以后调用addIceCandidate()方法设置接收到的候选地址

14.交换与使用媒体流:当一方执行addTrack后,另一方的RTCPerrConnection会触发track事件回调,在回调事件中可以获得对方的轨道里的媒体流,这样就能播放对方的流媒体。

that.peerConnB.addEventListener("track", that.getRemoteStream)
getRemoteStream: function (event) {
  let that = this
      if (that.remoteVideo.srcObject !== event.streams[0]) {
             that.remoteVideo.srcObject = event.streams[0]
             this.remoteVideo.play();
             console.log("开始获取远端视频流")
      }
}
4、示例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>RTCPeerConnection 连接测试</title>
</head>
<body>
<div class="app">
    <input type="button" title="开始" value="开始" v-on:click="start"/>
    <input type="button" title="呼叫" value="呼叫" v-on:click="call"/>
    <input type="button" title="挂断" value="挂断" v-on:click="stop"/>
    <input type="button" value="同步" v-on:click="canPlay"/>
    <hr>

    <span>当前使用视频设备:{[videoDeviceName]}</span>
    <br> <br>
    <span>当前使用音频设备:{[audioDeviceName]}</span>
    <hr>

    {{/*    本地视频*/}}
    <video id="localVideo" class="localVideo" height="240px" width="280px" playsinline autoplay muted></video>
    {{/*    远端视频*/}}
    <video id="remoteVideo" class="remoteVideo" height="240px" width="280px" playsinline autoplay muted></video>
    {{/*    本地mp4文件*/}}

    <br> <br>
    <hr>
    <span>测试本地mp4文件</span>

    <br>

    <br>
    <video id="myVideo" class="myVideo" autoplay="autoplay" width="400" height="200" controls loop muted
           v-on:oncanplay="canPlay">
        <source src="/static/file/capture.mp4" type="video/mp4">
    </video>

</div>
</body>
<script src="/static/js/Vue.2.5.3.js"></script>
<script type="text/javascript">
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
    let vm = new Vue({
        el: ".app",
        delimiters: ['{[', ']}'],
        data: {
            // 测试我的视频
            myVideo: null,
            // 本地视频
            localVideo: null,
            // 本地视频流
            localStream: null,
            // 远端视频流
            remoteVideo: null,
            isOpen: false,
            videoDeviceName: "",
            audioDeviceName: "",
            // ICE service地址
            configuration: {
                "iceServers": [{"url": "stun:stun.1.google.com:19302"}]
            },
            // peerConnA 本地对象
            peerConnA: null,
            // peerConnB 远程对象
            peerConnB: null,
            // 本地视频轨道
            videoTracks: [],
            // 本地音频轨道
            audioTrack: []
        },
        methods: {
            canPlay: function () {
                alert("zheli")
                let fps = 0;
                // 捕捉
                if (this.myVideo.captureStream) {
                    this.localStream = this.myVideo.captureStream(fps)
                } else if (this.capture.mozCaptureStream) {
                    this.localStream = this.myVideo.mozCaptureStream(fps)
                } else {
                    alert("不支持 captureStream")
                }

            },
            stop: function () {
                let that = this
                that.peerConnB.close()
                that.peerConnA.close()
                that.peerConnA = null
                that.peerConnB = null
                console.log("关闭会话")
            }
            ,
            start: async function () {
                let that = this;
                if (that.isOpen) {
                    return
                }
                try {
                    that.localStream = await navigator.mediaDevices.getUserMedia({audio: true, video: true})
                    that.isOpen = true
                    that.localVideo.srcObject = that.localStream
                    that.remoteVideo.srcObject = that.localStream
                    console.log("获取本地流成功", that.localStream)
                    // 获取设备轨道
                    that.videoTracks = that.localStream.getVideoTracks()
                    that.audioTrack = that.localStream.getAudioTracks()
                    if (that.videoTracks.length > 0) {
                        that.videoDeviceName = that.videoTracks[0].label
                    }
                    if (that.audioTrack.length > 0) {
                        that.audioDeviceName = that.audioTrack[0].label
                    }

                } catch (e) {
                    console.log("getUserMedia 错误" + e)
                }
            }
            ,
            call: async function () {
                let that = this;
                console.log("开始呼叫")

                //  监听返回icecandidate 信息
                that.peerConnA = new RTCPeerConnection(that.configuration)
                that.peerConnA.addEventListener("icecandidate", that.onIceCandidateA)
                that.peerConnB = new RTCPeerConnection(that.configuration)
                that.peerConnB.addEventListener("icecandidate", that.onIceCandidateB)
                // 监听ICE状态变化
                that.peerConnA.addEventListener("iceconnectionstatechange", that.onIceStateChangeA)
                that.peerConnB.addEventListener("iceconnectionstatechange", that.onIceStateChangeB)

                // 监听track,获取远端视频流视频
                that.peerConnB.addEventListener("track", that.getRemoteStream)
                // 将本地流加入本地连接
                that.localStream.getTracks().forEach((track) => {
                    that.peerConnA.addTrack(track, that.localStream)
                })


                // 创建通话offer
                try {
                    console.log("peerConnA 创建offer会话开始")
                    const offer = await that.peerConnA.createOffer()
                    await that.onCreateOfferSuccess(offer)
                } catch (e) {
                    console.log("创建会话描述SD失败:", e.toString())
                }
            }
            ,

            // 创建提议offer成功
            onCreateOfferSuccess: async function (event) {
                let that = this
                // 设置连接描述
                console.log("peerConnA 创建offer返回得SDP信息", event.sdp)
                console.log("设置peerConnA得本地描述start...")
                try {
                    await that.peerConnA.setLocalDescription(event)
                    console.log("设置peerConnA得本地描述成功")
                } catch (e) {
                    console.log("设置peerConnA得本地描述错误:", e.toString())
                }

                console.log("设置peerConnB得远端描述 start")
                try {
                    await that.peerConnB.setRemoteDescription(event)
                    console.log("设置peerConnB得远端描述成功")

                } catch (e) {
                    console.log("设置peerConnB得远端描述错误:", e.toString())
                }

                // 开始应答
                console.log("peerConnB创建应答 answer start")
                try {
                    const answer = await that.peerConnB.createAnswer()
                    console.log("peerConnB创建应答成功")
                    await that.onCreateAnswerSuccess(answer)
                } catch (e) {
                    console.log("peerConnB创建应答错误:", e.toString())
                }

            }
            ,

            // 创建answer应答成功
            onCreateAnswerSuccess: async function (answer) {
                let that = this
                console.log("peerConnB创建应答answer数据:", answer)
                console.log("peerConnA与peerConnB交换应答answer信息 start")

                try {
                    await that.peerConnB.setLocalDescription(answer)
                    console.log("设置peerConnB得本地answer 应答远端描述成功")

                } catch (e) {
                    console.log("设置peerConnB得本地answer应答描述错误:", e.toString())
                }

                try {
                    await that.peerConnA.setRemoteDescription(answer)
                    console.log("设置peerConnA得远端answer应答描述成功")

                } catch (e) {
                    console.log("设置peerConnA得远端answer应答描述错误:", e.toString())
                }
            }
            ,

            // 获取远端视频
            getRemoteStream: function (event) {
                let that = this
                console.log("获取远端视频数据如下:")
                console.log(event)
                if (that.remoteVideo.srcObject !== event.streams[0]) {
                    that.remoteVideo.srcObject = event.streams[0]
                    this.remoteVideo.play();
                    console.log("开始获取远端视频流")
                }
            }
            ,
            // 监听ICE状态变化事件回调方法
            onIceStateChangeA: function (event) {
                console.log("监听 peerConnA ICE状态", this.peerConnA.iceConnectionState)
                console.log(event)
            }
            ,
            // 监听ICE状态变化事件回调方法
            onIceStateChangeB: async function (event) {
                console.log("监听 peerConnB ICE状态", this.peerConnB.iceConnectionState)
                console.log(event)
            }
            ,

            onIceCandidateA: async function (event) {
                let that = this

                try {
                    if (event.candidate) {
                        // 直接交换candidate数据,就不需要通过信令服务器传送
                        await that.peerConnB.addIceCandidate(event.candidate)
                        console.log("peerConnB IceCandidate----------")
                        console.log(event)
                        that.onAddIceCandidateSuccess(that.peerConnB)
                    }
                } catch (e) {
                    that.onAddIceCandidateError(that.peerConnB, e)
                }
                console.log("onIceCandidateA data:" + event.candidate)
            }
            ,
            onIceCandidateB: async function (event) {
                let that = this
                try {
                    if (event.candidate) {
                        await that.peerConnA.addIceCandidate(event.candidate)
                        console.log("peerConnA IceCandidate----------")
                        console.log(event)
                        that.onAddIceCandidateSuccess(that.peerConnA)
                    }
                } catch (e) {
                    that.onAddIceCandidateError(that.peerConnA, e)
                }
                console.log("onIceCandidateB data:" + event.candidate)
            },
            //
            onAddIceCandidateSuccess: function (pc) {
                console.log("添加" + this.getPcName(pc) + "      IceCandidate 成功")
            },

            onAddIceCandidateError: function (pc, err) {
                console.log("添加" + this.getPcName(pc) + "       IceCandidate 失败" + err.toString())
            },
            getPcName: function (pc) {
                return (pc === this.peerConnA) ? "peerConnA" : "peerConnB"
            },
        }
        ,
        mounted: function () {
            this.localVideo = document.getElementById('localVideo');
            this.remoteVideo = document.getElementById('remoteVideo');
            this.myVideo = document.getElementById('myVideo');
        }
    })
</script>
</html>

webrtc 入门第三章 建立连接

			本地								远端同步           

在上述程序中未使用信令服务器作交换ICE和SDP数据,而是采用本地直接添加的方式,来实现了RTCPeerConnection的连接流程。

其连接过程也和第三段的流程一样,先获取本地的媒体地址,然后发起协商offer,设置本地描述,收到远端的协商offer后设置远端描述,生成会话offer,设置本地描述后发给提议方,提议方收到应答会话offer后,设置远端描述,整个流程结束。

三、总结

本章介绍了webrtc的连接流程即api,在一对一的对接过程中可以直接使用,其连接过程比较复杂也相当繁琐。学者需要先了解其连接原理和流程,然后再去结合代码即可掌握。另外学者们需要掌握一下几点。

1.对于媒体流的操作转换例如:获取视频的尺寸格式,监听远端视频流的变化,音频大小变化,视频清晰度自适应,编码方式。

2.了解提议(offer)/应答(answer)里的信息:这些是SDP信息包含,如分辨率,格式,编码等。

3.了解Candidate信息:这些也是SDP信息,里面包括媒体协商的信息,主要包括服务信息,如中继,打桩,服务器的ip和端口

地址,然后发起协商offer,设置本地描述,收到远端的协商offer后设置远端描述,生成会话offer,设置本地描述后发给提议方,提议方收到应答会话offer后,设置远端描述,整个流程结束。

三、总结

本章介绍了webrtc的连接流程即api,在一对一的对接过程中可以直接使用,其连接过程比较复杂也相当繁琐。学者需要先了解其连接原理和流程,然后再去结合代码即可掌握。另外学者们需要掌握一下几点。

1.对于媒体流的操作转换例如:获取视频的尺寸格式,监听远端视频流的变化,音频大小变化,视频清晰度自适应,编码方式。

2.了解提议(offer)/应答(answer)里的信息:这些是SDP信息包含,如分辨率,格式,编码等。

3.了解Candidate信息:这些也是SDP信息,里面包括媒体协商的信息,主要包括服务信息,如中继,打桩,服务器的ip和端口

4.通过学习视频连接后,可以进行举一反三实现canvas绘画板的同步功能。文章来源地址https://www.toymoban.com/news/detail-409610.html

到了这里,关于webrtc 入门第三章 建立连接的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【UnityShader入门精要学习笔记】第三章(1)Unity Shader介绍

    本系列为作者学习UnityShader入门精要而作的笔记,内容将包括: 书本中句子照抄 + 个人批注 项目源码 一堆新手会犯的错误 潜在的太监断更,有始无终 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 从本章节开始我们要学习Shader相关的知识了,诸位看客可能有的

    2024年02月02日
    浏览(66)
  • 《Jetpack Compose从入门到实战》第三章 定制 UI 视图

    -ui.theme.Color.kt ui.theme.Type.kt 先将Nunito Sans字体家族放入 res/font,再根据设计稿写代码 ui.theme/Shape.kt CompositionLocal 是 Jetpack Compose 中的一种数据传递方式。它可以在组合组件之间传递可变数据,而无需通过 props 或 state 管理器来传递数据。这个特性比传统的数据传递方式更为高效

    2024年02月07日
    浏览(47)
  • 【UnityShader入门精要学习笔记】第三章(2)Unity Shader的形式,章节答疑

    本系列为作者学习UnityShader入门精要而作的笔记,内容将包括: 书本中句子照抄 + 个人批注 项目源码 一堆新手会犯的错误 潜在的太监断更,有始无终 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 (该系列笔记中大多数都会复习前文的知识,特别是前文知识非

    2024年02月02日
    浏览(50)
  • 「第三章」python-docx 添加标题,word标题从入门到精通

    💡 1. add_heading() 简介 💡 2. add_heading() 基本用法 💡 3. 设置不同级别的标题 💡 4. 设置带有特殊字符的标题 💡 5. 使用循环添加多个标题 💡 6. 使用不同样式添加标题 💡 7. 结合其他元素使用标题 💡 8. 为标题设置复杂多变的样式 最近一段时间,一直在更新python关于PDF文档、

    2024年02月02日
    浏览(41)
  • maven从入门到精通 第三章 Maven中形成web对Java工程的依赖

    从来只有war包中含有jar包,而没有jar包中含有war包 web工程依赖的java工程,就是jar包,这个jar包经过自动化部署后,会放在web工程的web-inf/lib目录下 在 pro02-maven-web 工程的 pom.xml 中,找到 dependencies 标签,在 dependencies 标签中做如下配置 证明在Web工程中可以使用Java工程中创建的

    2024年02月02日
    浏览(46)
  • 【第三章 Python 机器学习入门之Series和DataFrame的创建、索引、切片、数据清洗、数据分析等】

    第一章 Python 机器学习入门之Pandas库的使用 第二章 Python 机器学习入门之NumPy库的使用 第四章 Python 机器学习入门之数据可视化 第五章 Python 机器学习入门之机器学习算法 第六章 Python 机器学习入门之实战项目 Series是一种一维数组,可以通过以下方式创建: 通过列表创建Ser

    2024年02月05日
    浏览(57)
  • 第三章 Elasticsearch简介

    Elasticsearch (后称为 ES )是一个天生支持分布式的搜索、聚合分析和存储引擎。 搜索引擎 全文检索引擎 分布式文档系统 分布式数据库 OLAP系统 分布式搜索中间件 不要去死背概念,概念应该作为一种辅助的手段帮助我们去理解一项技术或知识,总之,等你真正会用了,你就

    2024年02月06日
    浏览(41)
  • 第三章-上网行为安全

    1)宽带滥用 2)上网难监管 3)信息泄露 4)网络违法 5)安全威胁 1)上网行为三要素:用户、流量、行为 2)功能需求 (AC的功能)-- 重点 用户认证 应用控制 网页过滤 行为审计 流量管理 应用选路 互联网上网行为管控 一体化网关 无线Wi-Fi管控营销 无线防共享上网 全网上

    2024年01月23日
    浏览(48)
  • 第三章nginx详解

    特点: 1,稳定性高。(没有apache稳定) 2,系统资源消耗地较低。(处理http请求的并发能力非常高,单台物理服务器可以处理30000-50000个并发请求) 稳定:一般在企业中,为了保持服务器的稳定,并发量的设置在20000个左右。占用内存2M左右。 nginx主要功能: 1,静态文件服

    2024年02月12日
    浏览(48)
  • 第三章 decimal模块

    decimal 模块是 Python 提供的用于进行十进制定点和浮点运算的内置模块。使用它可以快速正确地进行十进制定点和浮点数的舍入运算,并且可以控制有效数字的个数。 使用 decimal 模块主要是因为它与 Python 自带的浮点数相比,有以下优点 : 基于浮点模型,提供与数字计算相同

    2024年02月09日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包