赞
踩
https://codepen.io/vicksiyi/pen/pMWybg
服务端录制: 优点是不用担心客户因自身电脑问题造成录制失败(如磁盘空间不足),也不会因录制时抢占资源(CPU 占用率过高)而导致其他应用出现问题等;缺点是实现的复杂度很高。
客户端录制: 优点是方便录制方(如老师)操控,并且所录制的视频清晰度高,实现相对简单。这里可以和服务端录制做个对比,一般客户端摄像头的分辨率都非常高的(如 1280x720),所以客户端录制可以录制出非常清晰的视频;但服务端录制要做到这点就很困难了,本地高清的视频在上传服务端时由于网络带宽不足,视频的分辨率很有可能会被自动缩小到了 640x360,这就导致用户回看时视频特别模糊,用户体验差。不过客户端录制也有很明显的缺点,其中最主要的缺点就是录制失败率高。 因为客户端在进行录制时会开启第二路编码器,这样会特别耗 CPU。而 CPU 占用过高后,就很容易造成应用程序卡死。除此之外,它对内存、硬盘的要求也特别高。
在了解基本原理之前首先得搞清楚以下问题:
在接入正题之前,首先我们得思考一下:JS存储二进制数据类型(ArrayBuffer、ArrayBufferView、Blob)之间关系
ArrayBuffer 对象表示通用的、固定长度的二进制数据缓冲区。因此,你可以直接使用它存储图片、视频等内容。
但你并不能直接对 ArrayBuffer 对象进行访问,类似于 Java 语言中的抽象类,在物理内存中并不存在这样一个对象,必须使用其封装类进行实例化后才能进行访问。
也就是说, ArrayBuffer 只是描述有这样一块空间可以用来存放二进制数据,但在计算机的内存中并没有真正地为其分配空间。只有当具体类型化后,它才真正地存在于内存中。如下所示:
let buffer = new ArrayBuffer(16); // 创建一个长度为 16 的 buffer
let view = new Uint32Array(buffer);
或
let buffer = new Uint8Array([255, 255, 255, 255]).buffer;
let dataView = new DataView(buffer);
在上面的例子中,一开始生成的 buffer 是不能被直接访问的。只有将 buffer 做为参数生成一个具体的类型的新对象时(如 Uint32Array 或 DataView),这个新生成的对象才能被访问。
ArrayBufferView 并不是一个具体的类型,而是代表不同类型的 Array 的描述。这些类型包括:Int8Array、Uint8Array、DataView 等。也就是说 Int8Array、Uint8Array 等才是 JavaScript 在内存中真正可以分配的对象。
以 Int8Array 为例,当你对其实例化时,计算机就会在内存中为其分配一块空间,在该空间中的每一个元素都是 8 位的整数。再以 Uint8Array 为例,它表达的是在内存中分配一块每个元素大小为 8 位的无符号整数的空间。
通过这上面的描述,你现在应该知道 ArrayBuffer 与 ArrayBufferView 的区别了吧?ArrayBufferView 指的是 Int8Array、Uint8Array、DataView 等类型的总称,而这些类型都是使用 ArrayBuffer 类实现的,因此才统称他们为 ArrayBufferView。
Blob(Binary Large Object)是 JavaScript 的大型二进制对象类型,WebRTC 最终就是使用它将录制好的音视频流保存成多媒体文件的。而它的底层是由上面所讲的 ArrayBuffer 对象的封装类实现的,即 Int8Array、Uint8Array 等类型。
var aBlob = new Blob( array, options );
其中,array 可以是ArrayBuffer、ArrayBufferView、Blob、DOMString等类型 ;option,用于指定存储成的媒体类型。
var mediaRecorder = new MediaRecorder(stream[, options]);
mimeType: 'video/webm;codecs=vp8'
。MediaRecorder 对象还有一个特别重要的事件,即 ondataavailable 事件。当 MediaRecoder 捕获到数据时就会触发该事件。通过它,我们才能将音视频数据录制下来。
开始录制关键代码:
... var buffer; ... var handleDataAvailable = (e) => { if (e && e.data && e.data.size > 0) { buffer.push(e.data); } console.log(buffer); } record.onclick = () => { 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 (e) { console.error('Failed to create MediaRecorder:', e); return; } // 当有音视频数据来了之后触发该事件 mediaRecorder.ondataavailable = handleDataAvailable; // 开始录制 mediaRecorder.start(10); } ...
停止播放关键代码:
mediaRecorder.stop(10);
下载关键代码:
btnDownload.onclick = () => {
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 = 'newBuffer.webm';
a.click();
}
完整代码:
<!DOCTYPE html> <html> <head> <title>WebRTC Learing</title> </head> <body> <div> <video autoplay playsinline id="player"></video> </div> <div> <video id="recvideo"></video> </div> <button id="record">Start Record</button> <button id="recplay" disabled>Play</button> <button id="download" disabled>Download</button> </body> <script> 'use strict'; var buffer; var mediaRecorder //是否已截图 var isSelectPicture = false; var videoplay = document.querySelector('video#player'); var record = document.querySelector('button#record'); var recplay = document.querySelector('button#recplay'); var btnDownload = document.querySelector('button#download'); var recvideo = document.querySelector('video#recvideo') var handleDataAvailable = (e) => { if (e && e.data && e.data.size > 0) { buffer.push(e.data); } console.log(buffer); } record.onclick = () => { 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 (e) { console.error('Failed to create MediaRecorder:', e); return; } // 当有音视频数据来了之后触发该事件 mediaRecorder.ondataavailable = handleDataAvailable; // 开始录制 mediaRecorder.start(10); record.disabled = true; recplay.disabled = false; } recplay.onclick = () => { mediaRecorder.stop(10); recplay.disabled = true; btnDownload.disabled = false; var blob = new Blob(buffer, { type: 'video/webm' }); recvideo.src = window.URL.createObjectURL(blob); recvideo.srcObject = null; recvideo.controls = true; recvideo.play(); } btnDownload.onclick = () => { 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 = 'newBuffer.webm'; a.click(); } ; ( () => { if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { console.log('getUserMedia is not supported!'); return; } else { var constraints = { video: { width: 640, height: 480, frameRate: 15, facingMode: 'enviroment' }, audio: false } navigator.mediaDevices.getUserMedia(constraints) .then((stream) => { window.stream = stream videoplay.srcObject = stream; }) .catch((err) => { console.log('getUserMedia error:', err); }); } } )(); </script> </html>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。