录屏功能——三种技术方案
作者:互联网
浏览器录制可通过存储与云端或本地的形式进行,综合对比三种方案,浏览器录制是较为性价比高的方案
方案① TRTC 云端录制功能
介绍
● TRTC 服务通过旁路推流使用 云直播 的能力提供全程的云端录制功能,并将录制下来的文件存储到 云点播 平台。
● 云端录制功能默认关闭,启用云端录制功能需要先开通云直播和云点播服务。
● 云端录制功能使用了云直播的能力,因此需先开通云直播服务。录制后将产生录制费用,以录制类型和录制时长为结算标准,详细计费规则请参见 云端录制计费说明 。
● 录制后的文件存储在云点播平台,将产生云点播的存储费用,按录制文件存储在云点播平台的存储容量计费,详细计费规则请参见云点播 > 视频存储(日结)价格说明 或云点播 > 视频存储资源包价格说明。
● 如需播放或下载录制的视频文件,将会产生云点播服务的流量(视频加速)费用,按下行加速流量计费,详细计费规则请参见 云点播 > 视频加速(日结)价格说明 或云点播 > 视频加速资源包价格说明 。
在录制形式上,可选择指定用户录制(可以指定只录制一部分用户的音视频流,这需要通过客户端的 SDK API 或者服务端的 Server API 进行控制)或全局自动录制(每一个 TRTC 房间中的每个用户的音视频上行流都会被自动录制下来,录制任务的启动和停止都是自动的)
全局自动录制
● 录制任务的开始
TRTC 房间中的每一个用户的音视频流都会被自动录制成文件,无需您的额外操作。
● 录制任务的结束
自动停止。即每个主播在停止音视频上行后,该主播的云端录制即会自行停止。
● 已经支持的平台
由您的服务端控制,不受客户端平台限制。
● 多路画面的混合
全局自动录制模式下的云端混流有两种方案,即“服务端 REST API 方案” 和 “客户端 SDK API 方案”,两套方案请勿混合使用:
○ 服务端 REST API 混流方案:需要由您的服务器发起 API 调用,不受客户端平台版本的限制。
○ 客户端 SDK API 混流方案:可以直接在客户端发起混流,目前支持 iOS、Android、Windows、Mac 和 Electron 等平台,暂不支持微信小程序和 Web 浏览器。
指定用户录制
● 录制任务的开始
由您的服务器调用 StartMCUMixTranscode ,并指定 OutputParams.RecordId 参数即可启动混流和录制。
● 录制任务的结束
自动停止,您也可以中途调用 StopMCUMixTranscode 停止混流和录制任务。
● 多路画面的混合
在调用 StartMCUMixTranscode 时同时指定 LayoutParams 参数即可实现云端混流。该 API 支持在整个直播期间多次调用,即您可以根据需要修改 LayoutParams 参数并再次调用该 API 来调整混合画面的布局。但需要注意的是,您需要保持参数 OutputParams.RecordId 和 OutputParams.StreamId 在多次调用中的一致性,否则会导致断流并产生多个录制文件。
● 已经支持的平台
由您的服务端控制,不受客户端平台的限制。
工作量估算
由于客户端 SDK 暂不支持 Web 浏览器,需要由服务端同学进行工作量估算。
Demo 原型
![图片](/api/project/8779773/files/26147939/imagePreview)
落地方案
![图片](/api/project/8779773/files/26147949/imagePreview)
录制的视频可通过回调地址实时接收到新文件,可供用户下载。
风险点
腾讯 TRTC 属于高投资低风险低开发成本
费用估算
1. 自2020年7月1日起首次在 TRTC 控制台创建应用的腾讯云账号,使用云端录制功能后产生的录制费用云端录制计费说明
![图片](/api/project/8779773/files/26147952/imagePreview)
2. 在2020年7月1日之前已经在 TRTC 控制台创建过应用的腾讯云账号,无论是在2020年7月1日之前还是之后创建的应用,使用云端录制功能后产生的录制费用均默认继续延用 云直播 > 直播录制 的计费规则。
![图片](/api/project/8779773/files/26147960/imagePreview)
3. 云端录制完成后输出的录制文件默认保存在云点播平台,云点播将根据您的使用情况收取存储费用和观看费用
![图片](/api/project/8779773/files/26147962/imagePreview)
![图片](/api/project/8779773/files/26147974/imagePreview)
![图片](/api/project/8779773/files/26147984/imagePreview)
也就是说一个录制视频时长为 700s、视频大小约 1G 的视频。需要花费约 (700s / 60)[视频分钟数] * 14[服务定价] = 163 元的云端录制金额 + 0.0048元/日 的媒资存储 + 1000次观看/日 而产生的流量费用 1000 × 1GB × 0.23元/GB = 230元。合计约为 393.0048 /日
![图片](/api/project/8779773/files/26147983/imagePreview)
方案② 浏览器录屏 recordrtc
介绍
可使用 RecordRTC 进行音频 + 视频 + 屏幕录制
![图片](/api/project/8779773/files/26147987/imagePreview)
工作量估算
前端侧: 2~3 day
服务端侧:包含视频上传、下载、转码等,需服务端人员自行评估
Demo 原型
https://www.webrtc-experiment.com/RecordRTC/simple-demos/screen-recording.html
落地方案
requestUserMedia() {
console.log('requestUserMedia')
captureUserMedia((stream) => {
this.setState({ src: window.URL.createObjectURL(stream) });
console.log('setting state', this.state)
});
}
startRecord() {
captureUserMedia((stream) => {
this.state.recordVideo = RecordRTC(stream, { type: 'video' });
this.state.recordVideo.startRecording();
});
setTimeout(() => {
this.stopRecord();
}, 4000);
}
stopRecord() {
this.state.recordVideo.stopRecording(() => {
let params = {
type: 'video/webm',
data: this.state.recordVideo.blob,
id: Math.floor(Math.random()*90000) + 10000
}
this.setState({ uploading: true });
S3Upload(params)
.then((success) => {
console.log('enter then statement')
if(success) {
console.log(success)
this.setState({ uploadSuccess: true, uploading: false });
}
}, (error) => {
alert(error, 'error occurred. check your aws settings and try again.')
})
});
}
疑惑点
录制视频是否能正常下载播放?
能正常下载播放
是否存在编码问题?
暂未发现,可能存在编码问题
录制是实时录制的?是形成一段视频流还是录制完成后形成一个视频文件---这点在demo中看到自动就生成一个播放链接,而且生成的速度几块,几乎没有压缩和转码,所以需要详细了解录制原理;
分为两个部分,将两个媒体源合为一个,在录制结束后形成一个视频文件(默认格式 mp4)
● 实时屏幕录制
● 实时录音(通过麦克风
录制视频大小?尝试抓取demo播放链接中的视频文件,但是没有成功,这边需要确认录制视频的质量大小,特别是在我们指挥调度视频为画面快速变化的,可能使视频变大---可以使用一个内嵌视频网站页面对视频播放进行录制,确认已嗲视频录制大小;
视频质量方面,可在录制前设置帧率、采样率
存储位置:可以储存在本机?还是一定要储存在服务端,如果储存在服务端,对上行带宽是不是有影响,如果不是实时传输至服务端,浏览器关闭是不是会中断上传
目前实现方式为浏览器直接下载,存储在本机。视频过大暂未发现问题
对浏览器的性能要求:我们的防控指挥只有几个吃浏览器性能的模块(地图、实时音视频等)再开发一个录屏是不是对电脑性能的要求更高;
关于录屏方面,浏览器选择上有硬性要求。Chrome > 72, Edge > 79, Firefox > 68, IE 不支持, Opera > 60, Safari > 13 版本。
视频过大等产生的问题暂时未做测试,于明日测试
demo中没有录制音频,请确认一下是否可以录制音频
可以录制音频。虽然 RecordRTC 并未支持通过麦克风录音,但今天将媒体源合而为一后,问题不再存在。
demo介绍中提到还可以录制非浏览器的电脑页面(腾讯实时音视频的共享应该也是用类似方案实现的)这点需确认
是的。本质 RecordRTC 是录制全屏。
在官网规范中明确表示用户代理必须让终端用户每次都从所有可选项中来选择共享哪个页面,严禁使用约束来限制选择。故需要引导用户操作,最好有操作手册说明。
录制时如果不断有人员接入,会有影响吗?
不会
下载视频的目录可以控制吗?是下载到桌面吗?
下载是下载到浏览器下载目录的。这个目录他可以自己修改的。
如果上传到服务器,可以边录边传吗?
能做边录边传,但需要服务端进行文件整合。如果服务端用的 websocket ,那可以将视频流在录制结束后导出为文件。
录制完成后多久会生成视频?
录制后是立即生成的。是浏览器的临时文件
附关键代码:
import React from 'react';
import { captureUserMedia, S3Upload, checkUserMedia, checkVideoCode, mergeAudioStreams } from './AppUtils';
import Webcam from './Webcam.react';
import RecordRTC from 'recordrtc';
import { Modal } from 'react-bootstrap';
// import 'mui-player/dist/mui-player.min.css'
import MuiPlayer from 'mui-player'
const hasGetUserMedia = !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia || navigator.msGetUserMedia);
class RecordPage extends React.Component {
constructor(props) {
super(props);
this.state = {
recordVideo: null,
muiPlayer: null,
src: null,
desktopStream: null, // 屏幕画面&系统声音
voiceStream: null, // 麦克风声音
aherf: null,
tracks: null,
uploadSuccess: null,
uploading: false
};
this.requestUserMedia = this.requestUserMedia.bind(this);
this.startRecord = this.startRecord.bind(this);
this.stopRecord = this.stopRecord.bind(this);
this.Download = this.Download.bind(this);
}
componentDidMount() {
if(!hasGetUserMedia) {
alert("Your browser cannot stream from your webcam. Please switch to Chrome or Firefox.");
return;
}
this.requestUserMedia();
}
requestUserMedia() {
console.log('requestUserMedia')
checkUserMedia((stream) => {
this.state.voiceStream = stream;
console.log('checkUserMedia', stream)
document.getElementById("audio").srcObject = stream;
});
}
startRecord() {
captureUserMedia(stream => {
this.state.desktopStream = new MediaStream(stream);
const track = this.state.desktopStream.getVideoTracks()[0];
// imageCapture = new ImageCapture(track);
console.log('captureUserMedia', this.state.desktopStream);
this.state.tracks = [
...track,
...mergeAudioStreams(this.state.desktopStream, this.state.voiceStream)
];
console.log('mergeAudioStreams', mergeAudioStreams(this.state.desktopStream, this.state.voiceStream));
// this.state.desktopStream.addTrack(mergeAudioStreams(this.state.desktopStream, this.state.voiceStream)[0])
this.state.desktopStream.addTrack(this.state.voiceStream.getAudioTracks()[0]);
this.state.recordVideo = RecordRTC(this.state.desktopStream, {
type: 'video',
});
this.state.recordVideo.startRecording();
let binaryData = [];
binaryData.push(stream);
let url = (URL || webkitURL).createObjectURL(new Blob(binaryData, { type: "video/mp4" }));
this.setState({ src: url });
// this.state.muiPlayer = new MuiPlayer({
// container:'#mui-player',
// title:'标题',
// src: null,
// })
// this.state.muiPlayer.video.srcObject = stream;
document.getElementById("video").srcObject = stream;
});
// setTimeout(() => {
// this.stopRecord();
// }, 4000);
}
stopRecord() {
this.state.recordVideo.stopRecording(() => {
console.log('this.state.tracks', this.state.tracks);
let params = {
type: 'video/mp4',
data: this.state.recordVideo.blob,
id: Math.floor(Math.random()*90000) + 10000
}
document.getElementById("video").srcObject = null;
this.setState({ uploading: true, src: null });
let binaryData = [];
binaryData.push(this.state.recordVideo.blob);
let url = (URL || webkitURL).createObjectURL(new Blob(binaryData, { type: "video/mp4" }));
// let url = (URL || webkitURL).createObjectURL(new Blob(this.state.tracks, { type: "video/mp4" }));
this.setState({ src: url });
console.log('stopRecord', params, this.state.recordVideo);
S3Upload(params)
.then((success) => {
console.log('enter then statement')
if(success) {
console.log(success)
this.setState({ uploadSuccess: true, uploading: false });
}
}, (error) => {
alert(error, 'error occurred. check your aws settings and try again.')
})
});
}
Download() {
let binaryData = [];
binaryData.push(this.state.recordVideo.blob);
let url = (URL || webkitURL).createObjectURL(new Blob(binaryData, { type: "video/mp4" }));
// let url = (URL || webkitURL).createObjectURL(new Blob(this.state.tracks, { type: "video/mp4" }));
let fileName = new Date().getTime() + ".mp4";
const aLink = document.createElement('a');
document.body.appendChild(aLink);
aLink.style.display='none';
aLink.href = url;
aLink.download = fileName;
aLink.click();
document.body.removeChild(aLink);
window.URL.revokeObjectURL(url);
}
Play(){
var recvideo = document.querySelector('video#video');
let binaryData = [];
binaryData.push(this.state.recordVideo.blob);
let url = (URL || webkitURL).createObjectURL(new Blob(binaryData, { type: "video/mp4" }));
// let url = (URL || webkitURL).createObjectURL(new Blob(this.state.tracks, { type: "video/mp4" }));
// checkVideoCode(new Blob(binaryData, { type: "video/mp4" }))
recvideo.src = url;
recvideo.srcObject = null;
recvideo.controls = true;
recvideo.volume = 1;
recvideo.play();
}
render() {
return(
<div>
<Modal show={this.state.uploadSuccess}><Modal.Body>Upload success!</Modal.Body></Modal>
<div><Webcam src={this.state.src}/></div>
<div id="mui-player"></div>
{this.state.uploading ?
<div>Uploading...</div> : null}
<div>
<button onClick={this.startRecord}>Start Record</button>
<button onClick={this.stopRecord}>End Record</button>
<button onClick={this.Download}>Download</button>
<button onClick={this.Play.bind(this)}>Play</button>
</div>
</div>
)
}
}
export default RecordPage;
标签:方案,let,stream,视频,录制,state,录屏,三种,video 来源: https://blog.csdn.net/weixin_48141737/article/details/122438189