MediaStream API 是连接 WebRTC API 和底层物理流的中间层, webRTC 将音视频经过Vocie / Video engine
进行处理后, 再通过 MediaStream API 给暴露给上层使用。
一个 MediaStream
对象包含零个或更多的 MediaStreamTrack
对象, 代表着各种的声轨和视频轨. 每一个 MediaStreamTrack
可能有一个或更多的通道. 这个通道代表着媒体流的最小单元, 比如一个音频信号对应着一个对应的扬声器, 像是在立体声音轨中的左通道或右通道。MediaTrack由 source
与 sink
组成. WebRTC并不能直接访问或者控制源, 对源的一切控制都可以通过轨道控制 MediaTrackConstraints
进行实施
MediaStreamTrack
MediaStreamTrack
是 WebRTC
中的基本媒体单位, 一个 MediaStreamTrack
包含一种媒体源(媒体设备或录制内容)返回的单一类型的媒体(如音频,视频).单个轨道可包含多个通道, 如立体声源尽管由多个音频轨道构成, 但也可以看作是一个轨道
MediaStream
MediaStream
是 MediaStreamTrack
的合集, 可以包含 >=0 个 MediaStreamTrack
. MediaStream
能够确保它所包含的所有轨道都是是同时播放的, 以及轨道的单一性
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;
如何获取MediaStream
- 本地设备
通过调用 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);
});
- 捕获屏幕
使用MediaDevices.getDisplayMedia()
方法, 可以提示用户去选择和授权捕获展示的内容或部分内容(如一个窗口), 并将录制内容在一个 MediaStream
里
- HTMLCanvasElement.captureStream()
使用 HTMLCanvasElement.captureStream()
方法返回的 CanvasCaptureMediaStream
是一个实时捕获的 canvas 动画流
//frameRate设置双精准度浮点值为每个帧的捕获速率。
//如果未设置,则每次画布更改时都会捕获一个新帧。
//如果设置为0,则会捕获单个帧。
cosnt canvasStream = canvas.captureStream(frameRate);
video.srcObject = canvasSream;
RTCPeerConnection
从其他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。