voip 音频采集时间_H5端音频采集播放

e949d7fb724285f5b75530f38e0803f7.png

web采集音频以及音频播放在前端直播有重要地位,通常可以做播放声音,连麦,甚至可以定制一个自己的播放器,包括任意倍速,数据自定义等操作。本文介绍通过AudioContext这个Api实现前端采集,发送给socket然后socket发送给另外一个界面,实现播放。

pcm-recorder

本文采集音频都是通过webrtc的getUserMedia实现的,获得一个MediaStream对象,将该对象关联到AudioContext上即可实现采集音频,代码如下:

首先我们建立音频音频分析节点

PcmRecorder.prototype.init = function() {
  this.context = new (window.AudioContext || window.webkitAudioContext)();
  <!-- 可以用来获取音频时间和频率数据,以及实现数据可视化 -->
  this.analyser = this.context.createAnalyser();
  this.analyser.fftSize = this.config.fftSize;
  <!-- 创建一个ScriptProcessorNode 用于通过JavaScript直接处理音频 -->
  const createScript = this.context.createScriptProcessor || this.context.createJavaScriptNode;
  this.recorder = createScript.apply(this.context,
    [this.config.fftSize, this.config.numberChannels, this.config.numberChannels]);

  // 音频采集
  this.recorder.onaudioprocess = this.onaudioprocess.bind(this)
}

此时onaudioprocess回调会在采集够this.config.fftSize后回调一次。

将分析节点和MediaStream关联,代码如下:

  var _this = this;
  navigator.mediaDevices.getUserMedia({
    audio: true,
  }).then((stream) => {
    _this.audioInput = _this.context.createMediaStreamSource(stream);
  }, (error) => {
    console.error(error);
  }).then(() => {
    _this.audioInput.connect(_this.analyser);
    _this.analyser.connect(_this.recorder);
    _this.recorder.connect(_this.context.destination);
  });

onaudioprocess实现如下:

PcmRecorder.prototype.onaudioprocess = function(e) {
  if (this.config.numberChannels === 1) {
    const data = e.inputBuffer.getChannelData(0);
    // 单通道
    this.handlePcm(data);
  }
}

此时我们在this.onaudioprocess方法中即可收到pcm音频数据,采样率根据不同浏览器不同,一般为44100,如果业务代码里播放采样率不是44100还需要resample(接下来文章会讲解),这里采用单声道采集,采集出来的数据是float32类型,如果业务里面需要转换还需要转换成Uint16Array或者Uint8Array,我们这里不转换直接发出去,在下一个界面就可以直接播放了。

采集模块通过http://socket.io发送数据代码如下:

<script src="../socket.io.js/socket.io.js"></script>

<script type="text/javascript">

var socket = io('http://127.0.0.1:8085', {transports: ['websocket']});
socket.binaryType = 'arraybuffer';
var pe = null;

function receivePcmFloat32(data) {
  socket.emit('pcm-data', {
    pcm: data,
  });
}
</script>

socekt发送数据

server收发数据我们采用socket.io,关于http://socket.io使用推荐去 官网学习一下,这里会给出详细注释:

我们通过http模块和http://socekt.io模块构建http服务和socekt服务,当是静态资源时候直接返回该路径对应的资源 fs.readFile(recorderPlayerPath + reqPath,function(){....}),当是socekt服务时候,http://socket.io会触发connection事件,该函数有一个socket回调,io.on('connection', function (socket) { socket.on('pcm-data', function (data) {socket.broadcast.emit('to-player', data.pcm); });});,这里broadcast代表全体广播,也就是只要连接上的socekt都会收到该广播,如果想定向给某个人发,可以通过socket.id来区分每个人,从而实现定制化发送。我们在socket回调即监听前端发送的数据,以及把音频pcm数据转发给另外一个界面。

完整代码如下:

var app = require('http').createServer(handler)
var io = require('socket.io')(app);
var fs = require('fs');
var path = require('path');

app.listen(8085, function() {
  console.log('server start success, port:8085');
});

function handler (req, res) {
  const reqPath = req.url;
  const recorderPlayerPath = path.resolve(__dirname, '../');
  fs.readFile(recorderPlayerPath + reqPath,
  function (err, data) {
    console.log('path:', recorderPlayerPath + reqPath);
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

io.on('connection', function (socket) {
  var socketId = socket.id;
  console.log(socketId);
  socket.on('pcm-data', function (data) {
    // console.log('server receive data:', data);
    // io.on('connection',function(socket){});//建立连接
    // io.sockets.emit(约定参数,data);//向全体人员广播
    // io.emit(约定参数, data);//向全体人员广播
    // socket.emit(约定参数,data)//发送信息
    // socket.on(约定参数,callback);//接收信息
    // socket.on('disconnect',callback);//用户断开连接触发事件
    socket.broadcast.emit('to-player', data.pcm);
  });
});

pcm-player

http://socket.io收server下发的数据

<script src="../socket.io.js/socket.io.js"></script>
<script>
  var socket = io('http://127.0.0.1:8085', {transports: ['websocket']});
  socket.binaryType = 'arraybuffer';
  socket.on('to-player', function (data) {
    const buffer = new Float32Array(data);
    console.log('player receive pcm: ', buffer.length);
    player.write(buffer);
  });
</script

播放模块,我们仍然通过AudioContext,采集时候通过e.inputBuffer.getChannelData(0)采集,播放时候我们通过e.outputBuffer.getChannelData(0).set(buffer)播放。

PcmPlayer.prototype.play = function() {
  const _this = this;
  this.scriptNode = this.audioContext.createScriptProcessor(this.config.bufferSize, 1, 1);
  this.gainNode = this.audioContext.createGain();
  this.scriptNode.connect(this.gainNode);
  this.gainNode.connect(this.audioContext.destination);
  this.scriptNode.onaudioprocess = function(e) {
    if (_this.bufferLength()) {
      e.outputBuffer.getChannelData(0).set(_this.read(_this.config.bufferSize));
    } else {
      console.log('silence');
      // e.outputBuffer.getChannelData(0).set(_this.silence);
    } 
  }
}

我们维护了一个bufferQue队列,将收到的buffer缓存在其中,然后等待AudioContext的onaudioprocess回调取出播放。

后续

后面我会陆续写出通过Webassmebly来实现倍速,重采样等功能,甚至基于electron高度自定义一个音频播放器。

github

github有完整代码以及demo跑起来的例子

github地址

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值