在前一篇讲了WebRTC的基本概念,接下去的几篇会介绍WebRTC的外围用法。
WebRTC离不开音频和视频,那么最开始的问题就是,这些媒体数据是怎么获取的?在RTC中,采集音频或视频设施后会源源不断地产生媒体数据,这些数据被称为媒体流。例如,从Canvas、摄像头或计算机桌面捕捉的流为视频流,从麦克风捕捉的流为音频流。媒体流是朝远端发送音视频数据的前置条件,否则会呈现连贯建设后数据不通的状况,即看不到对方的视频或听不到对方的声音。因为这些媒体流中混入的可能是多种数据,因而WebRTC又将其划分成多个轨道,分为视频媒体轨道和音频媒体轨道。这个和现实生活相比很好了解,比方高速要求开120KM,如果自行车下来了,就会造成整体速度的降落,所以才会分不同的车道,更快的火车就有铁轨,飞机也有飞机的路线,这样每个交通工具都可能利用带宽最大化。在WebRTC中,每个轨道对应于具体的设施,如视频直播中,主播的计算机上可能插入了多个高清摄像头和业余话筒。这些设施的名称和Id能够通过媒体轨道获取,从而能够对其进行管制或拜访以后设施状态,如麦克风静音和勾销静音。
媒体流和轨道相干API
MediaStream
媒体流能够通过getUserMedia或getDisplayMedia接口获取
MediaStreamTrack
媒体轨道通过MediaStream的getVideoTracks获取所有的视频轨道,通过getAudioTracks获取所有的音频轨道
Video.captureStream
Video为视频源对象,如正在点播的电影,通过captureStream办法能够捕捉到其媒体流,传入的帧率越大,视频画面越晦涩,同时所需的带宽也越大
Canvas.captureStream
Canvas为浏览器画布对象,如通过Canvas实现的一个画板,通过captureStream办法能够捕捉其媒体流,即可动静的获取画板中所画的内容。同理,传入的帧率越大,画板描述的动作越晦涩,当然,所需的带宽也越大,个别帧率取10就够用了
媒体流解决是通过MediaStream接口进行的。一个流蕴含几个轨道,比方视频轨道和音频轨道。有两种办法能够输入MediaStream对象:其一,能够将输入显示为视频或音频元素;其二,能够将输入发送到RTCPeerConnection对象,而后将其发送到近程计算机。媒体流不仅能够通过拜访设施产生,还能够通过其余形式获取。归纳起来有以下几种形式。
- 摄像头:捕捉用户的摄像头硬件设施。
- 麦克风:捕捉用户的麦克风硬件设施。
- 计算机屏幕:捕捉用户的计算机桌面。
- 画布Canvas:捕捉浏览器的Canvas标签内容。
- 视频源Video:捕捉Video标签播放的视频内容。
- 远端流:应用对等连贯来接管新的流,如对方发过来的音视频流。
要进行媒体流的开发,就要先相熟外围API,简略演绎一下MediaStream的属性、事件及办法。
- 属性通过MediaStream.active能够监听流是否处于活动状态,即是否有音视频流。罕用属性如下所示。·
- MediaStream.active:如果MediaStream处于活动状态,则返回true,否则返回false。
- MediaStream.ended:如果在对象上已触发完结事件,则返回true,这意味着流已齐全读取,如果未达到流结尾,则为false。
- MediaStream.id:对象的惟一标识符。
- MediaStream.label:用户代理调配的惟一标识符。
在高速上,有可能因为车出了故障造成拥挤,也有可能因为变道降速、提速,这些在WebRTC中就叫做事件。
MediaStream事件用于监听流解决及活动状态变动。如当用户点击共享屏幕的“进行”按钮时会触发MediaStream.oninactive事件。当增加新的MediaStreamTrack对象时触发MediaStream.addTrack事件。
罕用的事件如下。
- MediaStream.onactive:当MediaStream对象变为活动状态时触发的流动事件的处理程序。
- MediaStream.onaddtrack:在增加新的MediaStreamTrack对象时触发的addTrack事件的处理程序。
- MediaStream.onended:当流终止时触发的完结事件的处理程序。
- MediaStream.oninactive:当MediaStream对象变为非活动状态时触发的非流动事件的处理程序。
- MediaStream.onremovetrack:在从它移除MediaStreamTrack对象时触发的removeTrack事件的处理程序。
很显然,高速上如果有某个事件,那就须要比方打高速交警,开双闪等,在web RTC中,也有相似的对事件的解决。次要就是用办法MediaStream,用于增加、删除、克隆及获取音视频轨道。办法及阐明如下
- MediaStream.addTrack():将作为参数的MediaStreamTrack对象增加到MediaStream中。如果曾经增加了音轨,则没有产生任何事件。
- MediaStream.clone():应用新id返回MediaStream对象的克隆。
- MediaStream.getAudioTracks():从MediaStream对象返回音频MediaStreamTrack对象的列表。
- MediaStream.getTrackById():通过id返回跟踪。如果参数为空或未找到id,则返回null。如果多个轨道具备雷同的id,则返回第一个轨道
- MediaStream.getTracks():从MediaStream对象返回所有MediaStreamTrack对象的列表。
- MediaStream.getVideoTracks():从MediaStream对象返回视频MediaStreamTrack对象的列表。
- MediaStream.removeTrack():从MediaStream中删除作为参数的MediaStreamTrack对象。如果已删除该轨道,则不会产生任何操作。
再来讲一下很重要的媒体录制。影像及声音的保留在有些场景下是有必要的,如医生会诊,举办重要视频会议,老师传授课程等场景,目标是便于日后回放。
MediaRecorder是管制媒体录制的API,在原生App开发中是一个利用宽泛的API,用于在App内录制音频和视频。事实上随着Web的利用越来越富媒体化,W3C也制订了相应的Web规范,称为MediaRecorder API,它给咱们的Web页面赋予了录制音视频的能力,使得Web能够脱离服务器、客户端的辅助,独立进行媒体流的录制。任何媒体模式的标签都能够录制,包含音频标签<audio>、视频标签<video>以及画布标签<canvas>,其中<audio>与<video>能够来自网络媒体文件,也能够来自本机设备采集。而<canvas>的内容则更加自在,任何绘制在画布上的用户操作、2D或3D图像,都能够进行录制。它为Web提供了更多可能性,咱们甚至能够把一个HTML5游戏流程录成视频,保留落地或进行实况传输。录制进去的是通过规范编码后的媒体流数据,能够注入<video>标签,也能够打包生成文件,还能够进行流级别的数据处理,比方画面辨认、动静插入内容、播放跳转管制等。最初生成的文件能够是mp3、mp4、ogg以及webm等格局。
罕用API能够应用MediaRecorder.start(timeslice)录制媒体,其中,timeslice是可选项,如果没有设置,则在整个录制实现后触发ondataavailable事件,如果设置了,比方设置为10,就会每录制10毫秒触发一次ondataavailable事件。罕用办法如下所示。·MediaRecorder.stop():进行录制。·MediaRecorder.pause():暂停录制。·MediaRecorder.resume():复原录制。·MediaRecorder.isTypeSupported():查看是否反对录制某个格局。
制事件MediaRecorder有两个重要事件,
- MediaRecorder.ondataavailable:当数据无效时触发的事件,数据无效时能够把数据存储到缓存区里。
- MediaRecorder.onerror:当有谬误时触发的事件,出错时录制会被进行。
bitsPerSecond:指定音频和视频的比特率,此属性能够用来指定下面两个属性。如果只有上述两个属性之一或此属性被指定,则此属性能够用于设定另外一个属性。
通过MediaRecorder对象能够将麦克风采集的音频数据录制成一个声音文件,如ogg文件。
WebRTC能够间接捕捉用户电脑的整个屏幕,也可捕捉某个利用窗口。旧的Chrome浏览器还须要借助插件来解决这一问题,新的Chrome则能够间接获取。当获取到用户电脑的数据流stream后,再应用MediaRecorder则可将其录制成视频文件。显示器的分辨率能够设置成不同的值,如2880×1800、1440×900。分辨率越高,清晰度越高。当显示器的分辨率较高时,如果捕捉时设置了一个较小的束缚,则可能最终录下来的视频不清晰。
最初附上相干代码:
import React from "react";
import {Button} from "antd";
let mediaRecorder;
let recordedBlobs;
let stream;
class RecordScreen extends React.Component {
startCaptureScreen = async (e) => {
try {
stream = await navigator.mediaDevices.getDisplayMedia({
video: {
width: 2880,
height: 1800}
}); 
const video = _this_.refs['myVideo'];
const videoTracks = stream.getVideoTracks();
console.log(视频资源名称: ${videoTracks[0].label}
);
window._stream_ = stream;
video.srcObject = stream; 
_this_._startRecord_(); 
} catch (e) {
console.log('getUserMedia谬误:' + error);
}
}
 
startRecord = (e) => {
stream.addEventListener('inactive', e => {
console.log('监听到屏幕捕捉进行后进行录制!');
_this_._stopRecord_(e);
}); 
recordedBlobs = [];
try {
mediaRecorder = new MediaRecorder(window._stream_, {mimeType: 'video/webm'});
} catch (e) {
console.error('创立MediaRecorder谬误:', e);
_return_;
} 
mediaRecorder.onstop = (event) => {
console.log('录制进行: ', event);
console.log('录制的Blobs数据为: ', recordedBlobs);
}; 
mediaRecorder.ondataavailable = (event) => {
console.log('handleDataAvailable', event);
if (event.data && event.data.size > 0) {
recordedBlobs.push(event.data);}
};
mediaRecorder.start(10);
console.log('MediaRecorder started', mediaRecorder);
}
stopRecord = (e) => {
mediaRecorder.stop();
stream.getTracks().forEach(track => track.stop());
stream = _null_; 
const blob = new Blob(recordedBlobs, {type: 'video/webm'});
const url = window.URL._createObjectURL_(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'screen.webm';
document.body.appendChild(a);
a.click();
mediaRecorder = new MediaRecorder(window._stream_, {mimeType: 'video/webm'});
}
_catch_(e) {
console.error('创立MediaRecorder谬误:', e);
_return_;
}
mediaRecorder._onstop_ = (event) => {
console.log('录制进行: ', event);
console.log('录制的Blobs数据为: ', recordedBlobs);
};
mediaRecorder._ondataavailable_ = (event) => {
console.log('handleDataAvailable', event);
if (event.data && event.data.size > 0) {
recordedBlobs.push(event.data);
}
};
mediaRecorder.start(10;console.log(
'MediaRecorder started'
mediaRecorder;
} 
stopRecord = (e) => {
mediaRecorder.stop();
stream.getTracks().forEach(track => track.stop());
stream = _null_; 
const blob = new Blob(recordedBlobs, {type: 'video/webm'});
const url = window.URL._createObjectURL_(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'screen.webm';
document.body.appendChild(a);
a.click();
当咱们学会了如何通过多种形式获取本地媒体流、音视频设置以及管制媒体流的内容后,就要思考如何把本地的媒体数据以流的形式发送到远端,远端接管到媒体流后,渲染视频并播放声音,从而达到通话的目标。
在下一篇中,就会讲一对一的视频是怎么实现的了。
「本文为集体原创,首发于 RTC开发者社区」