<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>录音</title>
</head>

<body>
    <h1>录音</h1>
    <div>
        <button onclick="getDeviceList()">获取设备列表</button>
        <button onclick="checkSupportType()">检查支持的类型</button>
        <button id="start">开始录音</button>
        <button id="pause">暂停录音</button>
        <button id="resume">继续录音</button>
        <button id="stop">停止录音</button>
        <button id="play">播放录音</button>
        <button id="download">下载录音</button>
    </div>

    <script>
        const start = document.getElementById("start");
        const pause = document.getElementById("pause");
        const resume = document.getElementById("resume");
        const stop = document.getElementById("stop");
        const play = document.getElementById("play");
        const download = document.getElementById("download");
        const audioType = 'audio/webm';//audio/ogg和audio/webm两种格式通用
        // 录音相关变量
        let mediaRecorder;
        let chunks = []
        init();

        //初始化 
        function init() {
            //禁用按钮
            setDisabled(start, startHandler);
            setDisabled(pause, pauseHandler);
            setDisabled(resume, resumeHandler);
            setDisabled(stop, stopHandler);
            setDisabled(play, playHandler);
            setDisabled(download, downloadHandler);
            // 检查浏览器兼容性
            if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
                alert("您的浏览器不支持录音功能,请更换浏览器后重试!");
                return;
            }
            setEnabled(start, startHandler);
        }

        /**
         * 获取设备列表
         */
        function getDeviceList() {
            if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
                console.log("enumerateDevices() not supported.");
            } else {
                navigator.mediaDevices
                    .enumerateDevices()
                    .then((devices) => {
                        devices.forEach((device) => {
                            //console.log(device);
                            //device.kind: videoinput audioinput audiooutput
                            console.log(`${device.kind}: ${device.label} id = ${device.deviceId}`);
                        });
                    })
                    .catch((err) => {
                        console.log(`${err.name}: ${err.message}`);
                    });
            }
        }

        /**
         * 元素禁用
         */
        function setDisabled(element, removeFunction) {
            element.disabled = true;
            if (removeFunction) {
                element.removeEventListener("click", removeFunction);
            }
        }

        /**
         * 元素启用
         */
        function setEnabled(element, addFunction) {
            console.log('setEnabled');
            console.log(element);
            element.disabled = false;
            if (addFunction) {
                element.addEventListener("click", addFunction);
            }
        }

        function checkSupportType() {
            const types = [
                "video/webm",
                "audio/webm",
                "video/webm;codecs=vp8",
                "video/webm;codecs=daala",
                "video/webm;codecs=h264",
                "audio/webm;codecs=opus",
                "video/mp4",
            ];

            for (const type of types) {
                let res = MediaRecorder.isTypeSupported(type)
                //true不一定表示一定可以使用
                console.log(`${type}是否支持:${res}`);
            }
        }

        /**
         * 开始录音
         */
        function startHandler() {
            //开始录音
            const constraints = { audio: true }
            //const constraints = { audio: true, video: true }
            //https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia
            navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
                //mimeType支持webm和ogg,ogg不需要设置options的mineType
                let options = {};
                if (audioType == "audio/webm") {
                    options = { mimeType: 'audio/webm; codecs=opus' };
                }
                mediaRecorder = new MediaRecorder(stream, options);
                //设置的mimeType类型
                console.log(mediaRecorder.mimeType);
                //状态inactive recording paused
                console.log(mediaRecorder.state);
                //navigator.mediaDevices.getUserMedia的stream 对象
                console.log(stream);
                //视频采用的编码比率
                console.log(mediaRecorder.videoBitsPerSecond);
                //音频采用的编码比率
                console.log(mediaRecorder.audioBitsPerSecond);
                if (mediaRecorder.state != "inactive") {
                    alert('麦克风正在使用中,请先关闭再试!');
                    return;
                }
                mediaRecorder.ondataavailable = (e) => {
                    console.log('接收到收据');
                    chunks.push(e.data)
                }
                chunks = [];
                mediaRecorder.start();
                console.log('开始录音');
                setDisabled(start, startHandler);
                setEnabled(pause, pauseHandler);
                setEnabled(stop, stopHandler);
                setDisabled(resume, resumeHandler);
                setDisabled(play, playHandler);
                setDisabled(download, downloadHandler);
            }).catch((err) => {
                console.log(err.name);
                switch (err.name) {
                    case "AbortError":
                        alert("设备无法使用");
                        break;
                    case "NotAllowedError":
                        alert("您拒绝了访问您的设备!");
                        break;
                    case "NotFoundError":
                        alert("未找到设备");
                        break;
                    case "NotReadableError":
                        alert("设备不可读");
                        break;
                    case "OverconstrainedError":
                        alert("无法满足要求");
                        break;
                    case "SecurityError":
                        alert("安全错误");
                        break;
                    case "TypeError":
                        alert("类型错误");
                        break;
                    case "NotSupportedError":
                        alert("浏览器不支持");
                        break;
                }
            })
        }

        /**
         * 暂停录音
         */
        function pauseHandler() {
            if (!mediaRecorder || mediaRecorder.state != "recording") {
                alert('未开始录音');
                return;
            }
            mediaRecorder.pause();
            console.log('暂停录音');
            setDisabled(start, startHandler);
            setDisabled(pause, pauseHandler);
            setDisabled(stop, stopHandler);
            setEnabled(resume, resumeHandler);
            setDisabled(play, playHandler);
            setDisabled(download, downloadHandler);
        }

        /**
         * 继续录音
         */
        function resumeHandler() {
            if (!mediaRecorder || mediaRecorder.state != "paused") {
                alert('未暂停录音');
                return;
            }
            mediaRecorder.resume();
            console.log('继续录音');
            setDisabled(start, startHandler);
            setEnabled(pause, pauseHandler);
            setEnabled(stop, stopHandler);
            setDisabled(resume, resumeHandler);
            setDisabled(play, playHandler);
            setDisabled(download, downloadHandler);
        }

        /**
         * 停止录音
         */
        function stopHandler() {
            if (!mediaRecorder || mediaRecorder.state != "recording") {
                alert('未开始录音');
                return;
            }
            mediaRecorder.stop();
            console.log('停止录音');
            setEnabled(start, startHandler);
            setDisabled(pause, pauseHandler);
            setDisabled(stop, stopHandler);
            setDisabled(resume, resumeHandler);
            setEnabled(play, playHandler);
            setEnabled(download, downloadHandler);
        }

        /**
         * 播放录音
         */
        function playHandler() {
            if (!chunks || chunks.length <= 0) {
                alert('未有录音信息');
                return;
            }
            console.log('播放录音');

            // 创建音频文件
            const audioBlob = new Blob(chunks, { type: audioType });
            var audio = new Audio();
            audio.src = URL.createObjectURL(audioBlob);
            // 监听音频播放完毕的事件
            audio.addEventListener('ended', function () {
                // 销毁<audio>元素
                console.log('音频播放完毕,销毁<audio>元素');
                audio = null;
            });
            audio.play();
        }

        /**
         * 下载录音
         */
        function downloadHandler() {
            if (!chunks || chunks.length <= 0) {
                alert('未有录音信息');
                return;
            }
            console.log('下载录音');
            // 创建音频文件
            const audioBlob = new Blob(chunks, { type: audioType });
            const a = document.createElement('a');
            a.href = URL.createObjectURL(audioBlob);
            let audioExt = 'webm';
            if (audioType == 'audio/webm') {
                audioExt = 'webm';
            }
            if (audioType == 'audio/ogg') {
                audioExt = 'ogg';
            }
            a.download = `recording_${new Date().toISOString().replace(/[:.]/g, '-')}.${audioExt}`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);

        }
    </script>
</body>

</html>

支持点击录制、暂停、继续录制、停止录制、播放、下载的功能,可以下载试试效果~~

在这里插入图片描述

参考

https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia
https://developer.mozilla.org/zh-CN/docs/Web/API/MediaStream
https://developer.mozilla.org/zh-CN/docs/Web/API/MediaRecorder
https://gitee.com/qxscj/js-demo/blob/master/html+css/38-1.html

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐