electron获取电脑/麦克风音频流,实时推送

1.获取麦克风id:

麦克风音频,包含自定义麦克风和耳机等,如何切换?

navigator.mediaDevices只读属性返回一个 MediaDevices 对象,该对象可提供对相机和麦克风等媒体输入设备以及屏幕共享的连接访问。

可以使用navigator.mediaDevices.enumerateDevices()来获取到设备的种类和id,注意判断输入设备和输出设备

 getMicro() {
      // 获取麦克风设备
      navigator.mediaDevices
        .enumerateDevices()
        .then((devices) => {
          console.log("devices---------------------------", devices);
          devices.forEach((device) => {
            if (
              device.deviceId != "default" &&
              device.deviceId != "communications" &&
              device.kind == "audioinput"
            ) {
              this.micro =
                device.deviceId == this.micro ? setElectron('get',"SET_MICRO") : "";
            }
          });
        })
        .catch((err) => {
          console.error(`错误发生 ${err.name}: ${err.message}`);
        });
    },

获取信息如下:

a9d2dacab650a9cd877ab0f3016bcd5b.png

 

2.创建音频上下文

that.audioContext = new (window.AudioContext ||
        window.webkitAudioContext)({ sampleRate: 16000 });

 

3.获取媒体流

  对于获取电脑系统声音,使用electron自带的desktopCapturer.getSources(options)方法获取屏幕资源信息:

官方介绍desktopCapturer | Electron

desktopCapturer.getSources(options)

  • 选项 对象
    • types string[] - 一个字符串数组,列出要捕获的桌面源的类型,可用的类型可以是屏幕和窗口。
    • thumbnailSizeSize(可选) - 媒体源缩略图应缩放到的尺寸大小。 默认是 150 x 150。 当您不需要缩略图时,设置宽度或高度为0。 这将节省用于获取每个窗口和屏幕内容时的处理时间。
    • fetchWindowIcons boolean (可选) - 设置为 true 以启用提取窗口图标。 默认值为false。 当值为false时,源的appIcon属性返回null。 如果一个源是屏幕类型也是如此。

返回 Promise<DesktopCapturerSource[]> - resolve 一个DesktopCapturerSource 对象类型的数组,每个 DesktopCapturerSource 代表一个屏幕或一个可以被捕获的独立窗口。

 

获取媒体流对象:

使用navigator.mediaDevices.getUserMedia()方法,获取到的媒体流对象

navigator.mediaDevices.getUserMedia会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D 转换器等等),也可能是其他轨道类型。

它返回一个 Promise 对象,成功后会resolve回调一个 MediaStream 对象。若用户拒绝了使用权限,或者需要的媒体源不可用,promisereject回调一个 PermissionDeniedError 或者 NotFoundError 

此时获取的mediaStream就是媒体流对象,可以创建video,将mediaStream赋值给vedio的srcobject即可获取桌面视频,但是我们此时需要获取音频流实时发送到后端,还需要使用audioContext进行音频处理。

 

 if (that.radio == "1") {
        //电脑系统声音
        that.$electron.desktopCapturer.getSources(
          { types: ["window", "screen"] },
          async function (error, sources) {
            if (error) throw error;
            let source = sources[0];
            try {
              that.mediaStream = await navigator.mediaDevices.getUserMedia({
                video: {
                  chromeMediaSourceId: source.id,
                  mandatory: {
                    chromeMediaSource: "desktop",
                  },
                },
                // video:true,
                audio: {
                  chromeMediaSourceId: source.id,
                  mandatory: {
                    chromeMediaSource: "desktop",
                  },
                },
              });
              that.startRecording();
            } catch (err) {
              console.log("===============err", err);
            }
          }
        );
      } else {
        //麦克风
        var options = {
          audio: {
            sampleRate: 16000,//采样率16khz
            channelCount: 1,//声道数(单声道/双声道)
            sampleSize: 16,//每个采样点大小的位数
            deviceId:
              that.micro && that.micro != ""
                ? { exact: that.micro }
                : undefined,
          },
          video: false,
        };
        that.mediaStream = await navigator.mediaDevices.getUserMedia(options);
        await that.startRecording();
      }
    },

4.音频处理

我们已经预先创建好audioContext,通过audioContext.createScriptProcessor创建一个ScriptProcessorNode 用于通过 JavaScript 直接处理音频。
 参数:

bufferSize

缓冲区大小,以样本帧为单位。具体来讲,缓冲区大小必须是下面这些值当中的某一个:256, 512, 1024, 2048, 4096, 8192, 16384. 如果不传,或者参数为 0,则取当前环境最合适的缓冲区大小,取值为 2 的幂次方的一个常数,在该 node 的整个生命周期中都不变。 该取值控制着 audioprocess 事件被分派的频率,以及每一次调用多少样本帧被处理。较低 bufferSzie 将导致一定的延迟。较高的 bufferSzie 就要注意避免音频的崩溃和故障。推荐作者不要给定具体的缓冲区大小,让系统自己选一个好的值来平衡延迟和音频质量。

numberOfInputChannels

值为整数,用于指定输入 node 的声道的数量,默认值是 2,最高能取 32.

numberOfOutputChannels

值为整数,用于指定输出 node 的声道的数量,默认值是 2,最高能取 32.

  AudioContext.createMediaStreamSource()创建一个MediaStreamAudioSourceNode接口来关联可能来自本地计算机麦克风或其他来源的音频流MediaStream.

 AudioContext.destination 返回AudioDestinationNode对象,表示当前audio context中所有节点的最终节点,一般表示音频渲染设备。

总之,首先创建音频节点scriptProcessor ,将handleAudioProcess方法绑定到scriptProcessor 对象的onaudioprocess 事件上,用于处理音频操作,然后创建音频源对象source,并将其连接到scriptProcessor对象上,最后将scriptProcessor对象连接到音频上下文的输出目标。

 async startRecording() {
      let that = this;
      //创建socket
      let url = "";
      that.socket = new WebSocket(url, token);
      // 创建一个ScriptProcessorNode 用于通过 JavaScript 直接处理音频。
      that.scriptProcessor = that.audioContext.createScriptProcessor(
        that.bufferSize,
        that.numChannels,
        that.numChannels
      );
      // scriptNode.onaudioprocess 方法关联 audioProcessingEvent ,并用它来遍历每输入流的每一个声道,和每一个声道中的每一个样本,并添加一点白噪声。
      that.scriptProcessor.onaudioprocess = that.handleAudioProcess;
      const source = that.audioContext.createMediaStreamSource(
        that.mediaStream
      );
      source.connect(that.scriptProcessor);
      that.scriptProcessor.connect(that.audioContext.destination);
    //获取socket返回结果
      that.socket.onmessage = function (event) {
        let res = JSON.parse(event.data);
        // console.log("后端返回的结果res: ", event.data);
        if (res.code == 200) {....}
    },
async handleAudioProcess(event) {
      let that = this;
      if (!this.recording) {
        return; // 如果录制已停止,则不处理音频数据
      }
      const inputBuffer = event.inputBuffer;
      const channelData = inputBuffer.getChannelData(0);
      // let max = Math.max.apply(Math, channelData);
      // let maxVal = Math.round(max * 100);
      let data16k;
      let bufferPCM;
      let dataPCM;
      data16k = trans48to16(channelData);
      // 创建data16k.length * 2个字节的缓存区
      bufferPCM = new ArrayBuffer(data16k.length * 2);
      // 返回值:它返回一个新的DataView对象,它将代表指定的数据缓冲区。
      dataPCM = new DataView(bufferPCM);
      floatTo16BitPCM(dataPCM, 0, data16k);
      // 将 PCM 数据发送给后端 WebSocket
      if (this.socket && this.socket.readyState === WebSocket.OPEN) {
        this.socket.send(dataPCM); // 发送二进制数据
      }
    },

在handleAudioProcess方法中,可以根据你的需要进行音频处理,转换成你想要的音频,然后通过socket.send()向后端实时推送音频流,通过socket.onmessage()获取返回的信息。

音频格式转化处理请参考

 

 

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值