MediaStream 是连接 WebRTC API 和底层物理流的中间层,webRTC将音视频经过Vocie / Video engine进行处理后,再通过MediaStream API给暴露给上层使用。
概念
- 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生产媒体资源,也就是通过,,等媒体标签进行展示,或者是通过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
- 本地设备
可通过调用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);
});
列表中的每个媒体输入都可作为MediaTrackConstraints中对应类型的值,如一个音频设备输入audioDeviceInput可设置为MediaTrackConstraints中audio属性的值
cosnt constraints = { audio : audioDeviceInput }
将该constraint值作为参数传入到MediaDevices.getUserMedia(constraints)
中,便可获得该设备的MediaStream。
MediaDevices.enumerateDevices() MDN文档 MediaDevices.getUserMedia() MDN文档
- 捕获屏幕
使用MediaDevices.getDisplayMedia()
方法,可以提示用户去选择和授权捕获展示的内容或部分内容(如一个窗口),并将录制内容在一个MediaStream
里。
MediaDevices.getDisplayMedia() MDN文档
- HTMLCanvasElement.captureStream()
使用HTMLCanvasElement.captureStream()
方法返回的 CanvasCaptureMediaStream
是一个实时捕获的canvas动画流。
//frameRate设置双精准度浮点值为每个帧的捕获速率。
//如果未设置,则每次画布更改时都会捕获一个新帧。
//如果设置为0,则会捕获单个帧。
cosnt canvasStream = canvas.captureStream(frameRate);
video.srcObject = canvasSream;
HTMLCanvasElement.captureStream() MDN文档 CanvasCaptureMediaStream MDN文档
-
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
。
实现一个简易的录屏工具
- 获取捕获屏幕的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文档
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!