首页 > 其他分享 >WebRTC基础使用

WebRTC基础使用

时间:2024-03-20 18:13:46浏览次数:20  
标签:SDP 媒体 获取 基础 pc 使用 const WebRTC

一、什么是WebRTC

WebRTC(Web Real-Time Communication)是一个由Google、Mozilla、Opera等公司发起的开源项目,它支持网页浏览器进行实时音视频对话。它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer-to-Peer)的连接,实现视频流和音频流或者其他任意数据的传输。对于开发者而言,WebRTC提供了一套W3C Javascript API,包括音视频的采集、编解码、网络传输、显示等功能,使得开发者能够快速构建出音视频应用。WebRTC标准在较高层面上涵盖了两种不同的技术:媒体捕获和点对点连接。

二、媒体捕获

媒体捕获设备包括摄像头和麦克风,还包括屏幕捕获设备。可以利用navigator 这个浏览器内置对象身上的API来获取媒体流。

  • navigator.mediaDevices.getUserMedia():获取摄像头和麦克风媒体流
  • navigator.mediaDevices.getDisplayMedia():获取屏幕录制的媒体流

API的具体使用可以参考 MDN。从技术手册可以知道,getUserMedia()方法需要传入一个constraints约束对象作为参数。该参数用来指定请求的媒体类型和相对应的参数,而且必须指定至少一个类型。媒体类型具体可以配置哪些属性,可以使用navigator.mediaDevices.getSupportedConstraints()来获取。

image-20240320171940086

2.1 获取媒体流

// 指定约束对象,获取音频流和视频流,并设置视频流的分辨率为 1280 x 720
const constraints = {
  audio: true,
  video: {
    with: 1280,
    height: 720,
  },
};
// 获取媒体流
const stream = navigator.mediaDevices.getUserMedia(constraints);
console.log(stream);

运行上面的代码,浏览器会询问是否打开摄像头和麦克风,设置为允许就能获取媒体流了。

image-20240320172630472

打开控制台,发现getUserMedia()方法返回的是一个Promise。

image-20240320172754557

2.2 展示画面

将获取到的媒体流数据,绑定到一个video标签的srcObject属性上,就能通过video标签看到视频画面了。

这里有两个地方需要注意:

  • getUserMedia()方法返回的是Promise,所以需要在then方法中去绑定video标签的srcObject属性,或者使用async await的方式去获取Promise的值
  • video标签需要设置autoplay属性,否则视频没办法自动播放
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>webrtc</title>
  <style>
    .play-content {
      width: 300px;
      height: 300px;
    }
  </style>
</head>

<body>
  <video class="play-content" autoplay></video>
  <script src="./webrtc01.js"></script>
</body>

</html>
// 指定约束对象,获取音频流和视频流,并设置视频流的分辨率为 1280 x 720
const constraints = {
  audio: true,
  video: {
    with: 1280,
    height: 720,
  },
};

// 获取媒体流
navigator.mediaDevices.getUserMedia(constraints).then(value => {
  // 将媒体流与video标签绑定
  document.querySelector(".play-content").srcObject = value
})

执行上面的代码,就能看到视频画面了

image-20240320173549039

2.3 捕获屏幕

上面使用了getUserMedia()这个API来获取媒体流。接下来使用getDisplayMedia()这个API来捕获屏幕。使用方法和getUserMedia()一致

// 指定约束对象,获取音频流和视频流,并设置视频流的分辨率为 1280 x 720
const constraints = {
  audio: true,
  video: {
    with: 1280,
    height: 720,
  },
};
// 获取屏幕的媒体流,并与video标签绑定
navigator.mediaDevices.getDisplayMedia(constraints).then((value) => {
  document.querySelector(".play-content").srcObject = value;
});

运行上面的代码,浏览器会让你选择一个要打开的窗口。随便选择一个,就能获取到媒体流了。

image-20240320173811082 image-20240320173910335

三、点对点连接

通过getUserMedia()getDisplayMedia()这两个API已经可以获取到媒体流了,那现在就需要考虑如何将媒体流发送给远端的浏览器。WebRTC要建立点对点的连接,首先有两个关键的问题要解决:

  • 媒体协商:

    媒体协商主要关注通信双方支持的编解码器及其参数。这包括音频参数(如采样率、采样大小、通道数)和视频参数(如分辨率、帧率等)。媒体协商的目的是确保双方能够交换和理解彼此的音视频流。这通常通过Session Description Protocol(SDP)进行。

  • 网络协商:

    网络协商则主要关注在互联网上建立可通信的链路。由于大多数设备没有独立的公网IP,因此需要进行网络地址转换(NAT)。WebRTC使用STUN(Session Traversal Utilities for NAT)和TURN(Traversal Using Relays around NAT)协议来帮助完成这一操作。STUN协议用于发现公共IP地址和UDP端口号,而TURN协议则用于在直接通信不可行时提供中继服务。网络协商的目的是找到一条可靠的网络路径,使得通信双方能够建立连接并进行实时通信。

也就是说,要确保WebRTC成功通信,需要通过媒体协商来确定双方都可用的编解码方式,还需要通过网络协商确定两端可建立正确通信链路的Ip地址。下面是WebRTC建立通信的图解,针对里面的过程进行逐一讲解

1c28bc75134bd14146e8767e86fbd322

3.1 RTCPeerConnection

具体介绍查看MDN

RTCPeerConnection是WebRTC(Web实时通信)API的一部分,它用于在浏览器之间建立点对点(peer-to-peer)连接,以支持实时通信,如音频、视频和数据交换。RTCPeerConnection提供了创建和管理这些连接所需的底层机制。

根据上面的图解,可以知道WebRTC建立通信的流程大致有以下过程:

  1. 客户端1创建RTCPeerConnection对象,并将本地媒体流通过addTrack方法添加到对象身上
  2. 通过createOffer方法创建本地SDP描述,并通过RTCPeerConnection对象的setLocalDescription方法设置本地描述
  3. 将SDP描述信息通过信令服务器转发给客户端2
  4. 客户端2收到发来的SDP描述,创建RTCPeerConnection对象,并通过setRemoteDescription方法设置远端描述
  5. 客户端2通过createAnswer方法创建本地的SDP描述,并通过 setLocalDescription方法设置本地描述
  6. 客户端2将Answer SDP通过信令服务器转发给客户端1
  7. 客户端1收到发来的Answer SDP,通过setRemoteDescription方法设置远端描述

下面根据以上流程,创建一个WebRtcClient类。该类具有绑定本地媒体流、创建offer、创建answer、接收answer等方法。

在获取本地流时,我获取的是屏幕捕获的流,这样方便通过两个不一样的窗口看到效果。

class WebRtcClient {
  // 初始化 RTCPeerConnection 对象
  constructor() {
    this.pc = new RTCPeerConnection();
    this.offer = "";
    this.answer = "";
  }

  // 获取本地媒体流,并绑定到本地 video标签
  async init() {
    const localVideoBox = document.querySelector(".local");
    const remoteVideoBox = document.querySelector(".remote");
    // 设置约束
    const constraints = {
      audio: true,
      video: true,
    };
    // 获取视频流 (切换选择使用摄像头还是屏幕)
    // const stream = await navigator.mediaDevices.getUserMedia(constraints);
    const stream = await navigator.mediaDevices.getDisplayMedia(constraints);
    localVideoBox.srcObject = stream;

    // 将本地媒体流轨道添加到  RTCPeerConnection对象 中
    stream.getTracks().forEach((track) => {
      this.pc.addTrack(track, stream);
    });

    // 监听远程流变化
    this.pc.ontrack = (e) => {
      console.log("远端流变化了", e);
      remoteVideoBox.srcObject = e.streams[0];
    };
  }

  // 创建offer
  async createOffer() {
    // 存在新的候选,需要更新SDP信息
    this.pc.onicecandidate = async (e) => {
      if (e.candidate) {
        this.offer = this.pc.localDescription;
      }
    };
    // 创建offer, 并设置为本地SDP描述
    const offer = await this.pc.createOffer();
    await this.pc.setLocalDescription(offer);
  }

  // 创建answer
  async createAnswer(offer) {
    // 存在新的候选,需要更新SDP信息
    this.pc.onicecandidate = async (e) => {
      if (e.candidate) {
        this.answer = this.pc.localDescription;
      }
    };
    // 收到对端发送的offerSDP,设置为远端SDP
    await this.pc.setRemoteDescription(offer);
    // 创建answer,设置为本地SDP
    const answer = await this.pc.createAnswer();
    await this.pc.setLocalDescription(answer);
  }

  // 添加answerSDP
  async addAnswer(answer) {
    await this.pc.setRemoteDescription(answer);
  }
}

const client = new WebRtcClient();
client.init();
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .local,
    .remote {
      width: 300px;
      height: 300px;
    }
  </style>
</head>

<body>
  <video class="local" autoplay></video>
  <video class="remote" autoplay></video>
  <script src="./webrtcClient.js"></script>
</body>

</html>

下面一步一步操作,实现通信

  1. 打开两个浏览器页面,分别共享不同的画面

    image-20240315180149695

  2. 左边浏览器中调用client对象的createOffer方法

    image-20240315180403818

  3. 复制左边client对象的offerSDP,调用右边client对象的createAnswer方法,传入offerSDP

    直接右键复制,实际应该使用信令服务器进行转发

    image-20240315180641383

    调用右边client对象的createAnswer方法,传入offerSDP

    image-20240315180758176

  4. 复制右边client对象的answerSDP,调用左边client对象的addAnswer方法,并传入answerSDP

    image-20240315181100723

    image-20240315181132629

    然后,两个浏览器页面就可以进行视频通话了

    image-20240315192801153

上面的SDP交换过程,是手动进行的,主要是为了了解整个通信流程。后面可以创建一个信令服务器,通过WebSocket实现SDP的交换。

标签:SDP,媒体,获取,基础,pc,使用,const,WebRTC
From: https://www.cnblogs.com/finish/p/18085795

相关文章

  • 使用iframe内嵌网页的时候,如何做到内嵌网页的高度自适应 有大用
    原文链接:http://shipingzhong.cn/node-admin/9865在页面无刷新更新方面,虽然现在的ajax很强悍,但是处理代码相对多点。想比之下,iframe就简单多了!处理iframe的自适应宽、高,会经常用到,网上整理了一份,写在这里备用:单个iframe高度自适应:<iframe id="iFrame1" name="iFrame1" widt......
  • 卡码java基础课 | 16.出现频率最高的字母
    学习内容:哈希表:数组重点归纳:哈希表:根据关键码key的值而直接进行访问的数据结构。重点是哈希函数(散列函数),是一种对应关系f,根据关键字找到对应存储位置。大致分为3种,数组、set集合、map映射。本节主要学习数组作为哈希表的使用。例题:解:点击查看代码importjava.util.Scan......
  • 深度学习500问——Chapter03:深度学习基础(3)
    文章目录3.5BatchSize3.5.1为什么需要Batchsize3.5.2BatchSize值的选择3.5.3在合理范围内,增大BatchSize有何好处3.5.4盲目增大BatchSize有何坏处3.5.5调节BatchSize对训练效果影响到底如何3.6归一化3.6.1归一化含义3.6.2为什么要归一化3.6.3为什......
  • opengl日记10-opengl使用多个纹理示例
    文章目录环境代码CMakeLists.txt文件内容不变。fragmentShaderSource.fsvertexShaderSource.vsmain.cpp总结环境系统:ubuntu20.04opengl版本:4.6glfw版本:3.3glad版本:4.6cmake版本:3.16.3gcc版本:10.3.0在<opengl学习日记9-opengl使用纹理示例>的基础上,拓展使用多个纹......
  • 使用Pygame做一个乒乓球游戏
    项目介绍使用Pygame做一个乒乓球游戏。左侧为电脑,右侧为玩家。视频地址-YT视频搬运-B站视频教程约90分钟。代码地址环境:需要pygame库,可用pip安装:pipinstallpygame1.基础版本首先进行一些初始化,初始化pygame以及物体的初始状态。然后是主循环,游戏的主循环主要......
  • Chocolatey安装与使用
    目录使用Chocolatey完成一键盘安装Windows环境下的所有程序依赖Chocolatey安装Chocolatey安装前提条件安装要求Chocolatey组成安装ChocolateyChocolatey用法常用命令常用安装包补充search命令install命令update命令更新包卸载安装包uninstall命令如果你不想在命令行中搜索和安装......
  • python基础 1
    #coding:utf-8##号表示单行注释,被注释的代码不会被运行ctrl+/进行注释#python中的输出语句#print("hellodcs38")#print是python当中默认的打印方式name='helloworld'#定义了一个变量name,将=号右边的"helloworld"字符串赋值给到name这个变量##在pyth......
  • python coding with ChatGPT 打卡第23天| 回溯算法:理论基础
    文章目录视频讲解回溯法的效率解决的问题如何理解回溯法回溯框架视频讲解回溯算法理论篇回溯是递归的副产品,只要有递归就会有回溯。回溯法的效率回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法......
  • 使用 Keras 的 Stable Diffusion 实现高性能文生图
    前言在本文中,我们将使用基于KerasCV实现的StableDiffusion模型进行图像生成,这是由stable.ai开发的文本生成图像的多模态模型。StableDiffusion是一种功能强大的开源的文本到图像生成模型。虽然市场上存在多种开源实现可以让用户根据文本提示轻松创建图像,但Keras......
  • 【Python实用教程】使用Python快速查找电脑里的文件
    电脑随着使用时间的增加,我们在电脑中储存的文件变得越来越多。当这个时候你想要查找一个文件,但是又忘记了文件的位置在哪,想通过排序查找这个文件,又由于文件夹里面文件太多,根本找不到。如果使用电脑自带的搜索,时间又很长。那么在面对海量的存储文件,其实我们可以通过其实我......