WebRtc07: 音视频录制实战
录制基础知识
MediaRecorder
// stream通过getUserMedia获取
var mediaRecorder = new MediaRecorder(stream[,option]);
option参数说明
API和事件
// API
// 开始录制媒体,timeslice是可选的,如果设置了会按时间切片存储数据
MediaRecorder.start(timeslice)
// 停止录制,此时会触发包括最终Blob数据的dataavailable事件
MediaRecorder.stop()
// 暂停录制
MediaRecorder.pause()
// 恢复录制
MediaRecorder.resume()
// 类型支持
MediaRecorder.isTypeSupported()
// 事件
// 数据有效时触发, 每次记录一定时间的数据时(如果没有指定时间切片,则记录整个数据)会定期触发
MediaRecorder.ondataavailable
// 当有错误发生时,录制会被停止
MediaRecorder.onerror
JavaScript几种数据存储方式
- 字符串
- Blob:类似一块搞笑的存储区域,方便将整个缓冲区写入文件
- ArrayBuffer
- ArrayBufferView
实战
录制音视频
实测使用firefox浏览器不支持webm
index.html
<html>
<head>
<title>WebRtc capture video and audio</title>
<style>
.none {
-webkit-filter: none;
}
.blur {
-webkit-filter: blur(3px);
}
.grayscale {
-webkit-filter:grayscale(1);
}
.invert {
-webkit-filter: invert(1);
}
.sepia {
-webkit-filter:sepia(1);
}
</style>
</head>
<body>
<div>
<label>audioSource:</label>
<select id="audioSource"></select>
</div>
<div>
<label>audioOutput:</label>
<select id="audioOutput"></select>
</div>
<div>
<label>videoSource:</label>
<select id="videoSource"></select>
</div>
<div>
<label>filter:</label>
<select id="filter">
<option value="none"> None</option>
<option value="blur"> blur</option>
<option value="grayscale"> grayscale</option>
<option value="invert"> invert</option>
<option value="sepia"> sepia</option>
</select>
</div>
<!-- controls用于显示控制按钮 -->
<!-- <audio autoplay controls id="audioplayer" ></audio> -->
<table>
<tr>
<td><video autoplay playsinline id="player"></video></td>
<td><video playsinline id="recplayer"></video></td>
<!-- 显示视频约束 -->
<td><div id = 'constraints' class='output'></div></td>
</tr>
<tr>
<td><button id="record">Start Record</button></td>
<td><button id="recplay" disabled>Play</button></td>
<td><button id="download" disabled>Download</button></td>
</tr>
</table>
<div>
<button id="snapshot">Take snapshot</button>
</div>
<div>
<canvas id="picture"></canvas>
</div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="./js/client.js"></script>
</body>
</html>
client.js
'use strict'
// devices
var audioSource = document.querySelector('select#audioSource');
var audioOutput = document.querySelector('select#audioOutput');
var videoSource = document.querySelector('select#videoSource');
// filter
var filterSelect = document.querySelector('select#filter');
// picture
var snapshot = document.querySelector('button#snapshot');
var picture = document.querySelector('canvas#picture');
picture.width = 320;
picture.height = 240;
var videoplay = document.querySelector('video#player');
// 获取audioplayer
// var audioplay = document.querySelector('audio#audioplayer');
// div
var divConstraints = document.querySelector('div#constraints');
// record
var recVideo = document.querySelector('video#recplayer');
var btnRecord = document.querySelector('button#record');
var btnPlay = document.querySelector('button#recplay');
var btnDownload = document.querySelector('button#download');
var buffer;
var mediaRecorder;
function getDevice(deviceInfos) {
deviceInfos.forEach(function(deviceInfos) {
var option = document.createElement('option');
option.text = deviceInfos.label;
option.value = deviceInfos.deviceId;
if (deviceInfos.kind == 'audioinput') {
audioSource.appendChild(option);
} else if (deviceInfos.kind == 'audiooutput') {
audioOutput.appendChild(option);
} else if (deviceInfos.kind == 'videoinput') {
videoSource.appendChild(option);
}
});
}
function getMediaStream(stream) {
videoplay.srcObject = stream;
var videoTrack = stream.getVideoTracks()[0];
var videoConstraints = videoTrack.getSettings();
divConstraints.textContent = JSON.stringify(videoConstraints, null, 2);
// 将流放到全局对象中
window.stream = stream;
// audioplay.srcObject = stream;
return navigator.mediaDevices.enumerateDevices();
}
function handleError(err) {
console.log('getUserMedia error: ', err);
}
function start() {
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
console.log('getUserMedia is not supported');
} else {
var deviceId = videoSource.value;
var constraints = {
video : {
width: 640,
height: 480,
frameRate: 30,
deviceId : deviceId ? deviceId : undefined
},
// video : false,
audio : true
}
navigator.mediaDevices.getUserMedia(constraints).then(getMediaStream).then(getDevice).catch(handleError);
}
}
start();
videoSource.onchange = start;
filterSelect.onchange = function() {
videoplay.className = filterSelect.value;
}
snapshot.onclick = function() {
picture.className = filterSelect.value;
picture.getContext('2d').drawImage(videoplay, 0, 0, picture.width, picture.height);
}
function handleDataAvilable(e) {
if (e && e.data && e.data.size > 0) {
buffer.push(e.data);
}
}
function startRecord() {
buffer = [];
var options = {
mimeType: 'video/webm;codecs=vp8'
}
if (MediaRecorder.isTypeSupported(options.mimeType)) {
console.error(`${options.mimeType} is not supported`);
return;
}
try {
mediaRecorder = new MediaRecorder(window.stream, options);
} catch (error) {
console.error('failed to create mediaRecorder');
return;
}
mediaRecorder.ondataavilable = handleDataAvilable;
mediaRecorder.start(10);
}
function stopRecord() {
mediaRecorder.stop();
console.log(`媒体类型:${mediaRecorder.mimeType}`);
}
btnRecord.onclick = ()=>{
if (btnRecord.textContent == 'Start Record') {
startRecord();
btnRecord.textContent = 'Stop Record';
btnPlay.disabled = true;
btnDownload.disabled = true;
} else {
stopRecord();
btnRecord.textContent = 'Start Record';
btnPlay.disabled = false;
btnDownload.disabled = false;
}
}
btnPlay.onclick = ()=>{
// 这里的mimeType必须和录制的一致
var blob = new Blob(buffer, {type: 'video/webm'});
recVideo.src = window.URL.createObjectURL(blob);
recVideo.srcObject = null;
recVideo.controls = true;
recVideo.play();
}
btnDownload.onclick = ()=> {
// 这里的mimeType必须和录制的一致
var blob = new Blob(buffer, {type: 'video/webm'});
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.style.display = 'none';
a.download = 'aaa.webm';
a.click();
}
捕获桌面
getDisplayMedia
只需要把getUserMedia改成getDisplayMedia即可实现捕获桌面的功能