Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

CodecOpusOgg encoded data cannot be played #2043

Answered by pschatzmann
wangzongming asked this question in Q&A
Discussion options

Problem Description

hi, bro. First of all, thank you very much for your project. He did me a big favor! 🌹

Now I want to send the opus data to the server via ws, and the server will save it as record.opus.

It was found that the saved file has no length. The reason should be that there is no correct ending.

My Arduino code is in a mess! I hope you can help me answer this.

Arduino Code

#include <WiFi.h>
#include <WebSocketsClient.h>
#include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecOpusOgg.h"
#include <driver/i2s.h>
// 聆听模式
bool LISTENING = true;
bool ended = false;
// WEBSOCKET
SemaphoreHandle_t wsMutex;
WebSocketsClient webSocket;
// AUDIO INPUT SETTINGS
class WebsocketStream : public Print
{
public:
 virtual size_t write(uint8_t b) override
 {
 if (!webSocket.isConnected())
 {
 return 1;
 }
 if (xSemaphoreTake(wsMutex, pdMS_TO_TICKS(100)) == pdTRUE)
 {
 webSocket.sendBIN(&b, 1);
 xSemaphoreGive(wsMutex);
 return 1;
 }
 return 1;
 }
 virtual size_t write(const uint8_t *buffer, size_t size) override
 {
 if (size == 0 || !webSocket.isConnected())
 {
 return size;
 }
 if (xSemaphoreTake(wsMutex, pdMS_TO_TICKS(100)) == pdTRUE)
 {
 Serial.print("发送大小:");
 Serial.println(size);
 webSocket.sendBIN(buffer, size);
 xSemaphoreGive(wsMutex);
 return size;
 }
 return size;
 }
};
WebsocketStream wsStream;
I2SStream i2sInput;
// VolumeStream volume(i2sInput);
// StreamCopy micToWsCopier(wsStream, volume);
OpusOggEncoder encoder;
EncodedAudioStream out(&wsStream, &encoder);
StreamCopy micToWsCopier(out, i2sInput);
const int MIC_COPY_SIZE = 1024; // 或更高
void micTask(void *parameter)
{
 // Configure and start I2S input stream.
 auto i2sConfig = i2sInput.defaultConfig(RX_MODE);
 i2sConfig.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
 i2sConfig.sample_rate = 16000;
 i2sConfig.channels = 1;
 i2sConfig.i2s_format = I2S_LEFT_JUSTIFIED_FORMAT;
 i2sConfig.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
 // Configure your I2S input pins appropriately here:
 i2sConfig.pin_bck = 4;
 i2sConfig.pin_ws = 5;
 i2sConfig.pin_data = 6;
 i2sConfig.port_no = 0;
 i2sInput.begin(i2sConfig);
 // auto vcfg = volume.defaultConfig();
 // vcfg.copyFrom(i2sConfig);
 // vcfg.allow_boost = true;
 // volume.begin(vcfg);
 // volume.setVolume(12);
 // 配置 Opus 编码器
 OpusEncoderSettings cfg;
 cfg.sample_rate = i2sConfig.sample_rate;
 cfg.channels = i2sConfig.channels;
 // configure additinal parameters
 cfg.application = OPUS_APPLICATION_RESTRICTED_LOWDELAY;
 cfg.frame_sizes_ms_x2 = OPUS_FRAMESIZE_100_MS;
 // enc.config().complexity = 5; // 支持的范围是0-10,其中10代表最高的复杂度。
 if (!encoder.begin(cfg))
 {
 Serial.println("❌ Opus encoder failed to initialize!");
 while (true)
 {
 delay(1000); // 或根据你项目逻辑退出
 }
 }
 out.begin(i2sConfig);
 long start_time = millis();
 while (1)
 {
 if (webSocket.isConnected())
 {
 if ((millis() - start_time) > 6000)
 {
 micToWsCopier.end();
 i2sInput.end(); // 停止 I2S 输入
 out.flush(); // 刷新剩余数据
 vTaskDelay(100); // 等待数据发出
 encoder.end(); // 结束编码器,写入最后封包(EOS)
 vTaskDelay(1000); // 给 websocket 一点时间发送完最后一包
 Serial.println("----"); 
 ended = true;
 Serial.println("结束");
 webSocket.disconnect();
 }
 else
 {
 // Use smaller chunk size to avoid blocking too long
 // micToWsCopier.copyBytes(MIC_COPY_SIZE);
 micToWsCopier.copy();
 // Yield more frequently
 vTaskDelay(50);
 }
 }
 else
 {
 vTaskDelay(100);
 }
 }
}
// I2S 配置见上面
void setup()
{
 Serial.begin(115200);
 AudioLogger::instance().begin(Serial, AudioLogger::Debug);
 // AudioLogger::instance().begin(Serial, AudioLogger::Info);
 wsMutex = xSemaphoreCreateMutex();
 delay(2000);
 Serial.println("开始连接wifi");
 WiFi.begin("联域科技", "lykj987654321");
 while (WiFi.status() != WL_CONNECTED)
 {
 Serial.print(".");
 delay(500);
 }
 Serial.println("wifi连接成功");
 webSocket.begin("192.168.3.16", 8988, "/");
 webSocket.onEvent(webSocketEvent);
 webSocket.setReconnectInterval(5000);
 xTaskCreatePinnedToCore(
 micTask, // Function
 "Microphone Task", // Name
 1024 * 8, // Stack size
 NULL, // Parameters
 4, // Priority
 NULL, // Handle
 1 // Core 1 (application core)
 );
}
void loop()
{
 if (!ended)
 {
 webSocket.loop();
 }
}
void webSocketEvent(WStype_t type, uint8_t *payload, size_t length)
{ 
 switch (type)
 {
 case WStype_DISCONNECTED:
 Serial.println("WebSocket Disconnected");
 break;
 case WStype_CONNECTED:
 Serial.println("WebSocket Connected");
 break;
 case WStype_TEXT:
 Serial.printf("Received Text: %s\n", payload);
 break;
 case WStype_BIN:
 break; 
 }
}

Node Code


const WebSocket = require('ws')
var fs = require('fs')
// 和 esp32 连接的 ws 服务
let wss;
// 与 esp32 的通信
function wss_start() {
	wss = new WebSocket.Server({ port: 8988 });
	wss.on('connection', function connection(ws) {
		console.log('\n\n esp32 连接成功');
		const output = fs.createWriteStream('record.opus');
		ws.on('message', data => {
			console.log(data.length)
			output.write(data)
		});
		// 处理WebSocket断开
		ws.on('close', () => {
			console.log("服务断开");
			// 保险:延迟关闭文件,确保缓冲区刷新完成
			setTimeout(() => {
				output.end();
				console.log("文件写入完成");
			}, 300); // 等待 300ms
		});
	});
	console.log('WebSocket server is running on ws://localhost:8988');
}
wss_start();

Device Description

ESP32s3

Sketch

-

Other Steps to Reproduce

No response

What is your development environment (incl. core version info)

No response

I have checked existing issues, discussions and online documentation

  • I confirm I have checked existing issues, discussions and online documentation
You must be logged in to vote

Please follow the advice give in this Wiki and build and test your sketch in steps.

I would suggest to start to with a very simple sketch that just outputs the i2s data to a CSVOutput, if this is working replace the output with a HexDumpOutput. As the next step add the encoding. Only if this is working replace the output class.

Test each step separatley and advance only when you have the confirmation that it is wokring.
Also test your WebsocketStream class separatly by sending some defined data.

Replies: 2 comments

Comment options

This is the detailed information of the audio:

Input #0, ogg, from './record.opus':KB vq= 0KB sq= 0B
 Duration: 00:00:00.05, start: -0.009604, bitrate: 1562 kb/s
 Stream #0:0: Audio: opus, 48000 Hz, mono, fltp
 -0.07 M-A: 0.000 fd= 0 aq= 0KB vq= 0KB sq= 0B
You must be logged in to vote
0 replies
Comment options

Please follow the advice give in this Wiki and build and test your sketch in steps.

I would suggest to start to with a very simple sketch that just outputs the i2s data to a CSVOutput, if this is working replace the output with a HexDumpOutput. As the next step add the encoding. Only if this is working replace the output class.

Test each step separatley and advance only when you have the confirmation that it is wokring.
Also test your WebsocketStream class separatly by sending some defined data.

You must be logged in to vote
0 replies
Answer selected by pschatzmann
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet
Converted from issue

This discussion was converted from issue #2042 on May 21, 2025 06:57.

AltStyle によって変換されたページ (->オリジナル) /