VUE项目中录音

1.使用 electron + navigator.mediaDevices.getUserMedia 录制音视频

想获取视频流,首先需要获取所需要捕获视频流的 MediaSourceIdElectron 提供了一个获取各个“窗口”和“屏幕”视频 MediaSourceId 的通用 API

import { desktopCapturer } from 'electron';


// 获取全部窗口或屏幕的mediaSourceId
desktopCapturer.getSources({
    types: ['screen', 'window'], // 设定需要捕获的是"屏幕",还是"窗口"
    thumbnailSize: {
        height: 300, // 窗口或屏幕的截图快照高度
        width: 300 // 窗口或屏幕的截图快照宽度
    },
    fetchWindowIcons: true // 如果视频源是窗口且有图标,则设置该值可以捕获到的窗口图标
}).then(sources => {
    sources.forEach(source => {
        // 如果视频源是窗口且有图标,且fetchWindowIcons设为true,则为捕获到的窗口图标
        console.log(source.appIcon);


        // 显示器Id
        console.log(source.display_id);


        // 视频源的mediaSourceId,可通过该mediaSourceId获取视频源
        console.log(source.id);


        // 窗口名,通常来说与任务管理器看到的进程名一致
        console.log(source.name);


        // 窗口或屏幕在调用本API瞬间抓捕到的截图快照
        console.log(source.thumbnail);
    });
});

如果你只想获取当前窗口的 MediaSourceID

import { remote } from 'electron';
// const { remote } = require("electron");


// 获取当前窗口mediaSourceId的做法
const mediaSourceId = remote.getCurrentWindow().getMediaSourceId();

Windows音频流获取

const mediaRecorder = ref(null);
const audioRef=ref(null);//html写 <audio src="" id="download" ref="audioRef" controls></audio>


let recordedChunks = [];
const setSource = async () => {
    try {
        const stream = await navigator.mediaDevices.getUserMedia({
            // audio: true, // 强行表示不录制音频,音频额外获取,但是录制的是麦克风的声音
            // video: false,


            /* 上面简单写法只能录制麦克风的声音,若是需要录制系统声音,需要使用以下方法 */
            audio: {
                mandatory: {
                    // 无需指定mediaSourceId就可以录音了,录得是系统音频
                    chromeMediaSource: "desktop",
                },
            },
            video: {
                // 如果想要录制音频,必须同样把视频的选项带上,否则会失败
                mandatory: {
                    chromeMediaSource: "desktop",
                    chromeMediaSourceId: mediaSourceId,
                },
            },
        });


        // 若是不需要视频源,手工移除点不用的视频源,即可完成音频流的获取
        (stream.getVideoTracks() || []).forEach((track) =>
            stream.removeTrack(track)
        );
        // 去除音频源 getAudioTracks() 不过一般录制视频的时候,基本上都会保留音频源
        // (stream.getAudioTracks() || []).forEach((track) =>
        //     stream.removeTrack(track)
        // );


        var options = {
            audioBitsPerSecond: 128000, //音频比特率为 128kbps
            videoBitsPerSecond: 2500000, //视频比特率为 2.5Mbps
            mimeType:"audio/webm; codecs=pcm"
        };
        console.log(stream, "stream");
        mediaRecorder.value = new MediaRecorder(stream,options);// options 可不携带
        
        // 更新流
        mediaRecorder.value.addEventListener("dataavailable", function (e) {
            if (e.data.size > 0) {
                recordedChunks.push(e.data);
            }
        });


        mediaRecorder.value.addEventListener("pause", (e) => {
            console.log("暂停");
        });


        mediaRecorder.value.addEventListener("resume", (e) => {
            console.log("继续");
        });


        mediaRecorder.value.addEventListener("stop", function () {
            console.log(
                "停止",
                recordedChunks,
                new Blob(recordedChunks,{ type: "audio/webm; codecs=pcm" }),
                URL.createObjectURL(new Blob(recordedChunks,{ type: "audio/webm; codecs=pcm" }))
            );


            // 写入 audio里
            audioRef.value.src = URL.createObjectURL(new Blob(recordedChunks));
        });


        mediaRecorder.value.addEventListener("start", (e) => {
            console.log("开始");
        });
    } catch (e) {
        console.log(e);
    }
};


onMounted(() => {
    setSource();
})
// 操作按钮
// 开始
const startButton = () => {
    recordedChunks=[];
    mediaRecorder.value.start(1000);//1s读取一次
};


// 停止
const stopButton = () => {
    mediaRecorder.value.stop();
};


// 暂停/继续
const pauseButton = () => {
    console.log(mediaRecorder.value.state);
    if (mediaRecorder.value.state === "recording") {
        mediaRecorder.value.pause();
    } else if (mediaRecorder.value.state === "paused") {
        mediaRecorder.value.resume();
    }
};

2.使用 Recorder + navigator.mediaDevices.getUserMedia 录音音视频

Recorder链接:Recorder

import Recorder from "recorder-core";
import "recorder-core/src/engine/wav";
import "recorder-core/src/extensions/wavesurfer.view";


const fs = window.require("fs");
const { remote } = require("electron");
const { dialog } = remote;


const mediaRecorder = ref(null);
let rec = null;


const indexStr = ref("");
const index = ref(0);


const wave = ref();//绘制波形图
const blobData = ref();


let timer = null;


const recStart=(stream) => {
    console.log("未开始录音", stream);
    if (rec) {
        //清理掉已有的
        rec.close();
    }


    rec = Recorder({
        type: "wav",
        sampleRate: 16000,
        bitRate: 16,
        sourceStream: stream, //明确指定从这个流中录制音频
        onProcess: function (
            buffers,
            powerLevel,
            bufferDuration,
            bufferSampleRate
        ) {
            wave.value = Recorder.WaveSurferView({ elem: ".elem" });
            wave.value.input(
                buffers[buffers.length - 1],
                powerLevel,
                bufferSampleRate
            );
        },
    });
    rec.open(
        function () {
            setTimeout(() => {
                rec.start();
            }, 1000);
            timer = setInterval(() => {
                index.value += 1;
                getTime(index.value);
            }, 1000);
        },
        function (msg, isUserNotAllow) {
            console.log((isUserNotAllow ? "UserNotAllow," : "") + "无法录音:" + msg);
        }
    );
}


const setSource = async () => {
    try {
        const stream = await navigator.mediaDevices.getUserMedia({
            audio: {
                mandatory: {
                    // 无需指定mediaSourceId就可以录音了,录得是系统音频
                    chromeMediaSource: "desktop",
                },
            },
            video: {
                // 如果想要录制音频,必须同样把视频的选项带上,否则会失败
                mandatory: {
                    chromeMediaSource: "desktop",
                    chromeMediaSourceId: mediaSourceId,
                },
            },
        });


        // 接着手工移除点不用的视频源,即可完成音频流的获取
        (stream.getVideoTracks() || []).forEach((track) =>
            stream.removeTrack(track)
        );
        recStart(stream);
        console.log(stream, "stream");
    } catch (e) {
        console.log(e);
    }
};


const startButton = () => {
    indexStr.value = "00:00:00";
    setSource();
};


const stopButton = () => {
    clearInterval(timer);
    timer = null;
    rec.stop(
            function (blob, duration) {
                if (blob) {
                    blobData.value = blob;
                }
                console.log(blob,'blob');
            },
            function (msg) {
                console.log("录音失败:" + msg);
            }
        );
};


const getTime = (index) => {
    if (index < 10) {
        indexStr.value = "00:00:0" + index;
    }
    if (index > 10 && index < 60) {
        indexStr.value = "00:00:" + index;
    }
    if (index > 60 && index < 3600) {
        let miao = index % 60 < 10 ? "0" + (index % 60) : index % 60;
        let fen =
            parseInt(index / 60) < 10
                ? "0" + parseInt(index / 60)
                : parseInt(index / 60);
        indexStr.value = "00:" + fen + ":" + miao;
    }
};
// 写入 导出音频
const exportAudio = () => {
    dialog
        .showSaveDialog(null, {
            title: "保存",
            defaultPath: "recorder" + ".wav",
            properties: ["openFile"],
            filters: [{ name: "All Files", extensions: ["*"] }],
            // 点击保存回调
        })
        .then(({ filePath }) => {
            const fr = new FileReader();
            fr.readAsArrayBuffer(blobData.value);
            setTimeout(() => {
                let buffer = fr.result;
                var buf = new Buffer(buffer.byteLength);
                var view = new Uint8Array(buffer);
                for (var i = 0; i < buf.length; ++i) {
                    buf[i] = view[i];
                }
                fs.writeFile(filePath, buf, (err) => {
                    console.log(err, "err");
                });
            });
        });
};

在 方法1 中,最后导出音频的时候,在 mediaRecorder.value.addEventListener("stop", function () {})里 添加 blobData.value=new Blob(recordedChunks,{ type: "audio/wav" }),并调用 exportAudio方法,导致的音频并不是 wav 格式

原因是recordedChunks流里的格式是 audio/webm; codecs=pcm,在 new Blob里时候,并没有改变 recordedChunksmimeType的格式,仅仅是给Blob定义了一个type

并且mimeType里并没有 audio/wav这个格式配置 ,所以不仅在 options 里配置无效,还会报错。

tips:目前的 chrome 浏览器的采样率是只读状态且不能修改(修改无效), 需要在每一次onaudioprocess 触发的时候对 PCM 数据进行相应的转化处理,来达到使用要求。 推荐一个转化采样率的方法: Recorder源码
——来源:浏览器的录音

目前官方支持的格式:

const types = [
  "video/webm",
  "audio/webm",
  "video/webm;codecs=vp8",
  "video/webm;codecs=daala",
  "video/webm;codecs=h264",
  "audio/webm;codecs=opus",
  "video/mpeg"
];

使用 isTypeSupported 测试当前浏览器的支持状况

for (const type of types) {
    console.log(`Is ${type} supported? ${MediaRecorder.isTypeSupported(type) ? "Maybe!" : "Nope :("}`);
}

3. 扩展

1.使用 enumerateDevices()用于检查可用的输入设备
let itemOpiton=[]
navigator.mediaDevices.enumerateDevices().then((devices) => {
    devices.forEach((device) => {
        console.log(device,'device');
        if (device.kind === "audioinput") {
            itemOpiton.push({
                textContent:device.label,
                value:device.deviceId
            })
        }
    });
});
2.使用 HTML Canvas 作为源 MediaStream

在 9 秒后停止录制

const canvas = document.querySelector("canvas");


// Optional frames per second argument.
const stream = canvas.captureStream(25);
const recordedChunks = [];


console.log(stream);
const options = { mimeType: "video/webm; codecs=vp9" };
const mediaRecorder = new MediaRecorder(stream, options);


mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.start();


function handleDataAvailable(event) {
    console.log("data-available");
    if (event.data.size > 0) {
        recordedChunks.push(event.data);
        console.log(recordedChunks);
        download();
    } else {
        // …
    }
}
function download() {
    const blob = new Blob(recordedChunks, {
        type: "video/webm",
    });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    a.href = url;
    a.download = "test.webm";
    a.click();
    window.URL.revokeObjectURL(url);
}


// demo: to download after 9sec
setTimeout((event) => {
    console.log("stopping");
    mediaRecorder.stop();
}, 9000);

参阅文档:

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天弈初心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值