原创

MediaRecorder实现视频音频的录制与下载

文档地址:https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder

构造函数

new MediaRecorder(MediaStream, options);

MediaStream

媒体流,通过navigator.getUserMedia的success、video标签、audio标签、canvas标签等采集到的流数据。

options

  • mimeType:录制文件类型
  • videoBitsPerSecond:视频码率
  • audioBitsPerSecond:音频码率
  • ignoreMutedMedia:忽略静音媒体

属性

录制的状态

MediaRecorder.state

枚举 描述
inactive 未开始
recording 录制中
paused 暂停

对象方法

开始录制(timeslice为每段Blob毫秒数,不设置为一整个)

mediaRecorder.start(timeslice)

停止录制

mediaRecorder.stop()

暂停录制

mediaRecorder.pause()

恢复录制

mediaRecorder.resume()

录制数据(Blob格式)

mediaRecorder.requestData()

静态方法

判断mimeType文件类型是否支持

MediaRecorder.isTypeSupported(mimeType)

事件处理

当数据有效时触发

mediaRecorder.ondataavailable

示例:

// <video playsinline id="recPlayer"></video>
let recVideo = document.querySelector('video#recPlayer');

let buffer = [];

function videoPlay() {
    let blob = new Blob(buffer, {type: 'video/webm'});
    recVideo.srcObject = null;
    recVideo.src = window.URL.createObjectURL(blob);
    recVideo.controls = true;
    recVideo.play();
};

function download() {
    let blob = new Blob(buffer, {type: 'video/webm'});
    let url = window.URL.createObjectURL(blob);
    let a = document.createElement('a');
    a.href = url;
    a.style.display = 'none';
    a.download = 'aaa.webm';
    a.click();
};

function handleDataAvailable(e) {
    if (e && e.data && e.data.size > 0) {
        buffer.push(e.data);
    }
}

mediaRecorder.ondataavailable = handleDataAvailable;

当发生错误时

mediaRecorder.onerror

当暂停时

mediaRecorder.onpause

当恢复时

mediaRecorder.onresume

当开始时

mediaRecorder.onstart

当停止时

mediaRecorder.onstop

示例

client.js

'use strict';

let videoPlay = document.querySelector('video#player');
let recVideo = document.querySelector('video#recPlayer');
let audio = document.querySelector('audio#audio');
let audioInput = document.querySelector('select#audioInput');
let videoInput = document.querySelector('select#videoInput');
let audioOutput = document.querySelector('select#audioOutput');
let other = document.querySelector('select#other');
let filter = document.querySelector('select#filter');
let snapshot = document.querySelector('button#snapshot');
let record = document.querySelector('button#record');
let recPlay = document.querySelector('button#recPlay');
let download = document.querySelector('button#download');
let photo = document.querySelector('canvas#photo');
let divConstraints = document.querySelector('div#constraints');

let mediaRecorder;
let buffer = [];

navigator.getUserMedia = navigator.getUserMedia ||
    navigator.webkitGetUserMedia ||
    navigator.mozGetUserMedia ||
    navigator.msGetUserMedia ||
    navigator.mediaDevices.getUserMedia;

function start() {
    if (navigator.getUserMedia) {
        // MediaTrackConstraints参数属性
        // https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints

        // 参数可以为范围 min~max
        let videoDeviceId = videoInput.value;
        let audioDeviceId = audioInput.value;
        const constraints = {
            // video: false,
            video: {
                // 获取设备ID
                deviceId: videoDeviceId ? videoDeviceId : undefined,
                // groupIp  获取分组ID
                width: {
                    min: 300,
                    max: 640
                },
                height: 480,
                // 长宽比
                // aspectRatio : 1.1,
                // 帧率
                frameRate: 30,
                // 摄像头
                //  user         前置
                //  environment  后置
                //  left         前置左侧
                //  right        前置右侧
                // facingMode : "user",
                // 调整视频大小
                //  none
                //  crop-and-scale
                resizeMode: "ideal"
            },
            audio: {
                deviceId: audioDeviceId ? videoDeviceId : undefined,
                // 音量          0~1.0
                volume: 0.8,
                // 采样率
                sampleRate: 59,
                // 采样大小
                sampleSize: 59,
                // 回声消除(true | false)
                echoCancellation: true,
                // 自动增益控制(true | false)
                autoGainControl: false,
                // 噪声抑制(true | false)
                noiseSuppression: true,
                // 延迟大小(延迟大的话直播比较好,延迟小的话实时通讯比较好)
                latency: 8000,
                // 声道
                //  1 用于单声道声音
                //  2用于立体声
                //  ...
                channelCount: 1
            }
        };
        navigator.getUserMedia(constraints, getStream, handleError);

    } else {
        window.alert("不支持");
    }
}


function getDevices(devicesInfos) {
    devicesInfos.forEach(function (deviceInfo) {
        let option = document.createElement('option');
        option.text = deviceInfo.label;
        option.value = deviceInfo.deviceId;
        console.log(deviceInfo);
        switch (deviceInfo.kind) {
            case "function":
            case "table":
            case "memory":
            case "global":
                other.appendChild(option);
                break;
            case "audiooutput":
                audioOutput.appendChild(option);
                break;
            case "videoinput":
                videoInput.appendChild(option);
                break;
            case "audioinput":
                audioInput.appendChild(option);
        }
    });

}

function getStream(stream) {
    window.stream = stream;
    let videoTrack = stream.getVideoTracks()[0];
    let videoConstraints = videoTrack.getSettings();
    divConstraints.textContent = JSON.stringify(videoConstraints, null, 2);
    if (window.URL) {
        // Chrome浏览器
        videoPlay.srcObject = stream;
        audio.srcObject = stream;
    } else {
        // Firefox和Opera: 可以直接把视频源设置为stream
        videoPlay.src = stream;
        audio.src = stream;
    }
    navigator.mediaDevices.enumerateDevices().then(getDevices);
}

function handleError(err) {
    console.log(err);
}

function handleDataAvailable(e) {
    if (e && e.data && e.data.size > 0) {
        buffer.push(e.data);
    }
}


function startRecord() {

    const options = {
        mimeType: 'video/webm;codecs=vp8',
    };

    if (!MediaRecorder.isTypeSupported(options.mimeType)) {
        window.alert("不支持类型" + options.mimeType);
        return;
    }
    try {
        mediaRecorder = new MediaRecorder(window.stream, options);
        mediaRecorder.ondataavailable = handleDataAvailable;
        mediaRecorder.start(2000);
        alert(mediaRecorder.state);
    } catch (e) {
        console.error(e);
    }
}

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

videoInput.onchange = start;

audioInput.onchange = start;

filter.onchange = function () {
    videoPlay.className = filter.value;
};

snapshot.onclick = function () {
    photo.className = filter.value;
    photo.getContext('2d').drawImage(videoPlay, 0, 0, photo.width, photo.height);
};

record.onclick = function () {
    if (record.textContent === 'start record') {
        startRecord();
        record.textContent = 'stop record';
        recPlay.disabled = true;
        download.disabled = true;
    } else {
        stopRecord();
        record.textContent = 'start record';
        recPlay.disabled = false;
        download.disabled = false;
    }
};

recPlay.onclick = function () {
    let blob = new Blob(buffer, {type: 'video/webm'});
    recVideo.srcObject = null;
    recVideo.src = window.URL.createObjectURL(blob);
    recVideo.controls = true;
    recVideo.play();
};

download.onclick = function () {
    let blob = new Blob(buffer, {type: 'video/webm'});
    let url = window.URL.createObjectURL(blob);
    let a = document.createElement('a');
    a.href = url;
    a.style.display = 'none';
    a.download = 'aaa.webm';
    a.click();
};

start();
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebRTC</title>
    <style>
        .none {
            -webkit-filter: none;
        }

        .blur {
            -webkit-filter: blur(3px);
        }

        .grayscale {
            -webkit-filter: grayscale(1);
        }

        .invert {
            -webkit-filter: invert(1);
        }

        .sepia {
            -webkit-filter: sepia(1);
        }

        .saturate {
            -webkit-filter: saturate(1);
        }

        .hue-rotate {
            -webkit-filter: hue-rotate(90deg);
        }

        .opacity {
            -webkit-filter: opacity(0.8);
        }

        .brightness {
            -webkit-filter: brightness(0.8);
        }

        .contrast {
            -webkit-filter: contrast(0.8);
        }

        .drop-shadow {
            -webkit-filter: drop-shadow(5px 5px 10px black);
        }
    </style>
</head>
<body>
<div>
    <label>audioInput:</label>
    <select id="audioInput"></select>
</div>
<div>
    <label>audioOutput:</label>
    <select id="audioOutput"></select>
</div>
<div>
    <label>videoInput:</label>
    <select id="videoInput"></select>
</div>
<div>
    <label>other:</label>
    <select id="other"></select>
</div>
<div>
    <label>Filter特效:</label>
    <select id="filter">
        <option value="none">无</option>
        <option value="grayscale">灰度</option>
        <option value="sepia">褐色</option>
        <option value="saturate">饱和度</option>
        <option value="hue-rotate">色相旋转</option>
        <option value="invert">反色</option>
        <option value="opacity">透明度</option>
        <option value="brightness">亮度</option>
        <option value="contrast">对比度</option>
        <option value="blur">模糊</option>
        <option value="drop-shadow">阴影</option>
    </select>
</div>
<div>
    <audio hidden id="audio"></audio>
    <table>
        <tr>
            <td>
                <video autoplay playsinline id="player"></video>
            </td>
            <td>
                <video playsinline id="recPlayer"></video>
            </td>
            <td>
                <div id="constraints" class="output"></div>
            </td>
        </tr>
        <tr class="tr">
            <td>
                <button id="record">start record</button>
            </td>
            <td>
                <button id="recPlay">play</button>
            </td>
            <td>
                <button id="download">download</button>
            </td>
        </tr>
    </table>
</div>
<div>
    <button id="snapshot">快照</button>
</div>
<div>
    <canvas width="640" height="480" id="photo"></canvas>
</div>
</body>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="client.js" type="application/javascript"></script>
</html>
正文到此结束
本文目录