五分钟了解H5视频拍摄

1,000 阅读4分钟

项目背景

公司业务为分享链接给到用户,用户进入上传照片,视频等,需直接调用相机, 不允许从相册里拿,并对拍摄出视频大小有要求,为此进行技术调研

进程

一、Media Recorder API

因从未参与过此类的技术开发,两眼一抹黑,像无头苍蝇一样在搜索引擎里查找,首先找到了 Media Recorder API Web API。在此方案上找了N多的Demo进行验证

// 开始录制
function startRecording(stream, lengthInMS) {

    recorder = new MediaRecorder(stream, {
        //因为视频和音频分开录制这里直接注释了
        // audioBitsPerSecond: 128000, // 音频码率
        bitsPerSecond: 1843200, // 视频码率
        mimeType: "video/webm", // 编码格式   
    })
    recorder.ondataavailable = (event) => {
        let data = event.data;
        dataChunks.push(data);
    };
    recorder.start(1000);
    console.log(recorder.state + " start to recording .....");
}

//访问用户媒体设备的兼容方法
const getUserMedia = (constraints, success, error) => {

    if (navigator.mediaDevices.getUserMedia) {
        //最新的标准API
        navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
        var supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
        console.log('supportedConstraints', supportedConstraints)
    } else if (navigator.webkitGetUserMedia) {
        //webkit核心浏览器
        navigator.webkitGetUserMedia(constraints, success, error)
    } else if (navigator.mozGetUserMedia) {
        //firfox浏览器
        navigator.mozGetUserMedia(constraints, success, error);
    } else if (navigator.getUserMedia) {
        //旧版API
        navigator.getUserMedia(constraints, success, error);
    }
}

const Start = () => {
    if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) {
        startTime.value = new Date().getTime()
        //调用用户媒体设备, 访问摄像头
        getUserMedia({
            video: {
                width: { ideal: 1280, max: 1280 }, 
                height: { ideal: 720, max: 720 },
                facingMode: { exact: "environment" }, // 设置前后摄像头
                frameRate: { ideal: 30, max: 30 },  // 设置帧数
                torch: true
            }
        }, success, error);
    } else {
        alert('不支持访问用户媒体');
    }
}

优点: API 可以设置拍摄时的分辨率,帧数等参数,控制拍摄出视频大小
缺点:
1、支持的浏览器数量方面不足,测试5个手机3个不行(Can I use
2、拍摄出视频没有时间属性,导致播放时没有进度条

解决视频没有时间属性,可以使用 EBML.js 解决

function getSeekableBlob(inputBlob, callback) {
    // EBML.js copyrights goes to: https://github.com/legokichi/ts-ebml
    if (typeof EBML === 'undefined') {
        throw new Error('Please link: https://www.webrtc-experiment.com/EBML.js');
    }

    const reader = new EBML.Reader();
    const decoder = new EBML.Decoder();
    const tools = EBML.tools;

    const fileReader = new FileReader();
    fileReader.onload = function (e) {
        const ebmlElms = decoder.decode(this.result);
        ebmlElms.forEach((element) => {
            reader.read(element);
        });
        reader.stop();
        const refinedMetadataBuf = tools.makeMetadataSeekable(reader.metadatas, reader.duration, reader.cues);
        const body = this.result.slice(reader.metadataSize);

        const newBlob = new Blob([refinedMetadataBuf, body], {
            type: 'video/webm'
        });

        callback(newBlob);
    };
    fileReader.readAsArrayBuffer(inputBlob);
}

此方案Demo代码 video-web-record-api-demo 注意受浏览器安全限制,只有localhost或者https的网站才支持录制屏幕,上线需要部署https

二、HTML Media Capture

后续查到可以在手机端浏览器中使用HTML Media Capture来稳定的录制视频

<div><input type="file" accept="video/*" capture="user">video</div>

但是不能设置分辨率,帧数等参数,所以HTML Media Capture录制产物较大,所以又寻找压缩视频的方案,找到 FFmpeg.wasm 库可以在前端压缩视频

  const { createFFmpeg, fetchFile } = FFmpeg;
      let ffmpeg = null;

      const transcode = async ({ target: { files } }) => {
        if (ffmpeg === null) {
          ffmpeg = createFFmpeg({ log: true });
        }
        const message = document.getElementById('message');
        const { name } = files[0];
        message.innerHTML = 'Loading ffmpeg-core.js';
        if (!ffmpeg.isLoaded()) {
          await ffmpeg.load();
        }      
        console.log('transcode 初始大小', ((files[0]).size / 1024 / 1024).toFixed(2), 'M')
        console.time('transcode')
        ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
        message.innerHTML = 'Start transcoding';
        // await ffmpeg.run('-i', name,  'output.mp4');
        await ffmpeg.run('-i', name, '-c:v', 'libx264', '-crf', '23', '-preset', 'medium', '-c:a', 'aac', '-b:a', '128k', '-movflags', '+faststart', '-vf', 'scale=-2:720,format=yuv420p', 'output.mp4');
        // await ffmpeg.run('-y', '-i', name, '-s', '1280x720', '-r', '5', '-c:v', 'libx264', '-b:v', '600k', '-b:a', '44100', '-ac', '2', '-ar', '22050', '-tune', 'fastdecode', '-preset', 'ultrafast', 'output.mp4')
        message.innerHTML = 'Complete transcoding';
        const data = ffmpeg.FS('readFile', 'output.mp4');
        console.timeEnd('transcode')
        console.log('transcode 压缩后大小', ((new Blob([data.buffer])).size / 1024 / 1024).toFixed(2), 'M')
        const video = document.getElementById('output-video');
        video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
      }
      const elm = document.getElementById('uploader');
      elm.addEventListener('change', transcode);

      const cancel = () => {
        try {
          ffmpeg.exit();
        } catch(e) {}
        ffmpeg = null;
      }

压缩视频时间长,1分钟视频的压缩时间在1~2分钟,业务不接受,并且 FFmpeg.wasm 包大小在8M,致使页面加载时间较长,用户体验全无

三、WebRTC

WebRTC是一个免费、开放的项目。使web浏览器通过简单的JavaScript api接口实现实时通信功能。 还有另外一个重要的优势:因为它是以数据流方式工作的,所以它没有上传时间,并且在浏览器崩溃的情况中不会造成数据丢失。 但是WebRTC技术复杂,基础设备:信令服务器,TURN/STUN服务器,WebRTC终端,连接过程:连接过程可能在信令,发现或者P2P连接的时候发生失败

四、微信小程序

微信小程序 camera 组件配合 CameraContext API 实现视频拍摄

<camera binderror="getError" resolution="medium" device-position="{{devicePosition}}" style="width: 100vw;height: 100vh;"></camera>
    this.data.ctx.stopRecord({
      // compressed: false,  //是否压缩录完的视频
      success: (res) => {
        console.log(res)
        clearInterval(this.data.timer)
        this.setData({
          video_url: res.tempVideoPath,
          isRecording: false
        })
        this.compressVideo(res.tempVideoPath)

        this.data.isStop = false
        wx.hideLoading()
      },
      fail(err) {
        console.log(err)
        this.data.isStop = false
        wx.hideLoading()
      }
    })
    
    compressVideo(path) {
    console.time('compressVideo')
    let compressVideoTime = new Date().getTime()
    console.log('开始压缩', path)
    wx.compressVideo({
      src: path,
      bitrate: 900,
      fps: 30,
      resolution: 1,
      success: (res) => {
        console.log('压缩完成', res.tempFilePath, 'Time', (new Date().getTime()) - compressVideoTime, 'ms')
        console.timeEnd('compressVideo')
        let size = res.size / 1024;
        console.log('compressVideo Size:', size.toFixed(2), 'M')
        this.setData({
          video_url_compress: res.tempFilePath
        })
      },
      fail: (err) => {
        console.log('err', err)
      }
    })
  },

微信小程序,提供拍摄时的参数设置,和视频压缩,视频压缩的速度较快,产物根据配置参数控制其大小,并且微信为我们趟平了兼容性的问题,可满足业务需要

参考

在HTML5视频录制方面,我们为什么选WebRTC而不选Media Recorder API
RecordRTC网页录制视频无法播放以及无时长问题
微信小程序文档