美文网首页
谈谈MediaStream

谈谈MediaStream

作者: CBDxin | 来源:发表于2021-02-24 08:47 被阅读0次

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


image.png

概念

  • MediaStreamTrack
  • MediaStream
  • source
  • sink
  • MediaTrackConstraints

1. MediaStreamTrack

MediaStreamTrack是WebRTC中的基本媒体单位,一个MediaStreamTrack包含一种媒体源(媒体设备或录制内容)返回的单一类型的媒体(如音频,视频)。单个轨道可包含多个频道,如立体声源尽管由多个音频轨道构成,但也可以看作是一个轨道。
WebRTC并不能直接访问或者控制源,对源的一切控制都可以通过轨道控制MediaTrackConstraints进行实施。

MediaStreamTrack MDN文档
MediaTrackConstraints MDN文档

2. MediaStream

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

MediaStream MDN文档

3. 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生产媒体资源,也就是通过<img>,<video>,<audio>等媒体标签进行展示,或者是通过RTCPeerConnection将source通过网络传递到远端。RTCPeerConnection可同时扮演source与sink的角色,作为sink,可以将获取的source降低码率,缩放,调整帧率等,然后传递到远端,作为source,将获取的远端码流传递到本地渲染。

source 与sink构成一个MediaTrack,多个MeidaTrack构成MediaStram。

4. 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.
  });
});

如何播放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()我们可以得到一个本机可用的媒体输入和输出设备的列表,例如麦克风,摄像机,耳机设备等。

//获取媒体设备
navigator.mediaDevices.enumerateDevices().then(res => {
    console.log(res);
});
image.png

列表中的每个媒体输入都可作为MediaTrackConstraints中对应类型的值,如一个音频设备输入audioDeviceInput可设置为MediaTrackConstraints中audio属性的值

cosnt constraints = { audio : audioDeviceInput }

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

MediaDevices.enumerateDevices() MDN文档
MediaDevices.getUserMedia() MDN文档

  1. 捕获屏幕

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

image.png

MediaDevices.getDisplayMedia() MDN文档

  1. HTMLCanvasElement.captureStream()

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

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

HTMLCanvasElement.captureStream() MDN文档
CanvasCaptureMediaStream MDN文档

  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

实现一个简易的录屏工具

  • 获取捕获屏幕的MeidaStream
const screenStream = await navigator.mediaDevices.getDisplayMedia();
  • 获取本地音视频数据
const cameraStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });
const audioStream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true });
  • 将屏幕画面与摄像头画面绘制canvas中
function createStreamVideo(stream) {
    const video = document.createElement("video");
    video.srcObject = stream;
    video.autoplay = true;

    return video;
}

const cameraVideo = createStreamVideo(cameraStream);
const screenVideo = createStreamVideo(screenStream);
animationFrameHandler() {
      if (screenVideo) {
          canvas.drawImage(screenVideo, 0, 0, canvasWidth, canvasHeight);
      }

      if (cameraVideo) {
          canvas.drawImage(
              cameraVideo,
              canvasWidth - CAMERA_VIDEO_WIDTH,
              canvasHeight - CAMERA_VIDEO_HEIGHT,
              CAMERA_VIDEO_WIDTH,
              CAMERA_VIDEO_HEIGHT
          )
      }

      requestAnimationFrame(animationFrameHandler.bind(this));
  }
  • 获取canvas的MediaStream
const recorderVideoStream = await canvas.captureStream();
  • 合并本地的音频轨道和canvasStream的视频轨道,获得最终画面的MediaStream
const stream = new MediaStream();
audioStream.getAudioTracks().forEach(track => stream.addTrack(track));
recorderVideoStream.getVideoTracks().forEach(track => stream.addTrack(track));

video.srcObject = stream;
  • 通过MediaRecorder对画面进行录制
const recorder = new MediaRecorder(stream, { mineType: "video/webm;codecs=h264" });
recorder.ondataavailable = e => {
    recorderVideo.src = URL.createObjectURL(e.data);
};

//开始录制
recorder.start();

//停止录制
recorder.stop();

MediaRecorder MDN文档

相关文章

网友评论

      本文标题:谈谈MediaStream

      本文链接:https://www.haomeiwen.com/subject/xcdhfltx.html