JS基于页面实现音视频的录制(一)

前言
音频与视频信息的捕捉一直是Web开发中的一个难点。许多年来,我们一直依赖浏览器插件来实现这个需求,随着HTML 5推出,这种情况有所改变。因为,HTML5提供了许多可以访问硬件设备的API,例如:访问GPS设备的Geolocation API、访问accelerometer设备的Orientation API、访问GPU设备的WebGL API、访问音频播放设备的Web Audio API等等。这些API功能非常强大,使用这些API,开发者可以直接通过编写JavaSccript脚本代码来访问底层硬件设备,这也使得通过网页实现音视频的录制变成现实。

1.1 H5网页实现音视频录制的方法

H5网页实现录音(像)的方法有以下几种:

第一种方法:依赖于浏览器插件(Flash 或 Silverlight)实现。

第二种方法:通过Web Api中的MediaRecorder接口实现。MediaRecorder API由w3c制定并致力推进,但ios和微软反应冷淡,所以目前的兼容性都不理想。目前,主要的试验田在chrome和firefox,移动端兼容安卓内置的chrome内核浏览器,Safari/Edge等浏览器一直没有实现。

第三种方法:通过Web Api中的MediaDevices接口提供的getUserMedia方法,并结合AudioContext接口有关方法实现。这个方法涉及到WebRTC的一系列Web API,从采集、编码到通信层面都有,相对要复杂得多,但兼容性好,已经得到了所有主流浏览器的支持。

1.2 使用MediaRecorder类实现前端音视频录制

1.2.1 MediaRecorder类能够录制哪些数据源?
任何媒体形式的标签,对应的数据源都可以录制成音视频。包括 <audio>, <video>,<canvas>, 其中 <audio>, <video>可以来自网络媒体文件,也可以来自本机设备采集。而<canvas>的内容则更加自由,任何绘制在画布上的用户操作,2d或3d图像,都可以进行录制。它为web提供了更多可能性,我们甚至可以把一个h5游戏流程录成视频,保存落地或进行实况传输。

1.2.2 MediaRecorder类录制出来是什么格式的数据?
是经过标准编码后的媒体流数据,可以注入video标签,也可以打包生成文件,还可以进一步做流级别的数据处理,比如画面识别、动态插入内容、播放跳转控制等等。

1.2.3 MediaRecorder类常用的方法
使用MediaRecorder录音录像时,需要严格遵守API说明中的函数调用先后顺序,否则不能成功执行。MediaRecorder类的常用方法:

· MediaRecorder() 构造方法

· getMaxAmplitude() 得到目前为止最大的幅度

· prepare() 准备录音机

· release() 释放MediaRecorder对象

· reset() 重置MediaRecorder对象,使其为空闲状态

· setAudioEncoder() 设置音频编码

· setAudioSource() 设置音频源

· setCamera() 设置摄像机

· setMaxDuration() 设置最大期限

· setMaxFileSize() 设置文件的最大尺寸

· setOnErrorListener() 错误监听

· setOutputFile() 设置输出文件

· setOutputFormat() 设置输出文件的格式

· setPreviewDisplay() 设置预览

· setVideoEncoder() 设置视频编码

· setVideoFrameRate() 设置视频帧的频率

· setVideoSize() 设置视频的宽度和高度(分辨率)

· setVideoSource() 设置视频源

· start() 开始录制

· stop() 停止录制

1.2.4 MediaRecorder录制音视频的主要流程和代码
在这里插入图片描述

① 打开摄像头

HTML5的getUserMedia API为用户提供访问硬件设备媒体(摄像头、视频、音频、地理位置等)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件下访问硬件媒体设备。

语法:navigator.mediaDevices.getUserMedia(constraints,onSuccess,onError);

containers:指定请求的媒体类型,主要包含video和audio,至少要有一个类型被指定。

onSuccess:onSuccess是一个自定义的函数。当正确打开摄像头或麦克风,并捕获到视(音)频流时,回调此函数。视频流会作为默认参数传递给onSuccess。

onError:onError是一个自定义函数。如果浏览器无法找到指定的媒体类型或者无法满足相对应的参数要求,回调此函数。错误信息会作为默认参数传递给onError。

下面一段代码展现了getUserMedia接口与相关参数具体使用方法。

//不同的浏览器访问硬件设备的方法不同,首先要找到与浏览器匹配的方法
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

let constrains = {     //用来定义音视频格式、状态
    audio:true,
    video:true
}

if (navigator.getUserMedia) {  //判断是否找到与浏览器匹配的方法
    navigator.getUserMedia(constrains, onSuccess, onError);   //打开摄像头或麦克风,并获取视频流
} else {
    alert("你的浏览器无法访问到用户的媒体设备");
}
        
function onSuccess(stream) {    //摄像头正常打开时,回调此函数
     console.log(stream);
}

function onError(error) {      //调用摄像头异常时,回调此函数
    console.log("访问用户媒体设备失败:", error.name, error.message);
}

② 展示视频

getUserMedia API成功打开摄像头,捕获到的媒体流stream是blob数据,将它赋值给标签的srcObject,就能够直接将摄像头的视频展示出来。

<body>
  <video id="video" width="500" height="500" autoplay></video>
</body>

JS代码

//调用媒体设备成功时的回调函数
function onSuccess(stream){
    oVideo.srcObject = stream;  //将视频流设置为video元素的源,直接播放视频
}

③ 从摄像头采集视频

现在,我们只是从摄像头获取了图像,并把它展现了出来,但还无法控制它,也无法把图像采集下来。怎么才能实现视频录制呢,这就要用到MediaRecorder对象,首先创建MediaRecorder对象。

语法: var mediaRecorder = new MediaRecorder(stream,option)

说明:MediaRecorder接收两个参数,第一个是stream音视频流。第二个是option配置参数,option参数包括音频码率、视频码率、编码格式。下面给一个参考值,必要时大家可查阅相关资料具体设置。

  var mediaRecorder = new MediaRecorder(stream, {
    audioBitsPerSecond : 128000,  // 音频码率
    videoBitsPerSecond : 100000,  // 视频码率
    mimeType : 'video/webm;codecs=h264' // 编码格式
  })

为了完成音视频的采集,MediaRecorder提供了三个重要的方法:
MediaRecorder.start(): 开始录制视频。
MediaRecorder.stop(): 停止录制视频, 同时触发dataavailable事件。停止录制视频后,其后出现的视频不再被记录。
ondataavailable事件: MediaRecorder.stop触发该事件,该事件可用于获取记录的视频数据。
通俗地理解,当MediaRecorder.start()这个方法被调用时,便开始记录视频数据。当MediaRecorder.stop() 方法被调用时,立即停止记录视频数据,同时触发dataavailable事件。这时候用户便可以从该事件的data属性,取出录制的视频数据(Blob数据)。

最后源代码

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <script>
        window.onload=function(){

            //访问用户媒体设备(摄像头和麦克风)的兼容方法
            navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

            if (navigator.getUserMedia){  //判断浏览器是否支持访问媒体设备
                navigator.getUserMedia({video:true,audio:true},onSuccess,onError);   //打开摄像头或麦克风,并获取视频流
            } else {
                alert("你的浏览器不支持访问用户媒体设备");
            }


            var oVideo = document.getElementById("video");
            var oStart = document.getElementById("start");
            var oStop = document.getElementById("stop");
            var oSave = document.getElementById("save");
            var blob;   //采集的Blob格式的视频数据

            //调用媒体设备成功时的回调函数
            function onSuccess(stream){
                oVideo.srcObject = stream;  //将视频流设置为video元素的源,直接播放视频
                var mediaRecorder = new MediaRecorder(stream,{ //创建MediaStream 对象用来进行视频录制操作,有两个参数第一个是stream音视频流,第二个是配置参数
                    audioBitsPerSecond : 128000,  // 音频码率
                    videoBitsPerSecond : 100000,  // 视频码率
                    mimeType : 'video/webm;codecs=h264' // 编码格式
                });

                oStart.onclick = function(){  //开始录像
                    mediaRecorder.start();
                }

                oStop.onclick = function(){   //停止录像
                    mediaRecorder.stop();
                }

                oSave.onclick =function(){   //保存视频
                    // 下载视频
                    let a = document.createElement('a')
                    a.href = URL.createObjectURL(blob)
                    a.download = `test.mp4`
                    a.click()
                }

                  // 事件,开始录像时捕获数据,结束录像时将捕获的数据,传递到BLOB中,当此动作完成后,触发ondataavailable
                  mediaRecorder.ondataavailable = function (e) {
                      blob = new Blob([e.data], { 'type' : 'video/mp4' })
                  }
            }

            //调用媒体设备异常时的回调函数
            function onError(error){
                console.log("访问用户媒体设备失败:",error.name,error.message);
            }
        }
    </script>
</head>
<body>
    <!--video用于显示媒体设备的视频流,自动播放-->
    <video id="video" autoplay style="width: 600px;height: 450px"></video>
    <br>
    <input type="button" value="开始录像" id="start" />
    <input type="button" value="停止录像" id="stop" />
    <input type="button" value="保存视频" id="save" />
</body>
</html>

1.3 使用AudioContext实现前端音视频录制

详见下篇文章

热门文章

暂无图片
编程学习 ·

那些年让我们目瞪口呆的bug

程序员一生与bug奋战&#xff0c;可谓是杀敌无数&#xff0c;见怪不怪了&#xff01;在某知识社交平台中&#xff0c;一个“有哪些让程序员目瞪口呆的bug”的话题引来了6700多万的阅读&#xff0c;可见程序员们对一个话题的敏感度有多高。 1、麻省理工“只能发500英里的邮件” …
暂无图片
编程学习 ·

redis的下载与安装

下载redis wget http://download.redis.io/releases/redis-5.0.0.tar.gz解压redis tar -zxvf redis-5.0.0.tar.gz编译 make安装 make install快链方便进入redis ln -s redis-5.0.0 redis
暂无图片
编程学习 ·

《大话数据结构》第三章学习笔记--线性表(一)

线性表的定义 线性表&#xff1a;零个或多个数据元素的有限序列。 线性表元素的个数n定义为线性表的长度。n为0时&#xff0c;为空表。 在比较复杂的线性表中&#xff0c;一个数据元素可以由若干个数据项组成。 线性表的存储结构 顺序存储结构 可以用C语言中的一维数组来…
暂无图片
编程学习 ·

对象的扩展

文章目录对象的扩展属性的简洁表示法属性名表达式方法的name属性属性的可枚举性和遍历可枚举性属性的遍历super关键字对象的扩展运算符解构赋值扩展运算符AggregateError错误对象对象的扩展 属性的简洁表示法 const foo bar; const baz {foo}; baz // {foo: "bar"…
暂无图片
编程学习 ·

让程序员最头疼的5种编程语言

世界上的编程语言&#xff0c;按照其应用领域&#xff0c;可以粗略地分成三类。 有的语言是多面手&#xff0c;在很多不同的领域都能派上用场。大家学过的编程语言很多都属于这一类&#xff0c;比如说 C&#xff0c;Java&#xff0c; Python。 有的语言专注于某一特定的领域&…
暂无图片
编程学习 ·

写论文注意事项

参考链接 给研究生修改了一篇论文后&#xff0c;该985博导几近崩溃…… 重点分析 摘要与结论几乎重合 这一条是我见过研究生论文中最常出现的事情&#xff0c;很多情况下&#xff0c;他们论文中摘要部分与结论部分重复率超过70%。对于摘要而言&#xff0c;首先要用一小句话引…
暂无图片
编程学习 ·

安卓 串口开发

上图&#xff1a; 上码&#xff1a; 在APP grable添加 // 串口 需要配合在项目build.gradle中的repositories添加 maven {url "https://jitpack.io" }implementation com.github.licheedev.Android-SerialPort-API:serialport:1.0.1implementation com.jakewhart…
暂无图片
编程学习 ·

2021-2027年中国铪市场调研与发展趋势分析报告

2021-2027年中国铪市场调研与发展趋势分析报告 本报告研究中国市场铪的生产、消费及进出口情况&#xff0c;重点关注在中国市场扮演重要角色的全球及本土铪生产商&#xff0c;呈现这些厂商在中国市场的铪销量、收入、价格、毛利率、市场份额等关键指标。此外&#xff0c;针对…
暂无图片
编程学习 ·

Aggressive cows题目翻译

描述&#xff1a; Farmer John has built a new long barn, with N (2 < N < 100,000) stalls.&#xff08;John农民已经新建了一个长畜棚带有N&#xff08;2<N<100000&#xff09;个牛棚&#xff09; The stalls are located along a straight line at positions…
暂无图片
编程学习 ·

剖析组建PMO的6个大坑︱PMO深度实践

随着事业环境因素的不断纷繁演进&#xff0c;项目时代正在悄悄来临。设立项目经理转岗、要求PMP等项目管理证书已是基操&#xff0c;越来越多的组织开始组建PMO团队&#xff0c;大有曾经公司纷纷建造中台的气质&#xff08;当然两者的本质并不相同&#xff0c;只是说明这个趋势…
暂无图片
编程学习 ·

Flowable入门系列文章118 - 进程实例 07

1、获取流程实例的变量 GET运行时/进程实例/ {processInstanceId} /变量/ {变量名} 表1.获取流程实例的变量 - URL参数 参数需要值描述processInstanceId是串将流程实例的id添加到变量中。变量名是串要获取的变量的名称。 表2.获取流程实例的变量 - 响应代码 响应码描述200指…
暂无图片
编程学习 ·

微信每天自动给女[男]朋友发早安和土味情话

微信通知&#xff0c;每天给女朋友发早安、情话、诗句、天气信息等~ 前言 之前逛GitHub的时候发现了一个自动签到的小工具&#xff0c;b站、掘金等都可以&#xff0c;我看了下源码发现也是很简洁&#xff0c;也尝试用了一下&#xff0c;配置也都很简单&#xff0c;主要是他有一…
暂无图片
编程学习 ·

C语言二分查找详解

二分查找是一种知名度很高的查找算法&#xff0c;在对有序数列进行查找时效率远高于传统的顺序查找。 下面这张动图对比了二者的效率差距。 二分查找的基本思想就是通过把目标数和当前数列的中间数进行比较&#xff0c;从而确定目标数是在中间数的左边还是右边&#xff0c;将查…
暂无图片
编程学习 ·

项目经理,你有什么优势吗?

大侠被一个问题问住了&#xff1a;你和别人比&#xff0c;你的优势是什么呢? 大侠听到这个问题后&#xff0c;脱口而出道&#xff1a;“项目管理能力和经验啊。” 听者抬头看了一下大侠&#xff0c;显然听者对大侠的这个回答不是很满意&#xff0c;但也没有继续追问。 大侠回家…
暂无图片
编程学习 ·

nginx的负载均衡和故障转移

#注&#xff1a;proxy_temp_path和proxy_cache_path指定的路径必须在同一分区 proxy_temp_path /data0/proxy_temp_dir; #设置Web缓存区名称为cache_one&#xff0c;内存缓存空间大小为200MB&#xff0c;1天没有被访问的内容自动清除&#xff0c;硬盘缓存空间大小为30GB。 pro…
暂无图片
编程学习 ·

业务逻辑漏洞

身份认证安全 绕过身份认证的几种方法 暴力破解 测试方法∶在没有验证码限制或者一次验证码可以多次使用的地方&#xff0c;可以分为以下几种情况︰ (1)爆破用户名。当输入的用户名不存在时&#xff0c;会显示请输入正确用户名&#xff0c;或者用户名不存在 (2)已知用户名。…