312 lines
12 KiB
C++
312 lines
12 KiB
C++
#include "ogg_demuxer.h"
|
||
#include "esp_log.h"
|
||
|
||
#define TAG "OggDemuxer"
|
||
|
||
/// @brief 重置解封器
|
||
void OggDemuxer::Reset()
|
||
{
|
||
opus_info_ = {
|
||
.head_seen = false,
|
||
.tags_seen = false,
|
||
.sample_rate = 48000
|
||
};
|
||
|
||
state_ = ParseState::FIND_PAGE;
|
||
ctx_.packet_len = 0;
|
||
ctx_.seg_count = 0;
|
||
ctx_.seg_index = 0;
|
||
ctx_.data_offset = 0;
|
||
ctx_.bytes_needed = 4; // 需要4字节"OggS"
|
||
ctx_.seg_remaining = 0;
|
||
ctx_.body_size = 0;
|
||
ctx_.body_offset = 0;
|
||
ctx_.packet_continued = false;
|
||
|
||
// 清空缓冲区数据
|
||
memset(ctx_.header, 0, sizeof(ctx_.header));
|
||
memset(ctx_.seg_table, 0, sizeof(ctx_.seg_table));
|
||
memset(ctx_.packet_buf, 0, sizeof(ctx_.packet_buf));
|
||
}
|
||
|
||
/// @brief 处理数据块
|
||
/// @param data 输入数据
|
||
/// @param size 输入数据大小
|
||
/// @return 已处理的字节数
|
||
size_t OggDemuxer::Process(const uint8_t* data, size_t size)
|
||
{
|
||
size_t processed = 0; // 已处理的字节数
|
||
|
||
while (processed < size) {
|
||
switch (state_) {
|
||
case ParseState::FIND_PAGE: {
|
||
// 寻找页头"OggS"
|
||
if (ctx_.bytes_needed < 4) {
|
||
// 处理不完整的"OggS"匹配(跨数据块)
|
||
size_t to_copy = std::min(size - processed, ctx_.bytes_needed);
|
||
memcpy(ctx_.header + (4 - ctx_.bytes_needed), data + processed, to_copy);
|
||
|
||
processed += to_copy;
|
||
ctx_.bytes_needed -= to_copy;
|
||
|
||
if (ctx_.bytes_needed == 0) {
|
||
// 检查是否匹配"OggS"
|
||
if (memcmp(ctx_.header, "OggS", 4) == 0) {
|
||
state_ = ParseState::PARSE_HEADER;
|
||
ctx_.data_offset = 4;
|
||
ctx_.bytes_needed = 27 - 4; // 还需要23字节完成页头
|
||
} else {
|
||
// 匹配失败,滑动1字节继续匹配
|
||
memmove(ctx_.header, ctx_.header + 1, 3);
|
||
ctx_.bytes_needed = 1;
|
||
}
|
||
} else {
|
||
// 数据不足,等待更多数据
|
||
return processed;
|
||
}
|
||
} else if (ctx_.bytes_needed == 4) {
|
||
// 在数据块中查找完整的"OggS"
|
||
bool found = false;
|
||
size_t i = 0;
|
||
size_t remaining = size - processed;
|
||
|
||
// 搜索"OggS"
|
||
for (; i + 4 <= remaining; i++) {
|
||
if (memcmp(data + processed + i, "OggS", 4) == 0) {
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (found) {
|
||
// 找到"OggS",跳过已搜索的字节
|
||
processed += i;
|
||
|
||
// 不记录找到的"OggS",无必要
|
||
// memcpy(ctx_.header, data + processed, 4);
|
||
processed += 4;
|
||
|
||
state_ = ParseState::PARSE_HEADER;
|
||
ctx_.data_offset = 4;
|
||
ctx_.bytes_needed = 27 - 4; // 还需要23字节
|
||
} else {
|
||
// 没有找到完整"OggS",保存可能的部分匹配
|
||
size_t partial_len = remaining - i;
|
||
if (partial_len > 0) {
|
||
memcpy(ctx_.header, data + processed + i, partial_len);
|
||
ctx_.bytes_needed = 4 - partial_len;
|
||
processed += i + partial_len;
|
||
} else {
|
||
processed += i; // 已搜索所有字节
|
||
}
|
||
return processed; // 返回已处理的字节数
|
||
}
|
||
} else {
|
||
ESP_LOGE(TAG, "OggDemuxer run in error state: bytes_needed=%zu", ctx_.bytes_needed);
|
||
Reset();
|
||
return processed;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case ParseState::PARSE_HEADER: {
|
||
size_t available = size - processed;
|
||
|
||
if (available < ctx_.bytes_needed) {
|
||
// 数据不足,复制可用的部分
|
||
memcpy(ctx_.header + ctx_.data_offset,
|
||
data + processed, available);
|
||
|
||
ctx_.data_offset += available;
|
||
ctx_.bytes_needed -= available;
|
||
processed += available;
|
||
return processed; // 等待更多数据
|
||
} else {
|
||
// 有足够的数据完成页头
|
||
size_t to_copy = ctx_.bytes_needed;
|
||
memcpy(ctx_.header + ctx_.data_offset,
|
||
data + processed, to_copy);
|
||
|
||
processed += to_copy;
|
||
ctx_.data_offset += to_copy;
|
||
ctx_.bytes_needed = 0;
|
||
|
||
// 验证页头
|
||
if (ctx_.header[4] != 0) {
|
||
ESP_LOGE(TAG, "无效的Ogg版本: %d", ctx_.header[4]);
|
||
state_ = ParseState::FIND_PAGE;
|
||
ctx_.bytes_needed = 4;
|
||
ctx_.data_offset = 0;
|
||
break;
|
||
}
|
||
|
||
ctx_.seg_count = ctx_.header[26];
|
||
if (ctx_.seg_count > 0 && ctx_.seg_count <= 255) {
|
||
state_ = ParseState::PARSE_SEGMENTS;
|
||
ctx_.bytes_needed = ctx_.seg_count;
|
||
ctx_.data_offset = 0;
|
||
} else if (ctx_.seg_count == 0) {
|
||
// 没有段,直接跳到下一个页面
|
||
state_ = ParseState::FIND_PAGE;
|
||
ctx_.bytes_needed = 4;
|
||
ctx_.data_offset = 0;
|
||
} else {
|
||
ESP_LOGE(TAG, "无效的段数: %u", ctx_.seg_count);
|
||
state_ = ParseState::FIND_PAGE;
|
||
ctx_.bytes_needed = 4;
|
||
ctx_.data_offset = 0;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
case ParseState::PARSE_SEGMENTS: {
|
||
size_t available = size - processed;
|
||
|
||
if (available < ctx_.bytes_needed) {
|
||
memcpy(ctx_.seg_table + ctx_.data_offset,
|
||
data + processed, available);
|
||
|
||
ctx_.data_offset += available;
|
||
ctx_.bytes_needed -= available;
|
||
processed += available;
|
||
return processed; // 等待更多数据
|
||
} else {
|
||
size_t to_copy = ctx_.bytes_needed;
|
||
memcpy(ctx_.seg_table + ctx_.data_offset,
|
||
data + processed, to_copy);
|
||
|
||
processed += to_copy;
|
||
ctx_.data_offset += to_copy;
|
||
ctx_.bytes_needed = 0;
|
||
|
||
state_ = ParseState::PARSE_DATA;
|
||
ctx_.seg_index = 0;
|
||
ctx_.data_offset = 0;
|
||
|
||
// 计算数据体总大小
|
||
ctx_.body_size = 0;
|
||
for (size_t i = 0; i < ctx_.seg_count; ++i) {
|
||
ctx_.body_size += ctx_.seg_table[i];
|
||
}
|
||
ctx_.body_offset = 0;
|
||
ctx_.seg_remaining = 0;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case ParseState::PARSE_DATA: {
|
||
while (ctx_.seg_index < ctx_.seg_count && processed < size) {
|
||
uint8_t seg_len = ctx_.seg_table[ctx_.seg_index];
|
||
|
||
// 检查段数据是否已经部分读取
|
||
if (ctx_.seg_remaining > 0) {
|
||
seg_len = ctx_.seg_remaining;
|
||
} else {
|
||
ctx_.seg_remaining = seg_len;
|
||
}
|
||
|
||
// 检查缓冲区是否足够
|
||
if (ctx_.packet_len + seg_len > sizeof(ctx_.packet_buf)) {
|
||
ESP_LOGE(TAG, "包缓冲区溢出: %zu + %u > %zu", ctx_.packet_len, seg_len, sizeof(ctx_.packet_buf));
|
||
state_ = ParseState::FIND_PAGE;
|
||
ctx_.packet_len = 0;
|
||
ctx_.packet_continued = false;
|
||
ctx_.seg_remaining = 0;
|
||
ctx_.bytes_needed = 4;
|
||
return processed;
|
||
}
|
||
|
||
// 复制数据
|
||
size_t to_copy = std::min(size - processed, (size_t)seg_len);
|
||
memcpy(ctx_.packet_buf + ctx_.packet_len, data + processed, to_copy);
|
||
|
||
processed += to_copy;
|
||
ctx_.packet_len += to_copy;
|
||
ctx_.body_offset += to_copy;
|
||
ctx_.seg_remaining -= to_copy;
|
||
|
||
// 检查段是否完整
|
||
if (ctx_.seg_remaining > 0) {
|
||
// 段不完整,等待更多数据
|
||
return processed;
|
||
}
|
||
|
||
// 段完整
|
||
bool seg_continued = (ctx_.seg_table[ctx_.seg_index] == 255);
|
||
|
||
if (!seg_continued) {
|
||
// 包结束
|
||
if (ctx_.packet_len) {
|
||
if (!opus_info_.head_seen) {
|
||
if (ctx_.packet_len >=8 && memcmp(ctx_.packet_buf, "OpusHead", 8) == 0) {
|
||
opus_info_.head_seen = true;
|
||
if (ctx_.packet_len >= 19) {
|
||
opus_info_.sample_rate = ctx_.packet_buf[12] |
|
||
(ctx_.packet_buf[13] << 8) |
|
||
(ctx_.packet_buf[14] << 16) |
|
||
(ctx_.packet_buf[15] << 24);
|
||
ESP_LOGD(TAG, "OpusHead found, sample_rate=%d", opus_info_.sample_rate);
|
||
}
|
||
ctx_.packet_len = 0;
|
||
ctx_.packet_continued = false;
|
||
ctx_.seg_index++;
|
||
ctx_.seg_remaining = 0;
|
||
continue;
|
||
}
|
||
}
|
||
if (!opus_info_.tags_seen) {
|
||
if (ctx_.packet_len >= 8 && memcmp(ctx_.packet_buf, "OpusTags", 8) == 0) {
|
||
opus_info_.tags_seen = true;
|
||
ESP_LOGD(TAG, "OpusTags found.");
|
||
ctx_.packet_len = 0;
|
||
ctx_.packet_continued = false;
|
||
ctx_.seg_index++;
|
||
ctx_.seg_remaining = 0;
|
||
continue;
|
||
}
|
||
}
|
||
if (opus_info_.head_seen && opus_info_.tags_seen) {
|
||
if (on_demuxer_finished_) {
|
||
on_demuxer_finished_(ctx_.packet_buf, opus_info_.sample_rate, ctx_.packet_len);
|
||
}
|
||
} else {
|
||
ESP_LOGW(TAG, "当前Ogg容器未解析到OpusHead/OpusTags,丢弃");
|
||
}
|
||
}
|
||
ctx_.packet_len = 0;
|
||
ctx_.packet_continued = false;
|
||
} else {
|
||
ctx_.packet_continued = true;
|
||
}
|
||
|
||
ctx_.seg_index++;
|
||
ctx_.seg_remaining = 0;
|
||
}
|
||
|
||
if (ctx_.seg_index == ctx_.seg_count) {
|
||
// 检查是否所有数据体都已读取
|
||
if (ctx_.body_offset < ctx_.body_size) {
|
||
ESP_LOGW(TAG, "数据体不完整: %zu/%zu",
|
||
ctx_.body_offset, ctx_.body_size);
|
||
}
|
||
|
||
// 如果包跨页,保持packet_len和packet_continued
|
||
if (!ctx_.packet_continued) {
|
||
ctx_.packet_len = 0;
|
||
}
|
||
|
||
// 进入下一页面
|
||
state_ = ParseState::FIND_PAGE;
|
||
ctx_.bytes_needed = 4;
|
||
ctx_.data_offset = 0;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return processed;
|
||
}
|
||
|