无需Flash录视频——HTML5中级进阶

作者: 商领云 | 来源:发表于2016-10-13 11:00 被阅读2091次

    前言

    HTML5的权限越来越大了,浏览器可以直接调用摄像头、麦克风了,好激动啊。我们要用纯洁的HTML代码造出自己的天地。

    视频采集

    本篇介绍的栗子 都是在chrome 47 版本以上的,低版本的可能会出现白屏和错误。

    1.安全环境

    随着Chrome版本的升高,安全性问题也越来越被重视,较新版本的Chrome浏览器在调用一些API时需要页面处在安全环境中。本篇文章所介绍的API函数,都需要在安全环境中执行。如果处在非安全环境下 ( http页面 ) 这些API就会有意想不到的问题。

    比如 getUserMedia()就会报出警告,并执行出错。而在设备枚举enumerateDevices()时,虽然不会报错,但是他隐藏了设备label。

    getUserMedia() no longer works on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.

    根据谷歌的意思,常用的安全环境有如下

    如果你做了一个视频测试的页面,想嘚瑟给局域网的其他人,但是又没有域名证书怎么办?
    这时候只能通过修改其他人的hosts文件了
    比如你的测试服务器IP地址是192.168.2.18,那么其他人的hosts文件修改如下:

    #localhost 127.0.0.1
    localhost 192.168.2.18
    

    当使用别人的Chrome浏览器访问 http://localhost/[getUserMediaTestPage]时,就会顺利的执行这些API了。

    但是移动端的浏览器并不认localhost,就算你修改了hosts ,移动端的浏览器根本不理你,解析都不解析。
    所以想在手机上测试,只能老老实实申请个证书了。

    2.设备枚举

    在开启摄像头之前,先要把可以使用的麦克风和摄像头 ( 输入设备 ) 列出来,如果没有这两样设备也就无法继续。

    代码如下:

    <label for="audioDevice"> 录音设备: </label>
    <select id="audioDevice">
      
    </select>
    <br>
    <label for="videoDevice"> 录影设备: </label>
    <select id="videoDevice">
      
    </select>
    
    <script>
    navigator.mediaDevices.enumerateDevices().then(function (data) {
      data.forEach(function (item) {
        if(item.kind=="audioinput"){ //麦克风
         document.getElementById("audioDevice").innerHTML +=  "<option value='"+ item.deviceId +"'>" + item.label + " </option> "
        }else if(item.kind=="videoinput"){ //摄像头
         document.getElementById("videoDevice").innerHTML +=  "<option value='"+ item.deviceId +"'>" + item.label + " </option> "
        }
      })
    },function (error) {
      console.log(error);
    })
    </script>
    

    效果如下图,和浏览器自己获取的一模一样。

    这里写图片描述

    注意:上图的实例中,浏览器地址栏最右边的摄像头标识是需要使用 getUserMedia()函数时才会出现。

    <script>
        var getUserMedia = navigator.webkitGetUserMedia; //Chrome浏览器的方法
        getUserMedia.call(navigator, {
          video:true, // 开启音频
          audio:true  // 开启视频
        }, function(stream){
            console.log(stream); // 成功获取媒体流
        }, function(error){
            //处理媒体流创建失败错误
        });
    </script>
    
    

    这时候可以通过浏览器给出的菜单下拉选择设备。

    3.设置参数,预览

    我们可以通过代码来指定使用哪个摄像头和麦克风设备。
    也可以通过代码设置视频的宽、高和帧率。

    代码如下:

    
    <video id="video" autoplay></video> <!-- 一定要有 autoplay -->
    
    <script>
    var getUserMedia = navigator.webkitGetUserMedia ;
    
    getUserMedia.call(navigator, {
      "audio":{
            "mandatory":{
                "sourceId":"" // 指定设备的 deviceId
            }
        },
      "video":{
            "optional":[ 
                {"minWidth":400},
                {"maxWidth":400},  // 数字类型,固定宽度
                {"minHeight":220}, 
                {"maxHeight":220},  // 数字类型,固定高度
                {"frameRate":"12"}  // 帧率
            ],"mandatory":{
            "sourceId":"" // 指定设备的 deviceId
            }
          }
    }, function(stream){
        //绑定本地媒体流到video标签用于输出
        document.getElementById("video").src = URL.createObjectURL(stream);
    }, function(error){
        //处理媒体流创建失败错误
    });
    
    </script>
    

    输出的视频流通过blob对象链接绑定到video标签输出。

    这个deviceId就是从上文设备枚举 enumerateDevices() 获取到的。
    两种设备,如果有一个deviceId填写不正确,就会报出一个DevicesNotFoundError的错误。而且一旦指定了设备后,浏览器自己的设备选择就会变成灰色不可选。

    视频的宽高,并不会因为填写的数值比例不合法而失真。
    比如你设定了宽度30,高度100,那么他会从视频中心截取 30x100 的画面,而不是把原画面挤压到这个30x100的尺寸。

    效果如下:

    这里写图片描述

    如果您的预览一片漆黑,或者只有一个小黑点,那么说明您的摄像头正在被占用...

    吐槽:这个getUserMedia()函数的参数,w3的官方文档链接如下:
    https://www.w3.org/TR/mediacapture-streams/
    可是Chrome并没有遵循它,而且差距还挺大...

    视频保存

    1. 格式支持

    Chrome浏览器是大力推广webm的视频格式的。可以用MediaRecorder.isTypeSupported("video/webm")来测试是否支持这种类型的编码。如果返回true,那么我们录制的视频就可以被保存为这种指定的格式。如果不指定,那么将会使用浏览器自动指定的文件格式。文档原话如下

    If this paramater is not specified, the UA will use a platform-specific default format.

    但是这个默认值却无法直接获取,全靠猜...

    2. 视频录制 MediaRecorder

    我们使用 MediaRecorder来录制视频,参数是通过getUserMedia()获取的媒体流。

    • 通过绑定ondataavailable事件,来获取视频片段数据,并在内存中累积。
    • 录制的开始和结束分别使用 startstop 函数。
    • 执行start之后会周期性触发ondataavailable事件。
    • 执行stop之后会停止触发ondataavailable事件。
    • 录制结束后,把累计的片段数据保存为blob对象,并从浏览器下载存为视频文件。

    代码如下:

    <script>
    
    var getUserMedia = navigator.webkitGetUserMedia ;
    var g_stream = null, g_recorder = null;
    function startPreview(){
      getUserMedia.call(navigator, {
        video:true,
        audio:true
      }, function(stream){
          g_stream = stream;
      }, function(error){
      
      });
    }
    
    function stopRecording(){
      g_recorder.stop();
    }
    
    function startRecording(){
      var chunks = [];
      g_recorder = new MediaRecorder(g_stream,{mimeType:"video/webm"});
      g_recorder.ondataavailable = function(e) {
        chunks.push(e.data);
      }
      g_recorder.onstop = function(e) {
        var blob = new Blob(chunks, { 'type' : 'video/webm' });
        var audioURL = URL.createObjectURL(blob);
        window.open(audioURL);
      }
      g_recorder.start();
    }
    </script>
    

    注意:本例并没有填写视频文件头,所以保存出来的视频文件没有时间轨,无法快进和跳跃。可以用格式工厂转

    “莫基了”上面有一个录制音频的例子 传送门

    这篇文章的DEMO请戳 这里

    相关文章

      网友评论

      • FlyXhc:怎么加视频文件头让视频能快进?
      • 多情剑客a无情剑:但是用手机去测试,完全不知道录制的视频保存在那里了啊

      本文标题:无需Flash录视频——HTML5中级进阶

      本文链接:https://www.haomeiwen.com/subject/ndswyttx.html