+
95
-

回答

我们以微信小程序为例

后端采用swoole搭建websocket服务

<?php

$server = new Swoole\WebSocket\Server("0.0.0.0", 9502, SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
$server->set([
    'daemonize' => false,
    'ssl_cert_file' => "/data/cert/6284283_web.debug.only.bfw.wiki.pem",
    'ssl_key_file'  => "/data/cert/6284283_web.debug.only.bfw.wiki.key",
]);
$server->on('open', function($server, $req) {
    echo "connection open: {$req->fd}\n";
});

$server->on('message', function($server, $frame) {
   echo "received message\n";
    // 确保我们处理的是二进制数据
    //if ($frame->opcode == WEBSOCKET_OPCODE_BINARY) {
        // 广播音频数据到所有其他连接的客户端
      //  foreach($server->connections as $fd) {
          //  if($fd != $frame->fd) {
             //   $server->push($fd, $frame->data, WEBSOCKET_OPCODE_BINARY);
           // }
       // }
   // }
    $server->push($frame->fd, $frame->data,WEBSOCKET_OPCODE_BINARY);
});

$server->on('close', function($server, $fd) {
    echo "connection close: {$fd}\n";
});

$server->start();

小程序采用getRecorderManager与createInnerAudioContext实现实时录制与播放对方的声音,完整的微信小程序代码如下:

wxml

<view class="container" style="padding-top: 20vh;">
  <button bindtap="startCall" disabled="{{isCallStarted}}">开始通话</button>
  <button bindtap="stopCall" disabled="{{!isCallStarted}}">结束通话</button>
</view>

点击查看剩余代码

let socketTask;

Page({
   
  data: {
    isCallStarted: false,
    recorderManager: null,
    innerAudioContext: null,
    isRecording: false,
    audioQueue: [],
    
    isPlaying: false,
   
  },


  startCall() {
    const that = this;

    this.setData({recorderManager:wx.getRecorderManager()});
    this.setData({innerAudioContext:wx.createInnerAudioContext()});
    this.data.recorderManager.onError((error) => {
        console.error('录音错误:', error);
      });
      
      this.data.recorderManager.onStop((res) => {
        console.log('录音停止:', res);
      });
    this.data.recorderManager.onFrameRecorded((res) => {
      const { frameBuffer } = res;
      console.log(frameBuffer)
      if (frameBuffer) {
      
      
        if (socketTask) {
            socketTask.send({ data: frameBuffer});
       
        }
      }
    });

    socketTask = wx.connectSocket({
      url: 'wss://web.debug.only.bfw.wiki:9502/',
      success: (res) => console.log('创建成功', res),
      fail: (res) => console.log('创建失败', res),
    });

    socketTask.onOpen(() => {
      console.log('WebSocket 已连接');
  
      that.startRecording();
    });

    socketTask.onClose(() => console.log('WebSocket 已断开'));
    socketTask.onError((error) => console.error('WebSocket 发生错误:', error));
    socketTask.onMessage((message) => {
        console.log("getdata")
        that.playAudio(message.data);
   
     });

   this.setData({ isCallStarted: true });
  },

  stopCall() {
    
    if (this.data.recorderManager) this.data.recorderManager.stop();
    if (socketTask) socketTask.close();
    if (this.data.innerAudioContext) this.data.innerAudioContext.stop();
    this.setData({ isCallStarted: false });
  
  },

  startRecording() {


    this.data.recorderManager.start({
    
        sampleRate: 44100,
        numberOfChannels: 1,
        encodeBitRate: 192000,
        format:'mp3',
        frameSize: 100
    });
   
  },
   generateRandomId() {
    return Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
},
  playAudio(arrayBuffer) {
    

   let newq=this.data.audioQueue;

   newq.push(arrayBuffer);
this.setData({audioQueue:newq})

    if (!this.data.isPlaying) this.playNextInQueue();
  },

  playNextInQueue() {
      var that=this;
    if (this.data.audioQueue.length === 0) {
      this.setData({isPlaying:false});
      return;
    }

    this.setData({isPlaying:true});
    const arrayBuffer = this.data.audioQueue.shift();
    const filePath = `${wx.env.USER_DATA_PATH}/temp_audio${that.generateRandomId()}.pcm`;

    console.log("playing")

    wx.getFileSystemManager().writeFile({
      filePath: filePath,
      data: arrayBuffer,
      encoding: 'binary',
      success: () => {
          console.log("播放")
        that.data.innerAudioContext.src = filePath;
        that.data.innerAudioContext.play();
        that.data.innerAudioContext.onError((err) =>console.log(err));
        that.data.innerAudioContext.onEnded(() => that.playNextInQueue());
      },
      fail: (error) => {
        console.error('写入音频文件失败:', error);
        that.playNextInQueue();
      }
    });
  },
});

网友回复

我知道答案,我要回答