采集音视频数据

通过 enumerateDevices() 接口获得音视频设备后,就可以选择其中的设备进行数据采集了。在浏览器下采集音视频数据也很方便,调用 getUserMedia() 这个 API 就可以采集到。getUserMedia 的接口格式如代码 5.4 所示。

代码5.4 getUserMedia() 接口格式
navigator.mediaDevices.getUserMedia(MediaStreamConstrains);

该接口有一个 MediaStreamConstrains 类型的输入参数,可以用来控制从哪个设备上采集音视频数据,以及限制采集到的数据的格式,如限制采集到的视频分辨率、音频数据的采样率、采样大小等。其结构如代码 5.5 所示。

代码5.5 MediaStreamConstrains结构体
dictionary MediaStreamConstrains {
   (boolean) or (MediaTrackConstrains) video = false;
   (boolean) or (MediaTrackConstrains) audio = false;
}

从上面 MediaStreamConstrains 类型的定义可以看出,videoaudio 属性既可以是 boolean 类型,也可以是 MediaTrackConstrains 类型(只有像 JavaScript 这种弱类型语言才可以做这一点)。因此,我们既可以直接给 videoaudio 赋值 true/false,简单地指明是否采集视频或音频数据,也可以给它赋值一个 MediaTrackConstrains 类型的值,对音视频设备做更精准的设置。

如果直接给 video/audio 属性赋值 true,则浏览器会使用默认设备和默认参数采集音视频数据,否则如果给 video/audio 赋值 MediaTrackConstrains 类型值,则浏览器会按 MediaTrackConstrains 中的限制,从指定的设备中采集音视频数据。MediaTrackConstrains 结构如代码 5.6 所示。

代码5.6 MediaTrackConstraintSet结构体
dictionary MediaTrackConstraintSet {
  // 视频相关
  ConstrainULong width;
  ConstrainULong height;
  ConstrainDouble aspectRatio; // 宽高比
  ConstrainDouble frameRate;
  ConstrainDOMString facingMode; // 前置/后置摄像头
  ConstrainDOMString resizeMode; // 缩放或裁剪

  // 音频相关
  ConstrainULong sampleRate;
  ConstrainULong sampleSize;
  ConstrainBoolean echoCancellation;
  ConstrainBoolean autoGainControl;
  ConstrainBoolean noiseSuppression;
  ConstrainDouble latency; // 目标延迟
  ConstrainULong channelCount;

  // 设备相关
  ConstrainDOMString deviceId;
  ConstrainDOMString groupId;
};

从上面的代码片段中可以看到,MediaTrackConstrains 结构由三部分组成,即视频相关属性、音频相关属性以及设备相关属性。视频属性中包括分辨率、视频宽高比、帧率、前置/后置摄像头、视频缩放;音频属性包括采样率、采样大小、是否开启回音消除、是否开启自动增益、是否开启降噪、目标延迟、声道数;设备相关属性包括设备ID、设备组ID。

我们来看一个具体的例子,看看如何通过 getUserMedia() 接口来采集音视频数据。具体代码参见代码 5.7。

代码5.7 获取音视频流
// 采集到某路流
function gotMediaStream(stream) {
  // ...
}

// 从设备选项栏里选择某个设备
var deviceId = xxx;

// 设置采集限制
var constraints = {
  video: {
    width: 640,
    height: 480,
    frameRate: 15,
    facingMode: 'environment',
    deviceId: deviceId ? { exact: deviceId } : undefined
  },
  audio: false
};

// 开始采集数据
navigator.mediaDevices.getUserMedia(constraints)
  .then(gotMediaStream)
  .catch(handleError);

// ...

在上面的代码片段中,首先执行第 22 行代码,即调用 getUserMedia() 接口,然后根据 constraints 中的限制获取音视频数据。在这个例子中,getUserMedia() 从指定设备(deviceId)上按指定参数采集视频数据,具体参数如下:分辨率为 640×480、帧率为 15 帧/秒、使用后置摄像头(environment)。因为 audio 属性为 false,所以此例中仅采集视频数据而不采集音频数据。

此外,从上面的代码中还可以看到,调用 getUserMedia() 接口的方式与调用 enumerateDevices() 接口的方式是一样的,也是使用 Promise 方式。当调用 getUserMedia() 成功时,会回调 gotMediaStream() 函数,该函数的输入参数 MediaStream 里存放的就是音视频数据流。当获得音视频数据后,既可以把它作为本地预览,也可以将它传送给远端,从而实现一对一通信。如果调用 getUserMedia() 接口失败,则调用错误处理函数 handleError