当前位置:   article > 正文

web音频采集与播放

web音频采集与播放

前言

最近写了一个小工具,实现在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 方法获取到用户的媒体设备。

代码如下

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>Audio Capture Example</title>
  7. </head>
  8. <body>
  9. <button id="startButton">Start Recording</button>
  10. <button id="stopButton">Stop Recording</button>
  11. <script>
  12. var mediaStream = null
  13. var scriptNode = null
  14. var socket = null
  15. // 当按钮点击时开始录制音频
  16. //参考 https://blog.csdn.net/qq_46122292/article/details/130326752
  17. document.getElementById('startButton').addEventListener('click', function() {
  18.     socket = new WebSocket('ws://127.0.0.1:9090');
  19.   // 创建 AudioContext 对象
  20.     var audioContext = new (window.AudioContext || window.webkitAudioContext)();
  21.     // 请求用户授权访问麦克风
  22.     navigator.mediaDevices.getUserMedia({ audio: true })
  23.     .then(function(stream) {
  24.         mediaStream = stream
  25.         // 创建 MediaStreamAudioSourceNode 对象
  26.         var source = audioContext.createMediaStreamSource(stream);
  27.         // 创建 ScriptProcessorNode 对象
  28.         scriptNode = audioContext.createScriptProcessor(409611);
  29.         // 连接 source 到 scriptNode
  30.         source.connect(scriptNode);
  31.         // 连接 scriptNode 到 audioContext 的 destination
  32.         scriptNode.connect(audioContext.destination);
  33.         // 当 scriptNode 接收到音频数据时
  34.         scriptNode.onaudioprocess = function(event) { 
  35.             var inputData = event.inputBuffer.getChannelData(0);
  36.             // 在这里可以处理音频数据,例如将其发送到后端服务器
  37.             //console.log('Received audio data:', inputData);
  38.             var arrayBuffer = inputData.buffer;
  39.             socket.send(arrayBuffer);
  40.         };
  41.         console.log('Recording started');
  42.     })
  43.     .catch(function(error) {
  44.         console.error('Error accessing microphone:'error);
  45.     });
  46. });
  47. // 当按钮点击时停止录制音频
  48. document.getElementById('stopButton').addEventListener('click', function() {
  49.     if (mediaStream) {
  50.         mediaStream.getTracks().forEach(function(track) {
  51.             track.stop();
  52.         });
  53.         console.log('Recording stopped');
  54.         mediaStream = null
  55.         scriptNode.disconnect()
  56.         scriptNode = null
  57.         socket.close(1000)
  58.         socket = null
  59.     }
  60. });
  61. </script>
  62. </body>
  63. </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 服务器地址。

示例代码如下

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>PCM Audio Streaming</title>
  7. </head>
  8. <button id="startButton">Start Play</button>
  9. <button id="stopButton">Stop Play</button>
  10. <body>
  11.     <h1>PCM Audio Streaming Demo</h1>
  12.     <script>
  13.       document.getElementById('startButton').addEventListener('click', function() {
  14.         var audioContext = new (window.AudioContext || window.webkitAudioContext)();
  15.         var socket = new WebSocket('ws://192.168.3.17:9091');
  16.         socket.binaryType = "arraybuffer";
  17.         // 当 WebSocket 连接打开时
  18.         socket.onopen = function(event) {
  19.             console.log('WebSocket connected');
  20.         };
  21.         // 当 WebSocket 接收到消息时
  22.         socket.onmessage = function(event) {
  23.             var pcmData = event.data; // 接收到的 PCM 数据
  24.             playPCM(pcmData); // 播放 PCM 数据
  25.         };
  26.         // 当 WebSocket 连接关闭时
  27.         socket.onclose = function(event) {
  28.             console.log('WebSocket closed');
  29.         };
  30.         function playPCM(pcmData) {
  31.             // 将 PCM 数据转换为 Float32Array
  32.             var floatArray = new Float32Array(pcmData);
  33.             // 创建 AudioBuffer
  34.             var audioBuffer = audioContext.createBuffer(1409648000);
  35.             // 将 PCM 数据填充到 AudioBuffer 中
  36.             var audioBufferChannel = audioBuffer.getChannelData(0);
  37.             for (var i = 0; i < 4096; i++) {
  38.                 audioBufferChannel[i] = floatArray[i];
  39.             }
  40.             // 创建 AudioBufferSourceNode
  41.             var sourceNode = audioContext.createBufferSource();
  42.             sourceNode.buffer = audioBuffer;
  43.             sourceNode.connect(audioContext.destination);
  44.             // 播放音频
  45.             sourceNode.start();
  46.         }
  47.       })
  48.        
  49.     </script>
  50. </body>
  51. </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 服务器,分别运行在不同的线程中,使得两个服务器可以同时运行。

  1. import asyncio
  2. import threading
  3. import websockets
  4. UEws = None
  5. ## 接收网页数据
  6. def WebsocketServerRun():
  7.     asyncio.set_event_loop(asyncio.new_event_loop())
  8.     # 启动 WebSocket 服务端并等待连接
  9.     start_server = websockets.serve(
  10.         handle_websocket_connection,"192.168.3.17",9090)   
  11.     asyncio.get_event_loop().run_until_complete(start_server)
  12.     asyncio.get_event_loop().run_forever()
  13. async def handle_websocket_connection(websocket, path):
  14.     # 处理新的 WebSocket 连接
  15.     print("New WebSocket client connected")
  16.     # with open('binary_file.audio''wb') as f:
  17.     # 循环接收客户端消息并处理
  18.     async for message in websocket:
  19.         if isinstance(message, bytes):
  20.             if None != UEws:
  21.                 await UEws.send(message)
  22.         else:
  23.               print(f"Received message from client: {message}")
  24.     # 处理完毕,关闭 WebSocket 连接
  25.     print("WebSocket connection closed")
  26. thread = threading.Thread(target=WebsocketServerRun)
  27. thread.start()
  28. ## 发送给UE数据
  29. def UEWebsocketServerRun():
  30.     asyncio.set_event_loop(asyncio.new_event_loop())
  31.     # 启动 WebSocket 服务端并等待连接
  32.     start_server = websockets.serve(
  33.         UEhandle_websocket_connection,"192.168.3.17",9091)   
  34.     asyncio.get_event_loop().run_until_complete(start_server)
  35.     asyncio.get_event_loop().run_forever()
  36. async def UEhandle_websocket_connection(websocket, path):
  37.     global UEws
  38.     # 处理新的 WebSocket 连接
  39.     print("UE New WebSocket client connected")
  40.     UEws = websocket
  41.     async for message in websocket:
  42.         a = 1
  43.     print("UE WebSocket connection closed")
  44. uethread = threading.Thread(target=UEWebsocketServerRun)
  45. uethread.start()

我的知识星球

请关注公众号g0415shenw 加入知识星球。
星球地址 https://t.zsxq.com/15EvfoA7n
星球有本人经验心得全部总结 涵盖音视频,gb28181、虚幻引擎、其他编程工具等等。另外还可以在星球提问,我会尽力答复,等于给您多了一个引路人。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/765229
推荐阅读
相关标签
  

闽ICP备14008679号