MediaStream 的媒体流对象 (stream) 和流媒体轨道 (track) 详解

navigator.mediaDevices.getUserMediaMediaStream 是实时音视频处理的重要 API。通过这些 API,可以从摄像头、麦克风或其他设备捕获音视频流,应用于视频通话、录制等场景。本文将介绍 navigator.mediaDevices.getUserMedia 的参数配置、MediaStream 的传参、属性和方法,配合详细的代码示例,特别是如何动态添加和移除音视频轨道,以及轨道的处理。

目录

  1. navigator.mediaDevices.getUserMedia
    • 传参配置
      • 布尔值与对象配置
    • 错误处理
    • 兼容性
  2. MediaStream
    • 属性
    • 方法
    • 事件
    • 如何动态添加和移除音视频轨道
  3. 实例代码
  4. 流媒体轨道 (MediaStreamTrack)
    • 属性
    • 方法
    • 事件
    • 实例代码

navigator.mediaDevices.getUserMedia

传参配置

getUserMedia() 方法接受一个参数对象,用于定义所需的媒体类型和具体约束。它的语法如下:

navigator.mediaDevices.getUserMedia(constraints)
  .then(function(stream) {
    // 处理成功的媒体流
  })
  .catch(function(error) {
    // 处理错误
  });
布尔值与对象配置

constraints 参数用于定义音频和视频的需求,既可以是布尔值,也可以是一个详细的对象。

1. 布尔值配置
  • audio: true:请求音频轨道。
  • video: true:请求视频轨道。

这种简单配置仅表示是否请求音频或视频轨道,而不指定任何具体属性。

const constraints = {
  audio: true,
  video: true
};
2. 对象配置

如果需要更加精细地控制音视频流的特性,可以将 audiovideo 设置为对象,指定详细的参数。

音频参数

音频配置对象可包含以下参数:

  • echoCancellation:是否启用回声消除(布尔值)。
  • noiseSuppression:是否启用噪声抑制(布尔值)。
  • autoGainControl:是否启用自动增益控制(布尔值)。

示例:

const audioConstraints = {
  audio: {
    echoCancellation: true, // 启用回声消除
    noiseSuppression: true, // 启用噪声抑制
    autoGainControl: false  // 关闭自动增益控制
  }
};
视频参数

视频配置对象可以包含以下参数:

  • width:定义视频宽度,可以是具体值或理想值(idealminmax)。
  • height:定义视频高度,同样可以是具体值或理想值。
  • frameRate:帧率控制。
  • facingMode:定义使用前置或后置摄像头(“user” 为前置,“environment” 为后置)。

示例:

const videoConstraints = {
  video: {
    width: { ideal: 1280 },  // 理想宽度
    height: { ideal: 720 },  // 理想高度
    frameRate: { max: 30 },  // 最大帧率 30fps
    facingMode: "user"       // 前置摄像头
  }
};
综合示例
const constraints = {
  audio: {
    echoCancellation: true,
    noiseSuppression: true
  },
  video: {
    width: { min: 640, ideal: 1280, max: 1920 },
    height: { min: 480, ideal: 720, max: 1080 },
    frameRate: { ideal: 30, max: 60 },
    facingMode: "user"
  }
};

错误处理

getUserMedia() 调用失败时会返回一个 Promise,并触发错误。常见错误类型包括:

  • NotAllowedError:用户拒绝授予摄像头/麦克风访问权限。
  • NotFoundError:找不到满足请求的设备。
  • OverconstrainedError:指定的约束条件无法满足。
  • NotReadableError:设备硬件故障,无法读取数据。

处理示例:

navigator.mediaDevices.getUserMedia(constraints)
  .then(function(stream) {
    // 成功处理
  })
  .catch(function(error) {
    console.error("错误: ", error.name);
  });

兼容性

浏览器 支持情况
Chrome 完全支持
Firefox 完全支持
Edge 完全支持
Safari 仅 HTTPS 支持
Internet Explorer 不支持
移动端浏览器 部分支持

MediaStream

MediaStream 是由多条 MediaStreamTrack(音轨或视频轨)组成的媒体流。它允许开发者动态操控音视频轨道,包括添加、移除轨道,或将媒体流传递给其他 API 进行处理(如 WebRTC)。
MediaStream 接口代表一个媒体数据流,由多个轨道(MediaStreamTrack)组成。这些轨道可以是音频或视频轨道。MediaStream 可用于:

  • 获取用户的摄像头和麦克风输入。
  • 处理和修改媒体数据。
  • 通过 WebRTC 进行实时通信。

属性

  • id:流的唯一标识符(只读)。
  • active:指示流是否处于活动状态的布尔值(只读)。

方法

  • getTracks():返回流中所有的音视频轨道。
  • getAudioTracks():返回流中的所有音轨。
  • getVideoTracks():返回流中的所有视频轨道。
  • addTrack(track):向 MediaStream 添加音频或视频轨道。
  • removeTrack(track):从 MediaStream 中移除指定轨道。
  • clone():克隆当前的媒体流,生成一个相同的 MediaStream

事件

  • addtrack:当轨道被添加时触发。
  • removetrack:当轨道被移除时触发。

如何动态添加和移除音视频轨道

添加轨道

可以通过 addTrack() 方法向 MediaStream 动态添加新的音视频轨道。示例如下:

const videoElement = document.querySelector('video');

// 请求媒体流
navigator.mediaDevices.getUserMedia({ audio: true, video: true })
  .then(function(stream) {
    videoElement.srcObject = stream;

    // 获取视频轨道
    const videoTrack = stream.getVideoTracks()[0];

    // 添加新的视频轨道(假设你有一个新的视频轨道)
    const newVideoTrack = /* 假设从其他源获取 */;
    stream.addTrack(newVideoTrack);
  })
  .catch(function(error) {
    console.error("错误: ", error);
  });

移除轨道

可以通过 removeTrack() 方法从 MediaStream 中移除指定的音视频轨道:

navigator.mediaDevices.getUserMedia({ audio: true, video: true })
  .then(function(stream) {
    const audioTrack = stream.getAudioTracks()[0]; // 获取音频轨道

    // 从流中移除音频轨道
    stream.removeTrack(audioTrack);
  })
  .catch(function(error) {
    console.error("错误: ", error);
  });

实例代码

下面是一个完整的示例,展示如何通过 getUserMedia 获取摄像头和麦克风,添加和移除轨道。

  • 实例1
DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>MediaStream 示例title>
  <style>
    video {
      width: 640px;
      height: 480px;
      background-color: black;
    }
  style>
head>
<body>
  <h1>MediaStream 示例h1>
  <video id="video" autoplay playsinline>video>
  <script>
    // 检查浏览器是否支持
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      const constraints = {
        audio: true,
        video: {
          width: { ideal: 1280 },
          height: { ideal: 720 },
          facingMode: 'user' // 使用前置摄像头
        }
      };

      navigator.mediaDevices.getUserMedia(constraints)
        .then(function(stream) {
          const video = document.getElementById('video');
          video.srcObject = stream;
          video.onloadedmetadata = function(e) {
            video.play();
          };
        })
        .catch(function(err) {
          console.error('发生错误: ' + err.name + ': ' + err.message);
        });
    } else {
      alert('您的浏览器不支持 getUserMedia API');
    }
  script>
body>
html>

  • 实例2
DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>MediaStream 示例title>
  <style>
    video {
      width: 640px;
      height: 480px;
      background-color: black;
    }
  style>
head>
<body>
  <h1>MediaStream 示例h1>
  <video id="video" autoplay playsinline>video>
  <button id="addTrack">添加视频轨道button>
  <button id="removeTrack">移除音轨button>

  <script>
    const video = document.getElementById('video');
    let mediaStream;

    // 获取音视频流
    navigator.mediaDevices.getUserMedia({
      audio: { echoCancellation: true },
      video: { width: 1280, height: 720 }
    })
    .then(function(stream) {
      mediaStream = stream;
      video.srcObject = mediaStream;
    })
    .catch(function(err) {
      console.error('错误: ' + err);
    });

    // 添加视频轨道
    document.getElementById('addTrack').addEventListener('click', () => {
      const newVideoTrack = /* 获取新的视频轨道 */;
      mediaStream.addTrack(newVideoTrack);
    });

    // 移除音频轨道
    document.getElementById('removeTrack').addEventListener('click', () => {
      const audioTrack = mediaStream.getAudioTracks()[0];
      mediaStream.removeTrack(audioTrack);
    });
  script>

流媒体轨道 (MediaStreamTrack)

概述

MediaStreamTrack 代表 MediaStream 中的音频或视频轨道。每个轨道独立于其他轨道,可以单独控制、启用、禁用、或通过 clone() 方法复制。

通常,轨道通过调用 navigator.mediaDevices.getUserMedia() 返回的 MediaStream 对象获取。例如,一个 MediaStream 可以包含一条音频轨道和一条视频轨道。

属性

  • kind:轨道的类型,值为 "audio""video",表示该轨道是音频还是视频。

    track.kind; // 'audio' 或 'video'
    
  • id:轨道的唯一标识符,通常在流的不同上下文中用于识别不同的轨道。

    track.id; // 一个唯一的字符串
    
  • label:轨道的标签,通常是设备的名称(如摄像头或麦克风的名称)。

    track.label; // 例如 'Integrated Camera' 或 'Microphone'
    
  • enabled:布尔值,表示轨道是否启用。如果设置为 false,轨道的数据将不会传递,但轨道仍然存在于流中。

    track.enabled = false; // 暂停轨道传输
    
  • muted:布尔值,表示轨道是否被静音。该属性通常由浏览器控制,当轨道没有数据或用户禁用了权限时会自动设置为 true

    if (track.muted) {
      console.log('轨道被静音');
    }
    
  • readyState:轨道的当前状态,可能的值为:

    • "live":轨道正在传输数据。
    • "ended":轨道不再传输数据,例如设备已被移除。
    console.log(track.readyState); // 'live' 或 'ended'
    

方法

  • clone():返回当前轨道的一个副本。新轨道与原轨道相同,但可以独立控制。

    const clonedTrack = track.clone();
    
  • stop():停止轨道传输,并将 readyState 设置为 "ended"。停止后,该轨道将不再传输数据。

    track.stop();
    
  • applyConstraints(constraints):应用指定的约束条件(如分辨率或帧率)来控制轨道的参数。适用于视频轨道。

    const constraints = { width: { min: 640, ideal: 1280 }, height: { ideal: 720 } };
    track.applyConstraints(constraints)
      .then(() => console.log('应用约束成功'))
      .catch(err => console.error('应用约束失败', err));
    
  • getCapabilities():返回轨道的能力信息,表明该轨道支持的各种参数范围(如宽度、高度、帧率等)。

    const capabilities = track.getCapabilities();
    console.log(capabilities);
    
  • getConstraints():返回当前应用到该轨道的约束。

    const constraints = track.getConstraints();
    console.log(constraints);
    
  • getSettings():返回当前轨道的设置(如分辨率、帧率等),这些设置通常是在调用 applyConstraints 方法时应用的。

    const settings = track.getSettings();
    console.log(settings);
    

事件

MediaStreamTrack 可以触发多个事件,帮助开发者处理轨道的状态变化:

  • ended:当轨道停止传输(如设备被移除或调用了 stop() 方法)时触发。

    track.addEventListener('ended', () => {
      console.log('轨道结束');
    });
    
  • mute:当轨道由于某种原因静音时触发(例如用户拒绝权限或设备被禁用)。

    track.addEventListener('mute', () => {
      console.log('轨道被静音');
    });
    
  • unmute:当轨道恢复传输音频或视频数据时触发。

    track.addEventListener('unmute', () => {
      console.log('轨道恢复传输');
    });
    

实例代码

下面的示例,展示了如何通过 getUserMedia 获取媒体流,并操作 MediaStreamTrack 以控制轨道的启用和禁用。

DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>MediaStreamTrack 示例title>
head>
<body>
  <h1>MediaStreamTrack 示例h1>
  <video id="video" autoplay playsinline>video>
  <button id="toggleVideo">禁用/启用视频轨道button>
  <button id="stopVideo">停止视频轨道button>

  <script>
    let videoTrack;

    // 获取媒体流
    navigator.mediaDevices.getUserMedia({ video: true, audio: false })
      .then(function(stream) {
        document.getElementById('video').srcObject = stream;

        // 获取视频轨道
        videoTrack = stream.getVideoTracks()[0];
      })
      .catch(function(err) {
        console.error('无法获取媒体流:', err);
      });

    // 切换视频轨道的启用/禁用状态
    document.getElementById('toggleVideo').addEventListener('click', () => {
      if (videoTrack) {
        videoTrack.enabled = !videoTrack.enabled;
        console.log(`视频轨道 ${videoTrack.enabled ? '启用' : '禁用'}`);
      }
    });

    // 停止视频轨道
    document.getElementById('stopVideo').addEventListener('click', () => {
      if (videoTrack) {
        videoTrack.stop();
        console.log('视频轨道已停止');
      }
    });
  script>
body>
html>

参考资料

  • MDN Web Docs - MediaStream
  • MDN Web Docs - MediaDevices.getUserMedia()
  • Can I use - getUserMedia
  • MediaStreamTrack - MDN
  • WebRTC 官方文档

你可能感兴趣的:(前端)