websocket下发mp3帧数据时一个包被分包为几个子包而导致mp3解码失败而播放卡顿有杂音或断播的解决方法:
websocket发的mp3帧数据格式自定义为:
111+3字节mp3帧长度+mp3帧数据+111+111
如果想可靠传输mp3数据来播放:
第一 :服务器下发音频数据时需要一帧一帧的发,不能合在一起发,
第二:tcp收到的包可能被拆分为2-3个包了,然后就不完整,mp3解码就失败,识别到分包后需要合并后再送去mp3解码
服务器下发音频数据时需要一帧一帧的发:
private async Task StreamMp3WithNaudioSimpleAsync(Stream mp3Stream, mp3_play_t_do_info inn1,
CancellationToken cancellationToken = default)
{
// 直接使用NAudio的Mp3FileReader,它内部会处理帧解析
using (var mp3Reader = new NAudio.Wave.Mp3FileReader(mp3Stream))
{
//byte[] frameBuffer = new byte[4096]; // MP3帧通常小于4KB
int framesSent = 0;
while (true)
{
if (dic_Sockets[inn1.clientUrl1].is_need_end_the_tts_Sending == 1)
{
await SendCompletionAsync(inn1, cancellationToken);
return;
}
// 读取一个MP3帧
var frame = mp3Reader.ReadNextFrame();
if (frame == null)
break; // 没有更多帧
byte[] buffer2 = new byte[1+3+ frame.RawData.Length+2];
buffer2[0] = 111;//mp3 play
buffer2[1] = (byte)((frame.RawData.Length)&0xff);
buffer2[2] = (byte)((frame.RawData.Length>>8) & 0xff);
buffer2[3] = (byte)((frame.RawData.Length >> 16) & 0xff);
buffer2[4 + frame.RawData.Length] = 111;//end maker
buffer2[4 + frame.RawData.Length+1] = 111;//end maker
int need_ack_mode = 0;
Array.Copy(frame.RawData, 0, buffer2, 4, frame.RawData.Length);
if (need_ack_mode == 0)
{
try
{
await dic_Sockets[inn1.clientUrl1].conn.Send(buffer2);
}
catch (Exception err1)
{
log.fvdou_append_error_to_log_txtH("conn Send bytes err", $"{err1.Message}{err1.StackTrace}");
return;
}
// await Task.Delay(4);
}
if (need_ack_mode == 1)
{
int max_trynum = 0;
while (max_trynum <= 4)
{
max_trynum++;
dic_Sockets[inn1.clientUrl1].wait_ack.Reset();
try
{
await dic_Sockets[inn1.clientUrl1].conn.Send(buffer2);
}
catch (Exception err1)
{
log.fvdou_append_error_to_log_txtH("conn Send bytes err", $"{err1.Message}{err1.StackTrace}");
return;
}
bool isok_ret1 = dic_Sockets[inn1.clientUrl1].wait_ack.WaitOne(800);
if (isok_ret1 == true && dic_Sockets[inn1.clientUrl1].ACK_NUM == 111)
{
break;
}
else
{
log.fvdou_append_error_to_log_txtH("conn Send bytes err", $"dic_Sockets[inn1.clientUrl1].ACK_NUM = {dic_Sockets[inn1.clientUrl1].ACK_NUM}");
}
}
}
framesSent++;
// 每10帧让出一次,避免阻塞
if (framesSent % 20 == 0)
await Task.Delay(3);
}
log.fvdou_append_error_to_log_txtH("StreamMp3WithNaudioSimpleAsync",
$"{inn1.mac_id}发送完成,共 {framesSent} 帧");
await SendCompletionAsync(inn1, cancellationToken);
}
}
=======================================================
单片机端:
// 定义 WebSocket 数据重组器
typedef struct {
uint8_t buffer[64 * 1024]; // 64KB 缓冲区
size_t length;
bool expecting_more;
uint8_t opcode;
} websocket_reassembler_t;
static websocket_reassembler_t * ws_reassembler;
。。。。。。
int len_frame_=0;
static void websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
。。。。。。。
case WEBSOCKET_EVENT_DATA:
if (data->op_code == 0x2) { // Opcode 0x2 indicates binary data
if((((data->data_ptr[0]==111&&ws_reassembler->length==0)||is_fenbao_ing==1)) || (ws_reassembler->length>0) ){
if(data->data_ptr[0]==111&&ws_reassembler->length==0){
len_frame_= data->data_ptr[1]+data->data_ptr[2]*256+data->data_ptr[3]*256*256;
len_frame_=len_frame_+1+3+2;//1帧头+3字节长度+2字节帧尾。
if(len_frame_!=data->data_len){//如果分包发生了!!!
is_fenbao_ing=1;
}else{
is_fenbao_ing=0;
len_frame_=0;
}
}
if(is_fenbao_ing==1){
ESP_LOGI(TAG, "is_fenbao_ing=1,ws_reassembler->length=%d ,len_frame_=%d ",ws_reassembler->length,len_frame_);
}
if(!data->fin || (len_frame_!=0&&len_frame_>(ws_reassembler->length+data->data_len ) )) {
// 这是分片帧的一部分
if(!ws_reassembler->expecting_more) {
// 开始新的分片序列
ws_reassembler->opcode = data->op_code ;
ws_reassembler->length = 0;
ws_reassembler->expecting_more = true;
}
// 存储数据
if(ws_reassembler->length + data->data_len <= sizeof(ws_reassembler->buffer)) {
memcpy(ws_reassembler->buffer + ws_reassembler->length, data->data_ptr, data->data_len);
ws_reassembler->length += data->data_len;
}
ESP_LOGI(TAG, "not data->fin data come!");
} else {
// FIN 为 true,这是最后一片或独立消息
if(ws_reassembler->expecting_more) {
// 这是分片的最后一部分
if(ws_reassembler->length + data->data_len <= sizeof(ws_reassembler->buffer)) {
memcpy(ws_reassembler->buffer + ws_reassembler->length, data->data_ptr, data->data_len);
ws_reassembler->length += data->data_len;
// 现在有了完整消息,处理它
process_complete_mp3_data(ws_reassembler->buffer, ws_reassembler->length);
// 重置重组器
ws_reassembler->length = 0;
ws_reassembler->expecting_more = false;
}
} else {
// 独立消息,直接处理
process_complete_mp3_data((uint8_t * )data->data_ptr, data->data_len);
}
is_fenbao_ing=0;
}
。。。。。。。。
uint8_t is_fenbao_ing=0;
void play_A_mp3_stream(int len,const char * datas);
void process_complete_mp3_data(uint8_t *data, size_t len) {
// 验证是否是完整的 MP3 数据
//printf("收到完整 WebSocket 消息,长度: %d\n", len);
char sendata[1];
if(len<6 || data[len-1]!=111||data[len-2]!=111){
/* sendata[0]=222;
esp_websocket_client_send_bin_partial(client, sendata, sizeof(sendata), portMAX_DELAY);
esp_websocket_client_send_fin(client, portMAX_DELAY);
*/
is_fenbao_ing=1;
ESP_LOGE(TAG, "len<6 ||data[len-1]!=111||data[len-2]!=111 len=%d\r\n",len);
ESP_LOG_BUFFER_HEX("Received wrong binary data", data, len);
return;
}
is_fenbao_ing=0;
// 发送到 ringbuffer
if(buf_handle_for_mp3_play != nullptr) {
UBaseType_t res2 = xRingbufferSend(
buf_handle_for_mp3_play,
data,
len-2, //减去末尾2字节
pdMS_TO_TICKS(4000)
);
if(res2 != pdTRUE) {
printf("Failed to send item to ringbuffer\n");
}
}
/* sendata[0]=111;
esp_websocket_client_send_bin_partial(client, sendata, sizeof(sendata), portMAX_DELAY);
esp_websocket_client_send_fin(client, portMAX_DELAY);*/
G_is_can_INTO_VAD=0;
G_is_VAD_ing=0;
G_left_times_to_INTO_VAD=0;//防止上次的mp3结束时的 G_left_times_to_INTO_VAD=40; 影响本次的播放,
}