0


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 方法获取到用户的媒体设备。

代码如下

<!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、虚幻引擎、其他编程工具等等。另外还可以在星球提问,我会尽力答复,等于给您多了一个引路人。

标签: 前端 音视频

本文转载自: https://blog.csdn.net/g0415shenw/article/details/137031419
版权归原作者 g0415shenw 所有, 如有侵权,请联系我们删除。

“web音频采集与播放”的评论:

还没有评论