我们以微信小程序为例
后端采用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(); } }); }, });
网友回复