前面我们能打开本地摄像头,并且在网页上看到摄像头的预览图像。

本文我们使用MediaRecorder来录制视频。在网页上播放录制好的视频,并能提供下载功能。

html

首先创建一个html界面,放上一些元素

    <video id="v1" playsinline autoplay muted></video>
<video id="v2" playsinline loop></video> <div>
<button id="startCamera">开启摄像头</button>
<button id="stopCamera">停止摄像头</button>
<button id="record" disabled>录制</button>
<button id="play" disabled>播放</button>
<button id="download" disabled>下载视频</button>
</div>
<div> 录制使用的视频格式: <select id="codecSelect" disabled></select> </div>
<div>
<h4>视频设置</h4>
<p>回声消除: <input type="checkbox" id="echoCancellation"></p>
</div>
<div> <span id="msg" style="font-size:smaller"></span> </div> <!-- 使用本地的适配器 -->
<script src="../js/adapter-latest.js" async></script>
  • video

    • v1 用来预览
    • v2 用来播放录制好的视频
  • button 控制摄像头开启、录制,下载等等
  • select 选择录制用的视频格式
  • input 选择回声消除

js

准备

先把界面上的元素拿到

'use strict';

let mediaRecorder;
let recordedBlobs; // 录制下来的内容
let isRecording = false; // 先把页面元素拿到
const startCameraBtn = document.querySelector('button#startCamera'); // 启动摄像头按钮
const stopCameraBtn = document.querySelector('button#stopCamera');
const recordBtn = document.querySelector('button#record'); // 开始录制按钮
const playBtn = document.querySelector('button#play'); // 播放按钮
const downloadBtn = document.querySelector('button#download'); // 下载视频按钮
const codecSelector = document.querySelector('#codecSelect'); // 选择格式
const msgEle = document.querySelector('span#msg'); // 显示消息
const previewV1 = document.querySelector('video#v1'); // 预览用的
const recordedV2 = document.querySelector('video#v2'); // 用来播放录制好的视频

视频支持的格式

先预定几个可能的格式,然后一个个来判断是否支持。找到支持的格式。

function getSupportedMimeTypes() {
const possibleTypes = [
'video/webm;codecs=vp9,opus',
'video/webm;codecs=vp8,opus',
'video/webm;codecs=h264,opus',
'video/mp4;codecs=h264,aac',
];
return possibleTypes.filter(mimeType => {
return MediaRecorder.isTypeSupported(mimeType);
});
}

开启摄像头

同样要使用getUserMedia方法。这里给视频指定了宽高。回声消除是可选项。

// 启动摄像头
startCameraBtn.addEventListener('click', async () => {
startCameraBtn.disabled = true;
const isEchoCancellation = document.querySelector('#echoCancellation').checked;
const constraints = {
audio: {
echoCancellation: { exact: isEchoCancellation }
},
video: {
width: 1280, height: 720
}
};
await init(constraints);
}); async function init(constraints) {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
gotStream(stream);
} catch (e) {
showMsg(`navigator.getUserMedia error:${e.toString()}`);
}
} function gotStream(stream) {
recordBtn.disabled = false;
showMsg('拿到了 stream:', stream);
window.stream = stream;
previewV1.srcObject = stream; // 重置
var codecOption = codecSelector.lastChild;
while (codecOption != null) {
codecSelector.removeChild(codecOption);
codecOption = codecSelector.lastChild;
} getSupportedMimeTypes().forEach(mimeType => {
const option = document.createElement('option');
option.value = mimeType;
option.innerText = option.value;
codecSelector.appendChild(option);
});
codecSelector.disabled = false; // 可以进行选择了
}

下面是停止摄像头的方法

stopCameraBtn.addEventListener('click', () => {
var stream = previewV1.srcObject;
if (stream == null) {
return;
}
const tracks = stream.getTracks();
tracks.forEach(function (track) {
track.stop();
});
previewV1.srcObject = null;
window.stream = null;
codecSelector.disabled = true;
startCameraBtn.disabled = false;
});

开始录制

开始录制视频

function startRecording() {
recordedBlobs = [];
const mimeType = codecSelector.options[codecSelector.selectedIndex].value;
const options = { mimeType }; try {
mediaRecorder = new MediaRecorder(window.stream, options);
} catch (e) {
showMsg(`创建MediaRecorder出错: ${JSON.stringify(e)}`);
return;
} showMsg('创建MediaRecorder', mediaRecorder, ' -> options', options);
recordBtn.textContent = '停止录制';
isRecording = true;
playBtn.disabled = true;
downloadBtn.disabled = true;
codecSelector.disabled = true;
mediaRecorder.onstop = (event) => {
showMsg('录制停止了: ' + event);
showMsg('录制的数据Blobs: ' + recordedBlobs);
};
mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.start();
showMsg('录制开始 mediaRecorder: ' + mediaRecorder);
} function handleDataAvailable(event) {
if (event.data && event.data.size > 0) {
recordedBlobs.push(event.data);
}
} recordBtn.addEventListener('click', () => {
if (isRecording == false) {
startRecording();
} else {
stopRecording();
recordBtn.textContent = '开始录制';
playBtn.disabled = false;
downloadBtn.disabled = false;
codecSelector.disabled = false;
}
});
  • 重置录制内容recordedBlobs = []
  • 拿到选定的视频格式mimeType
  • 新建MediaRecorder对象,传入前面获取到的流
  • 处理各个按钮(ui)的状态
  • mediaRecorder
    • 设置停止监听器 onstop
    • 监听录制数据 ondataavailable,有数据来的时候存放在recordedBlobs
    • 启动录制 mediaRecorder.start()

停止录制

function stopRecording() {
mediaRecorder.stop();
}

播放录制好的视频

录制好的视频内容存放在recordedBlobs。新建Blob,交给video(recordedV2)来播放

playBtn.addEventListener('click', () => {
const mimeType = codecSelector.options[codecSelector.selectedIndex].value.split(';', 1)[0];
const superBuffer = new Blob(recordedBlobs, { type: mimeType });
recordedV2.src = null;
recordedV2.srcObject = null;
recordedV2.src = window.URL.createObjectURL(superBuffer);
recordedV2.controls = true;
recordedV2.play();
});

下载视频

录制好的视频内容存放在recordedBlobs

downloadBtn.addEventListener('click', () => {
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 = '视频_' + new Date().getTime() + '.webm';
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
});

新建Blob和一个a元素。根据blob创建ObjectURL,并传给a元素的href。

修改下载文件的默认名字a.download

触发a元素的click(),即能让浏览器下载这个文件。

延迟把这个a移除掉。

小结

getUserMedia()开启视频拿到视频流。MediaRecorder录制视频。用Blob来播放和下载。

实现一个小的录制视频效果。视频数据缓存在对象里。

完整的效果请参考 视频录制

原文链接

WebRTC网页打开摄像头并录制视频的更多相关文章

  1. Android切换前后置摄像头并录制视频

    项目需要对微信的视频模块也看了一下,在此就对这块进行了一个开发.首先给出效果图 首先给出java代码 /** * RecordActivity.java * 版权所有(C) 2013 * 创建:cui ...

  2. HTML5网页打开摄像头,并拍照

    谷歌提高了安全要求,要摄像头必须用https 效果图:

  3. DxPackNet 1.打开摄像头

    好久没写博客了 ,这个系列将给大家介绍.net下一个非常好用的视频控件 ------ DxPackNet, 用这个控件大家可以轻松开发出 视频会议,视频监控,远程桌面,远程教学,远程白板,视频直播,视 ...

  4. OpenCV x64 vs2010 下打开摄像头录制视频写成avi(代码为转载)

    首先参照下面这里进行opencv x64位机器下面的配置 http://wiki.opencv.org.cn/index.php/VC_2010%E4%B8%8B%E5%AE%89%E8%A3%85O ...

  5. Android IOS WebRTC 音视频开发总结(八十五)-- 使用WebRTC广播网络摄像头视频(下)

    本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...

  6. Android IOS WebRTC 音视频开发总结(八十三)-- 使用WebRTC广播网络摄像头视频(上)

    本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...

  7. Android开发之打开闪光灯录制视频

    Android的SDK在线API上对录制视频的方法.步骤都写得非常清楚,但是如果没有一点思路,写起来也比较式费事.录制视频的全过程要打开闪光灯(可能是因为项目需要,或者特殊原因),则必须按照一定的顺序 ...

  8. 网页播放摄像头视频一种新的实现方式(非ocx方式)

    前言 出于安全性考虑,浏览器对网页调用本地资源做了诸多限制.单纯的js是不能调用本地摄像头的,最常用的解决方案是通过ocx来实现.ocx是IE浏览器的扩展插件,并不是通用标准,很多浏览器并不支持ocx ...

  9. Delphi - 利用DLL编程控制摄像头实现拍照、录制视频

    Delphi利用avicap32.dll编程控制摄像头实现拍照.录制视频 项目需求:平板电脑(Windows系统)一维/二维码扫描功能: 需求分析: 需要扫描一维/二维码时,分两步实现. 第一步,av ...

随机推荐

  1. Noip模拟8 2021.6.17

    T1 星际旅行 仔细一看,发现像一个欧拉路(简称一笔画). 满足"可以一笔画"的条件是: 1.所有点都有偶数条连边; 2.有偶数个点连奇数条边; 满足以上两个条件的任意一个即可一笔 ...

  2. 2021.8.21考试总结[NOIP模拟45]

    T1 打表 由归纳法可以发现其实就是所有情况的总和. $\frac{\sum_{j=1}^{1<<k}(v_j-v_{ans})}{2^k}$ $code:$ 1 #include< ...

  3. Kioskcached(2) 之 使用tcmalloc 替换 ptmalloc

    前言 我在 Kioskcached(1)之 Memcached & Redis & Kioskcached 性能测试对比 中找到的一个问题是 malloc,对于一个内存型数据库,很容易 ...

  4. js判断是否是同一域名

    可以判断自己的网页是否是嵌入别的网页中 /** * 是否相同域名 * @returns {boolean} * @constructor */ function SameDomain() { try ...

  5. linux下go环境配置

    环境申明: centos 7.4 1.go下载最新的版本(linux) 下载本地后rz到服务器,然后tar -zxvf  go1.9.2.linux-amd64.tar.gz   解压出go文件放在 ...

  6. 基于霸道秉火的STM32F103ZET6嵌入式开发之------基于定时TIM3的PWM实验

    1:PWM脉冲宽度调制 STM32 的定时器除了 TIM6 和 7.其他的定时器都可以用来产生 PWM 输出.其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出.而通用定 ...

  7. 3D 穿梭效果?使用 UWP 也能搞定

    昨天 ChokCoco 大佬搞了个 3D 穿梭效果出来,具体可见这里: 3D 穿梭效果?使用 CSS 轻松搞定 这个效果太神奇了,他还问我能不能用 WPF 搞出来,因为我完全没用过 WPF 的 3D, ...

  8. Docker多机网络

    前言 前面的文章主要聚焦于单机网络上,对于生产环境而言,单机环境不满足高可用的特点,所以是不具备上生产的条件,因此在开始Docker Swarm篇的时候我们先来聊聊多机网络之间Docker的通信如何做 ...

  9. Spring中bean的初始化和销毁几种实现方式

    Bean的生命周期 : 创建bean对象 – 属性赋值 – 初始化方法调用前的操作 – 初始化方法 – 初始化方法调用后的操作 – --- 销毁前操作 – 销毁方法的调用. [1]init-metho ...

  10. 菜鸡的Java笔记 第三十一 扩展结构

    .新特性        可变参数的意义以及实现                那么下面通过一个简单的思考来分析可变参数的存在意义        范例:如果说现在要定义一个方法,这个方法可以实现任意多个 ...