前言
最近写了一个小工具,实现在web采集pcm音频并通过websocket传输到后台,再传输到web端进行播放。
结构
web采集音频---》websocket后台---》web播放音频
web采集音频
编写以个简单的 HTML 页面,用于录制音频并通过 WebSocket 将音频数据发送到后端服务器。
在 HTML 页面中,有两个按钮,分别是 "Start Recording" 和 "Stop Recording"。当点击 "Start Recording" 按钮时,页面会请求用户授权访问麦克风,并开始录制音频。录制音频的过程中,将音频数据通过 WebSocket 发送到指定的后端服务器。当点击 "Stop Recording" 按钮时,停止录制音频并关闭与 WebSocket 的连接。
具体解读如下:
在 JavaScript 部分,首先创建了一些变量用于存储音频流、ScriptProcessorNode、WebSocket 对象等。
当点击 "Start Recording" 按钮时,创建了一个新的 WebSocket 对象,并发起了请求以获取用户麦克风的授权。如果用户授权成功,则创建了一个 AudioContext 对象,并通过 getUserMedia 方法获取到音频流。
在获取到音频流后,创建了一个 MediaStreamAudioSourceNode 对象,并将其连接到一个 ScriptProcessorNode 对象上。ScriptProcessorNode 对象用于处理音频数据。在 onaudioprocess 事件处理程序中,获取到音频数据并将其发送到后端服务器。
当点击 "Stop Recording" 按钮时,停止音频录制。首先关闭了音频流,然后断开了 ScriptProcessorNode 与 AudioContext 的连接,关闭了 WebSocket 连接。
需要注意的是,这段代码中的 WebSocket 地址是固定的('ws://127.0.0.1:9090'),你需要根据实际情况修改为你的后端服务器地址。另外,由于浏览器的安全限制,通常需要在安全的环境中(例如 HTTPS)才能够使用 getUserMedia 方法获取到用户的媒体设备。
代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Audio Capture Example</title>
</head>
<body>
<button id="startButton">Start Recording</button>
<button id="stopButton">Stop Recording</button>
<script>
var mediaStream = null
var scriptNode = null
var socket = null
// 当按钮点击时开始录制音频
//参考 https://blog.csdn.net/qq_46122292/article/details/130326752
document.getElementById('startButton').addEventListener('click', function() {
socket = new WebSocket('ws://127.0.0.1:9090');
// 创建 AudioContext 对象
var audioContext = new (window.AudioContext || window.webkitAudioContext)();
// 请求用户授权访问麦克风
navigator.mediaDevices.getUserMedia({ audio: true })
.then(function(stream) {
mediaStream = stream
// 创建 MediaStreamAudioSourceNode 对象
var source = audioContext.createMediaStreamSource(stream);
// 创建 ScriptProcessorNode 对象
scriptNode = audioContext.createScriptProcessor(4096, 1, 1);
// 连接 source 到 scriptNode
source.connect(scriptNode);
// 连接 scriptNode 到 audioContext 的 destination
scriptNode.connect(audioContext.destination);
// 当 scriptNode 接收到音频数据时
scriptNode.onaudioprocess = function(event) {
var inputData = event.inputBuffer.getChannelData(0);
// 在这里可以处理音频数据,例如将其发送到后端服务器
//console.log('Received audio data:', inputData);
var arrayBuffer = inputData.buffer;
socket.send(arrayBuffer);
};
console.log('Recording started');
})
.catch(function(error) {
console.error('Error accessing microphone:', error);
});
});
// 当按钮点击时停止录制音频
document.getElementById('stopButton').addEventListener('click', function() {
if (mediaStream) {
mediaStream.getTracks().forEach(function(track) {
track.stop();
});
console.log('Recording stopped');
mediaStream = null
scriptNode.disconnect()
scriptNode = null
socket.close(1000)
socket = null
}
});
</script>
</body>
</html>
wen播放音频
写一个简单的 HTML 页面,用于通过 WebSocket 接收 PCM 音频数据,并在浏览器中播放该音频数据。
在 HTML 页面中,有两个按钮,分别是 "Start Play" 和 "Stop Play"。当点击 "Start Play" 按钮时,页面会建立 WebSocket 连接,并开始接收 PCM 音频数据。接收到 PCM 数据后,将其转换为 Float32Array,并使用 Web Audio API 中的 AudioContext 来创建音频缓冲区和音频源节点,并将 PCM 数据填充到音频缓冲区中。然后通过 AudioBufferSourceNode 播放音频。
具体解读如下:
在 JavaScript 部分,当点击 "Start Play" 按钮时,首先创建了一个新的 AudioContext 对象用于处理音频。然后创建了一个 WebSocket 对象,并建立与指定地址的 WebSocket 连接。
在建立 WebSocket 连接后,通过设置 socket.binaryType = "arraybuffer";,告知 WebSocket 接收到的数据是二进制数组缓冲区类型。
当 WebSocket 接收到消息时,会触发 socket.onmessage 事件处理程序。在该处理程序中,接收到的 PCM 数据存储在 event.data 中,并调用 playPCM 函数来播放 PCM 数据。
playPCM 函数首先将 PCM 数据转换为 Float32Array 类型,然后创建一个包含该 PCM 数据的 AudioBuffer。接着创建一个 AudioBufferSourceNode,并将 AudioBuffer 设置为其 buffer 属性。最后将 AudioBufferSourceNode 连接到 AudioContext 的 destination(即音频输出设备),并调用 start 方法开始播放音频。
需要注意的是,该示例中播放 PCM 数据使用了 Web Audio API,因此只支持现代浏览器。另外,该代码中的地址 'ws://192.168.3.17:9091' 应该根据实际情况修改为你的 WebSocket 服务器地址。
示例代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PCM Audio Streaming</title>
</head>
<button id="startButton">Start Play</button>
<button id="stopButton">Stop Play</button>
<body>
<h1>PCM Audio Streaming Demo</h1>
<script>
document.getElementById('startButton').addEventListener('click', function() {
var audioContext = new (window.AudioContext || window.webkitAudioContext)();
var socket = new WebSocket('ws://192.168.3.17:9091');
socket.binaryType = "arraybuffer";
// 当 WebSocket 连接打开时
socket.onopen = function(event) {
console.log('WebSocket connected');
};
// 当 WebSocket 接收到消息时
socket.onmessage = function(event) {
var pcmData = event.data; // 接收到的 PCM 数据
playPCM(pcmData); // 播放 PCM 数据
};
// 当 WebSocket 连接关闭时
socket.onclose = function(event) {
console.log('WebSocket closed');
};
function playPCM(pcmData) {
// 将 PCM 数据转换为 Float32Array
var floatArray = new Float32Array(pcmData);
// 创建 AudioBuffer
var audioBuffer = audioContext.createBuffer(1, 4096, 48000);
// 将 PCM 数据填充到 AudioBuffer 中
var audioBufferChannel = audioBuffer.getChannelData(0);
for (var i = 0; i < 4096; i++) {
audioBufferChannel[i] = floatArray[i];
}
// 创建 AudioBufferSourceNode
var sourceNode = audioContext.createBufferSource();
sourceNode.buffer = audioBuffer;
sourceNode.connect(audioContext.destination);
// 播放音频
sourceNode.start();
}
})
</script>
</body>
</html>
python后台中转
创建两个 WebSocket 服务器,一个用于接收网页数据,另一个用于发送给到网页数据。
首先定义了两个函数 WebsocketServerRun 和 UEWebsocketServerRun 分别用于启动 WebSocket 服务器。这两个函数都使用了 asyncio 库来实现异步处理。WebsocketServerRun 函数用于创建接收网页数据的 WebSocket 服务器,而 UEWebsocketServerRun 函数用于创建发送给 UE 数据的 WebSocket 服务器。
在 WebsocketServerRun 函数中,使用 websockets.serve 方法创建了一个 WebSocket 服务器,并指定了 IP 地址和端口号。当有客户端连接到该服务器时,会调用 handle_websocket_connection 函数来处理连接。在该函数中,通过 async for 循环接收客户端发送的消息,并根据消息类型进行相应处理。
在 UEWebsocketServerRun 函数中,同样使用 websockets.serve 方法创建了另一个 WebSocket 服务器,用于发送给 UE 数据。当有客户端连接到该服务器时,会调用 UEhandle_websocket_connection 函数来处理连接。在该函数中,保存了连接对象 UEws,并在接收到消息时将消息发送给 UE。
最后,通过多线程的方式启动了两个 WebSocket 服务器,分别运行在不同的线程中,使得两个服务器可以同时运行。
import asyncio
import threading
import websockets
UEws = None
## 接收网页数据
def WebsocketServerRun():
asyncio.set_event_loop(asyncio.new_event_loop())
# 启动 WebSocket 服务端并等待连接
start_server = websockets.serve(
handle_websocket_connection,"192.168.3.17",9090)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
async def handle_websocket_connection(websocket, path):
# 处理新的 WebSocket 连接
print("New WebSocket client connected")
# with open('binary_file.audio', 'wb') as f:
# 循环接收客户端消息并处理
async for message in websocket:
if isinstance(message, bytes):
if None != UEws:
await UEws.send(message)
else:
print(f"Received message from client: {message}")
# 处理完毕,关闭 WebSocket 连接
print("WebSocket connection closed")
thread = threading.Thread(target=WebsocketServerRun)
thread.start()
## 发送给UE数据
def UEWebsocketServerRun():
asyncio.set_event_loop(asyncio.new_event_loop())
# 启动 WebSocket 服务端并等待连接
start_server = websockets.serve(
UEhandle_websocket_connection,"192.168.3.17",9091)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
async def UEhandle_websocket_connection(websocket, path):
global UEws
# 处理新的 WebSocket 连接
print("UE New WebSocket client connected")
UEws = websocket
async for message in websocket:
a = 1
print("UE WebSocket connection closed")
uethread = threading.Thread(target=UEWebsocketServerRun)
uethread.start()
我的知识星球
请关注公众号g0415shenw 加入知识星球。
星球地址 https://t.zsxq.com/15EvfoA7n
星球有本人经验心得全部总结 涵盖音视频,gb28181、虚幻引擎、其他编程工具等等。另外还可以在星球提问,我会尽力答复,等于给您多了一个引路人。
版权归原作者 g0415shenw 所有, 如有侵权,请联系我们删除。