chrome有API接口,可以获取摄像头和麦克风。理论上可以做 语音/视频聊天/ 浏览器直播主播端。
因为网上录制视频的软件都要钱,而且 怕有毒,所以用chrome撸了一个。浏览器打开,录制完毕直接下载导出
视频链接
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>本地屏幕录制</title>
<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
<!-- bootstrap.bundle.min.js 用于弹窗、提示、下拉菜单,包含了 popper.min.js -->
<script src="https://cdn.staticfile.org/popper.js/1.15.0/umd/popper.min.js"></script>
<!-- 最新的 Bootstrap4 核心 JavaScript 文件 -->
<script src="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
</head>
<body style="height: 100%">
<div style="display: flex;justify-content: center;align-items: center;margin-top: 10vh">
<div style="text-align: center;background-color: #9a9a9a;width: 100%;display: block">
<video id="livePlayer" style="max-width: 800px;width: 100%;min-height: 50%" ></video>
<audio id="liveAudio"></audio>
<div>
<video id="recordPlayback"
style="max-width: 800px;width: 100%;min-height: 50%"
controls playsinline autoplay hidden >
</video>
</div>
</div>
</div>
<div style="text-align: center">
<div style="display: block;">
<div id="recordControls">
<p>
<input name="closePlayerAudio" value="1" checked type="radio"/>回声消除
<input name="closePlayerAudio" value="0" type="radio"/>播放回声
<label for="frameRate">帧数FPS</label>
<input id="frameRate" value="100" type="text" />
</p>
<span id="recordStatus"></span>
<button class="recordBtn btn btn-primary" id="recordStart">开始</button>
<button class="recordBtn btn btn-warning" id="recordPause" disabled>暂停</button>
<button class="recordBtn btn btn-danger" id="recordStop" disabled>结束</button>
<button class="recordBtn btn btn-info" id="recordPlay" disabled>播放</button>
<button class="recordBtn btn btn-success" id="recordSave" disabled>下载</button>
</div>
</div>
</div>
</body>
<script >
let recorder;
let recordingData = [];
let recorderStream;
/**
* 混合音频
* */
function mixer(stream1, stream2) {
const ctx = new AudioContext();
const dest = ctx.createMediaStreamDestination();
if(stream1.getAudioTracks().length > 0)
ctx.createMediaStreamSource(stream1).connect(dest);
if(stream2.getAudioTracks().length > 0)
ctx.createMediaStreamSource(stream2).connect(dest);
let tracks = dest.stream.getTracks();
tracks = tracks.concat(stream1.getVideoTracks()).concat(stream2.getVideoTracks());
return new MediaStream(tracks)
}
/**
* 根据URL和时间戳中的Jitsi房间名称返回文件名
*
*/
function getFilename(){
const now = new Date();
const timestamp = now.toISOString();
const room = new RegExp(/(^.+)\s\|/).exec(document.title);
if(room && room[1]!=="")
return `${room[1]}_${timestamp}`;
else
return `recording_${timestamp}`;
}
/**
* 检查浏览器支持的编码
*
*/
function checkCodecsSupported(){
let options = {mimeType: 'video/webm;codecs=vp9,opus'};
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
console.error(`${options.mimeType} is not supported`);
options = {mimeType: 'video/webm;codecs=vp8,opus'};
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
console.error(`${options.mimeType} is not supported`);
options = {mimeType: 'video/webm'};
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
console.error(`${options.mimeType} is not supported`);
options = {mimeType: ''};
}
}
}
return options;
}
/**
* 开始录制
* */
const start = document.getElementById('recordStart');
const frameRate = document.getElementById('frameRate');
start.addEventListener('click', async ()=> {
const options = checkCodecsSupported();
let gumStream, gdmStream;
recordingData = [];
try {
gumStream = await navigator.mediaDevices.getUserMedia({video: false, audio: {
echoCancellation: {exact: true}
}});
gdmStream = await navigator.mediaDevices.getDisplayMedia({video: {width: 1280, height: 720,displaySurface: "browser",frameRate: frameRate.value > 0 ? frameRate.value : 15}, audio: {
echoCancellation: true
}})
} catch (e) {
console.error("capture failure", e);
return
}
recorderStream = gumStream ? mixer(gumStream, gdmStream) : gdmStream;
var liveVideo=document.getElementById("livePlayer");
liveVideo.srcObject = gdmStream;
liveVideo.play();
let radio=document.getElementsByName("closePlayerAudio");
radio.forEach(v=>{
if(v.checked==true){
if(v.value==0){
var liveAudio=document.getElementById("liveAudio");
liveAudio.srcObject = recorderStream;
liveAudio.play();
}
}
})
recorder = new MediaRecorder(recorderStream, options);
recorder.ondataavailable = e => {
if (e.data && e.data.size > 0) {
recordingData.push(e.data);
}
};
recorder.onStop = () => {
recorderStream.getTracks().forEach(track => track.stop());
gumStream.getTracks().forEach(track => track.stop());
gdmStream.getTracks().forEach(track => track.stop());
};
recorderStream.addEventListener('inactive', () => {
console.log('Capture stream inactive');
stopCapture();
});
recorder.start();
console.log("started recording");
start.innerText = "录屏中..";
start.disabled = true;
pause.disabled = false;
stop.disabled = false;
play.disabled = true;
save.disabled = true;
});
/**
* 停止录制
*/
const stop = document.getElementById('recordStop');
function stopCapture(){
console.log("Stopping recording");
recorder.stop();
start.disabled = false;
pause.disabled = true;
stop.disabled = true;
play.disabled = false;
save.disabled = false;
start.innerText = "开始";
pause.innerText = "暂停";
}
stop.addEventListener('click', stopCapture);
/**
* 暂停录制
*/
const pause = document.getElementById('recordPause');
pause.addEventListener('click', ()=>{
if(recorder.state ==='paused'){
recorder.resume();
pause.innerText = "暂停"
}
else if (recorder.state === 'recording'){
recorder.pause();
pause.innerText = "继续"
}
else
console.error(`recorder in unhandled state: ${recorder.state}`);
console.log(`recorder ${recorder.state === 'paused' ? "paused" : "recording"}`);
});
/**
*
* 播放录制
*/
let isPlaying = false;
const play = document.getElementById('recordPlay');
play.addEventListener('click', ()=>{
var p=document.getElementById("livePlayer");
playback.hidden = !playback.hidden;
p.hidden=!playback.hidden;
if (!isPlaying && !playback.hidden){
playback.src = window.URL.createObjectURL(new Blob(recordingData, {type: 'video/webm'}));
playback.play();
play.innerText = "停止播放";
}
else{
play.innerText = "播放录制";
}
});
const playback = document.getElementById('recordPlayback');
// 添加处理事件
playback.addEventListener('play', ()=>{isPlaying = true});
playback.addEventListener('pause', ()=>{isPlaying = false});
playback.addEventListener('playing', ()=>{isPlaying = true});
playback.addEventListener('ended', ()=>{isPlaying = false});
/**
* 保存录制
* */
const save = document.getElementById('recordSave');
save.addEventListener('click', () => {
const blob = new Blob(recordingData, {type: 'video/webm'});
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = `${getFilename()}.webm`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
console.log(`${a.download} save option shown`);
}, 100);
});
</script>
</html>
(客户端)
哎呀真大神呀。好奇能不能录浏览器以外的?还是只能录浏览器操作
小米8(白)
厉害
小米MIX2s(白)
obs studio
============
楼主真是神豪,,用mac
我们有着共同的敌人——天命;
你却认命了,还对我说天命不可违
https://www.jianguoyun.com/p/Dfttx3sQv7TOCBjZ3O0D
我们有着共同的敌人——天命;
你却认命了,还对我说天命不可违
@胡椒舰长,大部分主播和UP主都使用OBS Studio进行录制和推流,也推荐你使用。
只要看一个主播的直播看得久,总有机会看到他调OBS Studio窗口。PDD、装机猿等知名主播都在直播期间调整过他们的OBS Studio窗口,足见该软件的市场占有率多高。
不过不用担心该软件处于市场支配地位有什么不好之处,因为它和Linux内核一样是以GPL协议开源的自由软件,所有人都可以自由获取软件的安装包和源代码并用于任何用途。
比如,B站的官方直播软件“直播姬”就是OBS改的,而且他们也按GPL的要求提供了修改后的源代码。
OBS不仅可以直播推流,也支持视频录制。无论是摄像头、采集卡还是屏幕录制,OBS都能给出专业的解决方案,就算三者同时进行,同时再推流,OBS也能出色完成。
此外,OBS还支持GPU加速视频编码,无论是A/N/I哪家的显卡/集显都能兼容。显卡的视频编码加速通常使用专用电路实现(和3D不是同一个处理器),所以启用后相当于OBS视频编码既不占用CPU也不占用显卡3D图形性能,在录制游戏时对性能的影响就可以降到最小。对于CPU性能一般又不想搭建双机采集环境的主播来说该功能相当重要。如果直接使用CPU编码视频,可能他们根本没办法流畅播出游戏,因为视频编码会占用大部分CPU,导致游戏卡顿。
而且最好的一点是,OBS支持Windows、Linux、macOS。所以无论你使用什么电脑、什么操作系统,你总有机会用上它。
免费的,最不贵。大主播都在用。
@老虎会游泳,了解,我一直用的付费版,花了不少学费
牛逼
小米8高配版(五颜六色)
@胡椒舰长,你用过什么付费版