从零开始搭建直播系统01——如何通过浏览器访问摄像头

许多JavaScript开发者主要认为JavaScript是一种控制网页的工具。可能很难想象将JavaScript用于音频和视频处理。你可能会想,JavaScript或浏览器的性能是否能跟上。

然而,谷歌并不这么想。谷歌的目标是实现普通人无法想象或理解的东西;否则,它就不是谷歌了。

“浏览器+WebRTC “是谷歌提供的答案。2011年,谷歌成立了WebRTC项目,愿景是实现浏览器之间的快速音频和视频通信。

随着WebRTC 1.0规范的发布,Chrome、Firefox、Safari和Edge等主流浏览器现在支持WebRTC库。换句话说,这些浏览器之间的实时音频和视频通信已经相当成熟。

接下来,我将演示通过现代浏览器使用JavaScript/浏览器访问电脑上的音频和视频设备是多么简单。

WebRTC的处理流程

在解释如何通过浏览器收集音视频数据之前,让我介绍一下WebRTC实现一对一实时音视频通话的整个过程。了解这个过程可以帮助你清楚地知道本文的定位和整个过程中所学习的知识点。

从零开始搭建直播系统01——如何通过浏览器访问摄像头
WebRTC 1对1音视频实时通话流程示意图

上图展示了使用WebRTC进行一对一音视频实时通信的整个过程。通过这张图可以看出,实现一对一音视频实时通信的过程是相当复杂的。

该图大致可以分为 4 个部分:两个 WebRTC 端点(图中的两个大矩形)、一个 Signal(信令)服务器一个 STUN/TURN 服务器

  • WebRTC端点负责音视频采集、编解码、NAT穿越、音视频数据传输。
  • 信令服务器处理信号处理,例如加入房间、离开房间和传递媒体协商消息。
  • STUN/TURN 服务器负责获取 WebRTC 端点的公共 IP 地址,并在 NAT 穿越失败时中继数据。

接下来我将描述WebRTC音视频通话的大致流程

在一端(WebRTC 端点)进入房间之前,它首先检查其设备是否可用。如果设备可用,它将继续进行音频和视频数据收集,这是本文的主要重点。

采集到的数据可以用来预览,让用户看到自己的视频,也可以记录下来保存为文件。视频通话结束后,可以将文件上传到服务器,供用户查看之前的内容。

音频和视频数据准备就绪后,WebRTC 端点会向信令服务器发送“加入”信号。信令服务器随后会在收到此消息后创建一个房间。在另一端,采取类似的步骤,但不是创建房间,而是加入现有房间。当第二个端点成功加入房间时,第一个用户会收到一条消息,表明“另一个用户已成功加入”。

此时,第一端创建一个“媒体连接”对象,即RTCPeerConnection(后面会详细解释),通过RTCPeerConnection对象对采集到的音视频数据进行编码,最终通过P2P方式发送给另一端。

当然,P2P穿越也有可能失败。在这种情况下,为了保证端点之间仍然可以交换音频和视频数据,TURN服务器(将在后面的文章中介绍)用于中继音频和视频数据。

在克服各种挑战到达目的地后,接收到的音频和视频数据在显示之前由另一端解码。这样就完成了从一端到另一端的单向通信。对于双方之间的双向通信,每一方都需要通过 RTCPeerConnection 对象传输自己的数据,同时相互接收数据。

总之,这就是这张图大致描述的内容。本文的重点是 WebRTC 端点内的音频和视频采集部分。

音视频采集的基本概念

在介绍用于采集音视频数据的JavaScript API 之前,你需要了解一些基本概念。这些概念虽然不难理解,但是后面讲解API的时候会用到,非常重要。因此,我将在这里总结并强调它们:

  • 相机:用于捕捉(采集)图像和视频。
  • 帧率:现代相机非常强大;通常,它们每秒可以捕捉 30 幅或更多图像。一些好的相机甚至可以拍摄 100 多张图像。摄像机每秒拍摄的图像数量称为帧率。更高的帧速率会产生更流畅的视频,但会消耗更多的网络带宽。
  • 分辨率:除了帧速率之外,相机还可以调整其分辨率设置。常见的分辨率包括 2K、1080P、720P 和 420P。更高的分辨率会产生更清晰的图像,但也会消耗更多的带宽。
  • 纵横比:分辨率一般有两种纵横比:16:9 或 4:3。4:3 的宽高比源自黑白电视,而 16:9 的宽高比则来自显示器。如今,标准通常是 16:9 的比例。
  • 麦克风:用于收集音频数据。和视频一样,它可以指定每秒的样本数(采样率)。每个样本由一定数量的比特(采样深度或大小)表示。
  • 轨道:WebRTC 的“轨道”概念借鉴了多媒体术语。火车轨道的特点是它们永远不会相交;同样,在多媒体方面,每个轨道的数据都是独立的,不会与其他轨道相交(例如,MP4 文件中的音频和视频轨道是分开存储的)。
  • Stream:可以理解为一个容器。在WebRTC中,“流”可以分为媒体流(MediaStream)和数据流(DataStream)。媒体流可以存储零个或多个音频或视频轨道,而数据流可以存储零个或多个数据轨道。

音频和视频采集

有了上面的基本概念,你就可以很容易地理解下面的内容了。下面我们就来看看浏览器采集音视频的API格式,以及如何控制音视频采集。

1. getUserMedia 方法

const promise = navigator.mediaDevices.getUserMedia(constraints);

getUserMedia API 返回一个Promise对象。

  • 如果getUserMedia调用成功,你可以通过Promise获取到一个MediaStream对象,也就是说你已经获取了设备的音视频数据。
  • 如果调用失败,例如,如果用户拒绝访问媒体设备(音频或视频)或请求的媒体设备不可用,则返回的 Promise 将导致错误对象,如 PermissionDeniedError 或 NotFoundError。

2. MediaStreamConstraints 参数

从上面的调用格式可以看出,该getUserMedia方法有一个名为 constraints 的输入参数,其类型为MediaStreamConstraints它指定了MediaStream中应该包含哪些类型的媒体轨道(音轨、视频轨道),并为这些媒体轨道设置了一些约束条件。现在我们来仔细看看它包含的约束条件。在这里,我将引用MediaStreamConstraints的 WebRTC 1.0 规范,其格式如下:

dictionary MediaStreamConstraints { 
(boolean or MediaTrackConstraints) video = false ,
(boolean or MediaTrackConstraints) audio = false
};

constraints对象可以有两个属性:audiovideo。每个属性可以是一个布尔值,也可以是针对相应媒体轨道的一组特定约束。如果属性设置为true,则表示相应的轨道应包含在流中而没有任何特定限制。如果它是具有特定约束的对象,则这些约束将应用于相应的轨道。

const mediaStreamContrains = { 
video : true ,
audio : true
};

实际上,你可以使用 MediaTrackConstraints 进一步限制每个媒体轨道。例子:

const constraints = { 
audio : {
echoCancellation : true ,
noiseSuppression : true
},
video : {
width : { ideal : 1280 },
height : { ideal : 720 },
frameRate : { ideal : 30 }
}
};

在此示例中,该audio属性对回声消除和噪声抑制具有特定约束。该video属性指定宽度、高度和帧速率的理想值。浏览器在获取媒体流时会尽量匹配这些设置。

还有许多其他属性可用于音频和视频轨道。可以在MDN Web 文档中找到有关这些属性的更多信息。

使用getUserMediaAPI

为了快速看到效果,我们使用Next.js,使用Vercel部署,可以按照以下步骤操作:

1.新建一个Next.js项目:

pnpm create-next-app my-webcam-app 

cd my-webcam-app
{
  "name": "webrtc-examples",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@types/node": "20.1.4",
    "@types/react": "18.2.6",
    "@types/react-dom": "18.2.4",
    "autoprefixer": "10.4.14",
    "eslint": "8.40.0",
    "eslint-config-next": "13.4.2",
    "next": "13.4.2",
    "postcss": "8.4.23",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "tailwindcss": "3.3.2",
    "typescript": "5.0.4"
  }
}

2. 创建并编辑app/getUserMedia/page.tsx文件以包含以下代码:

"use client";
import { useEffect, useState, useRef } from "react";

const GetUserMediaPage = () => {
  const [errorMsg, setErrorMsg] = useState("");
  const videoElRef = useRef<null | HTMLVideoElement>(null);
  const initialize = () => {
    const mediaStreamContrains: MediaStreamConstraints = {
      video: true,
      audio: true,
    };

    function gotLocalMediaStream(mediaStream: MediaProvider) {
      if (videoElRef.current) {
        videoElRef.current.srcObject = mediaStream;
      }
    }

    function handleLocalMediaStreamError(error: Error) {
      console.log("navigator.getUserMedia error: ", error);
      setErrorMsg(error.message);
    }

    navigator.mediaDevices
      .getUserMedia(mediaStreamContrains)
      .then(gotLocalMediaStream)
      .catch(handleLocalMediaStreamError);
  };

  useEffect(() => {
    initialize();
  }, []);

  return (
    <div className="px-4">
      <h1 className="py-10 text-center">Realtime communication with WebRTC</h1>
      <video className="m-auto" ref={videoElRef} autoPlay playsInline></video>
      {errorMsg && <p>connected failed: {errorMsg}</p>}
    </div>
  );
};

export default GetUserMediaPage;

3.启动开发服务器:

pnpm dev

4. 打开浏览器并访问http://localhost:3000以查看您的网络摄像头流。

5. 按照以下步骤将你的应用程序部署到 Vercel:

  • 安装 Vercel CLI:
pnpm i -g vercel
  • 登录或注册一个 Vercel 帐户
vercel login
  • 部署应用
vercel --prod

部署后,你将收到一个生产URL,你可以在那里查看你的实时网络摄像头流。

以上 github 代码:https://github.com/aiden-art/webRTC-examples/blob/main/src/app/getUserMedia/page.tsx

通过浏览器访问摄像头到此结束。你可以使用上面提供的Vercel URL (选择索引 1)查看效果。如果对代码有疑问,可以fork项目了解实现细节。

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/yinshipin/27511.html

(0)

相关推荐

发表回复

登录后才能评论