Skip to content

浏览器调用输入输出接口

navigator.mediaDevices可以通过浏览器调用媒体设备,例如输入设备:麦克风、摄像头,输出设备:显示器等。其中getUserMedia调用输入设备的接口,getDisplayMedia调用输出接口(开启屏幕共享)。

例子

实现一个录屏并上传的功能。

html

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      http-equiv="X-UA-Compatible"
      content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button onclick="getUserDevices()">获取摄像头和麦克风</button>
    <button onclick="getDisplayDevices()">获取显示器和扬声器权限</button>
    <video></video>
    <script>
      ....查看js部分
    </script>
  </body>
</html>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      http-equiv="X-UA-Compatible"
      content="IE=edge" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button onclick="getUserDevices()">获取摄像头和麦克风</button>
    <button onclick="getDisplayDevices()">获取显示器和扬声器权限</button>
    <video></video>
    <script>
      ....查看js部分
    </script>
  </body>
</html>

js

js
const video = document.querySelector("video");

// getUserMedia获取输入设备
async function getUserDevices() {
  if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
    console.log(await navigator.mediaDevices.getSupportedConstraints());
    try {
      const data = await navigator.mediaDevices.getUserMedia({
        // 获取输入设备-麦克风 的调用权限
        audio: true,
        // 获取输入设备-摄像头 的调用权限
        video: true,
      });
      video.srcObject = data;
      video.onloadedmetadata = () => {
        video.play();
      };
    } catch (error) {
      console.log("拒绝了调用请求.");
    }
  } else {
    Promise.race("浏览器不支持调用设备接口!");
  }
}

// getDisplayMedia获取输出设备
async function getDisplayDevices() {
  if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
    try {
      const data = await navigator.mediaDevices.getDisplayMedia({
        // 获取输出设备-扬声器 的调用权限
        audio: true,
        // 获取输出设备-显示器 的调用权限
        video: true,
      });
      video.srcObject = data;
      video.onloadedmetadata = () => {
        video.play();
      };
    } catch (error) {
      console.log("拒绝了调用请求.");
    }
  } else {
    Promise.race("浏览器不支持调用设备接口!");
  }
}
const video = document.querySelector("video");

// getUserMedia获取输入设备
async function getUserDevices() {
  if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
    console.log(await navigator.mediaDevices.getSupportedConstraints());
    try {
      const data = await navigator.mediaDevices.getUserMedia({
        // 获取输入设备-麦克风 的调用权限
        audio: true,
        // 获取输入设备-摄像头 的调用权限
        video: true,
      });
      video.srcObject = data;
      video.onloadedmetadata = () => {
        video.play();
      };
    } catch (error) {
      console.log("拒绝了调用请求.");
    }
  } else {
    Promise.race("浏览器不支持调用设备接口!");
  }
}

// getDisplayMedia获取输出设备
async function getDisplayDevices() {
  if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
    try {
      const data = await navigator.mediaDevices.getDisplayMedia({
        // 获取输出设备-扬声器 的调用权限
        audio: true,
        // 获取输出设备-显示器 的调用权限
        video: true,
      });
      video.srcObject = data;
      video.onloadedmetadata = () => {
        video.play();
      };
    } catch (error) {
      console.log("拒绝了调用请求.");
    }
  } else {
    Promise.race("浏览器不支持调用设备接口!");
  }
}

将文件流合并成二进制文件

要将 srcObject 的媒体流数据上传到服务端并保存为视频文件,你需要先将媒体流数据转换为视频文件的格式,然后将该文件上传到服务器。以下是一般的步骤:

  1. 在客户端,使用 MediaRecorder API 将 srcObject 的媒体流数据录制为视频文件。MediaRecorder 提供了对媒体流进行录制的功能。你可以设置录制的输出格式和编解码器等参数。

    示例代码如下:

js
// srcObject为源数据,也就是音频
const mediaRecorder = new MediaRecorder(srcObject);
// 保存的数据片段
const chunks = [];
// 有点像可写流一样,每次有数据流过我们只需要保存好数据即可。
mediaRecorder.ondataavailable = (event) => {
  // 每次有数据流过就保存数据片段
  chunks.push(event.data);
};

// 停止录屏时,将
mediaRecorder.onstop = () => {
  const blob = new Blob(chunks, { type: "video/webm" });
  // 将 blob 上传到服务器或进行其他操作
};

mediaRecorder.start();
// srcObject为源数据,也就是音频
const mediaRecorder = new MediaRecorder(srcObject);
// 保存的数据片段
const chunks = [];
// 有点像可写流一样,每次有数据流过我们只需要保存好数据即可。
mediaRecorder.ondataavailable = (event) => {
  // 每次有数据流过就保存数据片段
  chunks.push(event.data);
};

// 停止录屏时,将
mediaRecorder.onstop = () => {
  const blob = new Blob(chunks, { type: "video/webm" });
  // 将 blob 上传到服务器或进行其他操作
};

mediaRecorder.start();

WARNING

一定要调用 mediaRecorder.start(),否则 ondataavailable 事件不会触发!!

录制一段时间后调用 mediaRecorder.stop() 停止录制并触发 onstop 事件

  1. 将录制的媒体数据存储为一个 Blob 对象。通过在 ondataavailable 事件中收集录制的数据块,最后将它们合并为一个 Blob 对象。

  2. 通过 Ajax 请求发送给后端。

停止浏览器调用输入输出设备

通过调用mediaStream.getTracks().forEach(track => track.stop())方法停止浏览器调用输入输出设备。

mediaStream.getTracks()方法将返回一个MediaStreamTrack对象的数组,表示当前MediaStream中的所有轨道(音频轨道和视频轨道)。通过遍历这个数组,并调用每个轨道的stop()方法,你可以停止浏览器调用输入输出设备。

在你的代码中,你可以在停止录制后的recordData.stop()之后添加以下代码:

javascript
mediaStream.getTracks().forEach((track) => track.stop());
mediaStream.getTracks().forEach((track) => track.stop());

这将停止浏览器调用输入输出设备,释放资源。

参考

  1. https://juejin.cn/post/6924563220657586184
  2. https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices