js录制系统/麦克风声音(基于electron)

最近想用electron写一个和音视频相关的软件,尽管在写之前都想好了要用哪些技术,但依然写得很艰难,今天对相关知识的个人理解做个记录和整理。

时隔半年,最近发现网上有大神造好的轮子,而且功能强大:https://www.cnblogs.com/xiaoqi/p/6993912.html  

先看几个浏览器api

1、AudioContext构造器

  • AudioContext接口表示由音频模块连接而成的音频处理上下文;它可以控制它所包含的节点的创建,以及音频处理、解码操作的执行。做任何事情之前都要先创建AudioContext对象,因为一切都发生在这个环境之中。 more   
  • 通过var audioCtx = new AudioContext()创建;

2、ScriptProcessorNode 方法

  • ScriptProcessorNode是一个音频节点(AudioNode),js可以通过它直接处理音频
  • 通过audioCtx.createScriptProcessor方法创建
  • AudioContext.createScriptProcessor(bufferSize,inputChannels ,outputChannels);

--参数

  1. bufferSize:缓冲区大小 256 到 16384 之间的 2 的次幂, 为 2565121024204840968192 或者 16384(为0则由系统自动选取最优值)
  2. inputChannels :输入node的声道数(1-32)默认值是2
  3. outputChannels:输出node的声道数(1-32)默认值是2

--概念

  1. AudioNode 接口是一个处理音频的通用模块, 比如一个音频源一个AudioNode 既有输入也有输出。输入与输出都有一定数量的通道。只有一个输出而没有输入的 AudioNode 叫做音频源。more》

3、createMediaStreamSource方法

  • createMediaStreamSource用于通过音频上下文创建一个可以被播放和处理的媒体流资源

  • 它需要传入一个流对象(可以通过navigator.getUserMedia获得

好了,开干

获取系统/麦克风音频流的buffer:

        
// config.resourceType            :媒体类型(可传'system'|'device'指定获取系统/麦克风声音)
// config.bufferHanduler          :buffer处理回调函数
// config.getMediaSuccessCallback :获取媒体设备(系统声道或这麦克风)成功后的回调函数
// config.errorHanduler           :异常处理函数

class Recorder
{
    constructor (desktopCapturer,config){
        this.audioContext = null;
        this.desktopCapturer = desktopCapturer; // electron的desktopCapturer对象
        this.config = config;
    }

    start() {
        if(this.audioContext && this.audioContext.state == 'running'){
            this.stop();
        }
        if(!(navigator.getUserMedia && AudioContext)){
            return false;
        }

        if(!this.AudioContext || this.audioContext.state == 'closed'){
            this.audioContext = new AudioContext(); 
            // audioContext.onstatechange = function(e){
            //     console.log(e);               
            // }
            let audioNode = this.audioContext.createScriptProcessor(0, 1, 1);
   
           let _self = this;
           let getMediaSuccess = function(stream){
                console.log('success get ===========');
                let mediaSource = _self.audioContext.createMediaStreamSource(stream);
                
                mediaSource.connect(audioNode);
                audioNode.connect(_self.audioContext.destination);

                audioNode.onaudioprocess = (e) => {                    
                    // 好了,这里就获取到了音频流的buffer,你可以为所欲为了,嘿嘿。。 
                    // 我这里发给另个一个线程把buffer转成pcm编码
                    _self.config.bufferHanduler && _self.config.bufferHanduler(e.inputBuffer.getChannelData(0)); 
                
                }

                // 获取媒体成功,执行回调
                _self.config.getMediaSuccessCallback && _self.config.getMediaSuccessCallback();
            }

            // 获取音视频媒体
            if(this.config.resourceType == 'system'){
                // 系统音频流
                this.desktopCapturer.getSources(
                    {types: ['screen']}
                ).then(async sources => {
                    for (const source of sources) {
                        console.log(source.name);
                        if (source.name === "Entire screen" || source.name === "Entire Screen") {
                            try {
                                const stream = await navigator.mediaDevices.getUserMedia({
                                    video: {
                                        mandatory: {
                                            // cursor:"never",
                                            chromeMediaSource: 'desktop'
                                        }
                                    },
                                    audio: {
                                        mandatory: {
                                            chromeMediaSource: 'desktop',
                                        }
                                    }
                                });
                                getMediaSuccess(stream);
                            } catch (err) {
                                this.config.errorHanduler && this.config.errorHanduler(err);
                            }
                        }
                    }
                });                
            }else if(this.config.resourceType == 'device'){
                // 麦克风音频流
                navigator.mediaDevices.getUserMedia({ video: false, audio: true }).then(function(stream){
                    if(!stream){
                        this.config.errorHanduler && this.config.errorHanduler('读取设备失败,请确认你的设备是否已经正确连接好麦克风设备!');
                        return;   
                    }
                    getMediaSuccess(stream);                        
                }).catch(function(err){
                    this.config.errorHanduler && this.config.errorHanduler(err);
                });
            }else{
                this.config.errorHanduler && this.config.errorHanduler('不支持此类型!');
            }

        }
        
    } 

    stop(){
        try{
            this.audioContext.close();
        }catch (e){
            console.log(e);
        }            
    }

    restart(){
        if(this.audioContext && this.audioContext.state == 'running'){
            this.stop();
        }
        this.start();
    }

}

export default Recorder;

---------------

附:

  1. mateType https://www.iana.org/assignments/media-types/media-types.xhtml#video
  2. h5端调用有大神造好的轮子:https://www.cnblogs.com/xiaoqi/p/6993912.html  

猜你喜欢

转载自blog.csdn.net/qq_36110571/article/details/103086601