Quiet
  • 主页
  • 归档
  • 分类
  • 标签
  • 链接
  • 关于我

bajiu

  • 主页
  • 归档
  • 分类
  • 标签
  • 链接
  • 关于我
Quiet主题
  • 流媒体
  • WebRTC

MediaStream 简介

bajiu
前端

2021-07-14 16:55:05

MediaStream API 是连接 WebRTC API 和底层物理流的中间层, webRTC 将音视频经过Vocie / Video engine进行处理后, 再通过 MediaStream API 给暴露给上层使用。

MediaStream

一个 MediaStream 对象包含零个或更多的 MediaStreamTrack 对象, 代表着各种的声轨和视频轨. 每一个 MediaStreamTrack 可能有一个或更多的通道. 这个通道代表着媒体流的最小单元, 比如一个音频信号对应着一个对应的扬声器, 像是在立体声音轨中的左通道或右通道。MediaTrack由 source 与 sink 组成. WebRTC并不能直接访问或者控制源, 对源的一切控制都可以通过轨道控制 MediaTrackConstraints 进行实施

MediaStreamTrack

MediaStreamTrack 是 WebRTC 中的基本媒体单位, 一个 MediaStreamTrack 包含一种媒体源(媒体设备或录制内容)返回的单一类型的媒体(如音频,视频).单个轨道可包含多个通道, 如立体声源尽管由多个音频轨道构成, 但也可以看作是一个轨道

MediaStream

MediaStream 是 MediaStreamTrack 的合集, 可以包含 >=0 个 MediaStreamTrack . MediaStream 能够确保它所包含的所有轨道都是是同时播放的, 以及轨道的单一性

MediaStream MDN文档

source 与 sink

在 MediaTrack 的源码中, MediaTrack 都是由对应的 source 和 sink 组成的

//src\pc\video_track.cc
void VideoTrack::AddOrUpdateSink(rtc::VideoSinkInterface<VideoFrame>* sink, const rtc::VideoSinkWants& wants) {
  RTC_DCHECK(worker_thread_->IsCurrent());
  VideoSourceBase::AddOrUpdateSink(sink, wants);
  rtc::VideoSinkWants modified_wants = wants;
  modified_wants.black_frames = !enabled();
  video_source_->AddOrUpdateSink(sink, modified_wants);
}
 
void VideoTrack::RemoveSink(rtc::VideoSinkInterface<VideoFrame>* sink) {
  RTC_DCHECK(worker_thread_->IsCurrent());
  VideoSourceBase::RemoveSink(sink);
  video_source_->RemoveSink(sink);
}

浏览器中存在从 source 到 sink 的媒体管道, 其中 source 负责生产媒体资源, 包括多媒体文件, web 资源等静态资源以及麦克风采集的音频, 摄像头采集的视频等动态资源。而sink则负责消费source生产媒体资源, 也就是通过video 等媒体标签进行展示, 或者是通过 RTCPeerConnection 将 source 通过网络传递到远端。 RTCPeerConnection 可同时扮演 source 与 sink 的角色,作为 sink, 可以将获取的 source 降低码率, 缩放, 调整帧率等, 然后传递到远端, 作为 source, 将获取的远端码流传递到本地渲染

MediaTrackConstraints

MediaTrackConstraints 描述 MediaTrack 的功能以及每个功能可以采用的一个或多个值, 从而达到选择和控制源的目的 MediaTrackConstraints 可作为参数传递给 applyConstraints() 以达到控制轨道属性的目的, 同时可以通过调 getConstraints() 用来查看最近应用自定义约束

const constraints = {
  width: {min: 640, ideal: 1280},
  height: {min: 480, ideal: 720},
  advanced: [
    {width: 1920, height: 1280},
    {aspectRatio: 1.333}
  ]
};

// { video: true }也是一个MediaTrackConstraints对象,用于指定请求的媒体类型和相对应的参数。
navigator.mediaDevices.getUserMedia({ video: true })
.then(mediaStream => {
  const track = mediaStream.getVideoTracks()[0];
  track.applyConstraints(constraints)
  .then(() => {
    // Do something with the track such as using the Image Capture API.
  })
  .catch(e => {
    // The constraints could not be satisfied by the available devices.
  });
});

更多不同的约束条件:

//使用1280x720的摄像头分辨率
{
  audio: true,
  video: { width: 1280, height: 720 }
}
//移动设备上面,优先使用前置摄像头
{ audio: true, video: { facingMode: "user" } }
//移动设备上面,强制使用后置摄像头
{ audio: true, video: { facingMode: { exact: "environment" } } }

如何播放MediaStream

可将 MediaStream 对象直接赋值给 HTMLMediaElement 接口的 srcObject 属性

video.srcObject = stream;

srcObject MDN文档

如何获取MediaStream

  1. 本地设备

通过调用 MediaDevices.getUserMedia() 来访问本地媒体,调用该方法后浏览器会提示用户给予使用媒体输入的许可, 媒体输入会产生一个 MediaStream, 里面包含了请求的媒体类型的轨道. 此流可以包含一个视频轨道( 来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等) 、一个音频轨道 (同样来自硬件或虚拟音频源,比如麦克风、A/D转换器等等), 也可能是其它轨道类型

navigator.mediaDevices.getUserMedia(constraints)
  .then(function(stream) {
    /* 使用这个stream*/
    video.srcObject = stream;
  })
  .catch(function(err) {
    /* 处理error */
  });

通过 MediaDevices.enumerateDevices() 我们可以得到一个本机可用的媒体输入和输出设备的列表, 例如麦克风, 摄像机, 耳机设备等

将该constraint值作为参数传入到MediaDevices.getUserMedia(constraints)中, 便可获得该设备的MediaStream。

cosnt constraints = { audio : audioDeviceInput }


//获取媒体设备
navigator.mediaDevices.enumerateDevices().then(res => {
    console.log(res);
});
  1. 捕获屏幕

使用MediaDevices.getDisplayMedia()方法, 可以提示用户去选择和授权捕获展示的内容或部分内容(如一个窗口), 并将录制内容在一个 MediaStream 里

  1. HTMLCanvasElement.captureStream()

使用 HTMLCanvasElement.captureStream() 方法返回的 CanvasCaptureMediaStream 是一个实时捕获的 canvas 动画流

//frameRate设置双精准度浮点值为每个帧的捕获速率。
//如果未设置,则每次画布更改时都会捕获一个新帧。
//如果设置为0,则会捕获单个帧。
cosnt canvasStream = canvas.captureStream(frameRate);
video.srcObject = canvasSream;
  1. RTCPeerConnection

  2. 从其他MediaStream中获取

可通过构造函数 MediaStream() 返回新建的空白的 MediaStream 实例

newStream = new MediaStream();

传入 MediaStream 对象,该 MediaStream 对象的数据轨会被自动添加到新建的流中. 且这些数据轨不会从原流中移除, 即变成了两条流共享的数据

newStream = new MediaStream(otherStream);

传入 MediaStreamTrack 对象的 Array 类型的成员,代表了每一个添加到流中的数据轨。

newStream = new MediaStream(tracks[]);

MediaStream.addTrack() 方法会给流添加一个新轨道。

MediaStream.clone() 方法复制一份副本 MediaStream 这个新的MediaStream对象有一个新的id。

上一篇

视频会议服务器SFU、MCU、Mesh三种类型的区别

下一篇

视频会议服务器SFU、MCU、Mesh三种类型的区别

©2025 By bajiu.