espressif / esp-adf

Espressif Audio Development Framework
Other
1.54k stars 677 forks source link

关于ADF (latest) opus 编解码的问题 (AUD-4947) #1093

Open danielwibree opened 11 months ago

danielwibree commented 11 months ago

我司产品使用ESP32S3官方16R8 模块, 目前需要添加对讲功能(双工),准备使用OPUS编码,采用ADF加IDF 4.4.2,目前先使用官方esp32-s3-korvo-2 V3 开发板做loopback测试。 基于ADF 搭建的上行和下行 pipeline 如下:

upload_pipeline: I2S_Reader--> OPUS_Encoder --> Raw_Reader download_pipeline: RAW_Writter -->OPUS_Decoder-->I2S_Writter

然后创建一个task, 把RAW_Reader 数据发回到I2S_Writter。

疑问1: OPUS_Encoder,Raw_Reader 的采样率设置为32KHZ,44.1KHZ,48KHZ的时候(mono,16bit), loopback声音正常, 但是在运行中,采样率和配置的参数不匹配(audio_element_getinfo函数), 例如,设置采样率32KHZ, 尝试获取OPUS_Encoder 的采样率固定为44.1KHZ, stereo,而 I2S_Reader的信息正确(32KHZ,mono)。而无论设置何种采样率 OPUS_Decoder (AEL_MSG_CMD_REPORT_MUSIC_INFO) 汇报的采样率固定为48KHZ,然后设置到I2S_Writter(i2s_stream_set_clk), 再获取I2S_Writter的信息,采样率和I2S_Reader的匹配(32KHZ)?

疑问2: OPUS_Encoder,Raw_Reader 的采样率设置为8K,16K, 则声音loopback 像慢放一样,请问是何问题?

问题3: 为了确认OPUS编码采样率的问题,我测试了pipeline_recording_to_sdcard 历程,选择OPUS编码,8KHZ采样率, 测试发现其中的i2s_stream_reader,fatfs_stream_writer 采样率均为8KHZ, 尝试播放录制的rec.opus文件,播放器显示 采样率为48KHZ, 但是文件属性信息里面,显示采样率为8KHZ, 声音播放正常

PumpkinLin314 commented 11 months ago

Q1:

Q2:

Q3:

danielwibree commented 11 months ago

感谢您的回复。

关于 resampler, 考虑到esp32-s3-korvo-2 V3 硬件ADC/DAC 为独立chipset, 共享I2S0,这样需要ADC/DAC 运行再相同的采样率,这也是为何8/16KHZ opus 编码,loopback后OPUS decoder 48KHZ 输出 不能工作的原因(32/44.1可以工作是因为采样率相近?)。 这样为了避免使用resampler, 我们产品的硬件设置是否可以使用I2S1 驱动其中一个codec chipset, 实现ADC/DAC I2S 分离,可以运行于不对称的采样率(ESP32S3 有两路 I2S)?

另外当前我们设计的用于websocket (ESP32S3 作为http server)传输opus的两路pipeline如下,请帮忙确认下是否合理?

上行录音 I2S_Reader-->opus encoder -->raw reader -->(read task) websocek

下行播放
websocket (write task)--> raw writter-->opus decoder -->resampler -->I2S Writter

PumpkinLin314 commented 11 months ago

Q1: 关于使用 esp32-s3-korvo-2 V3 配置采样率 8K 无法工作, 而较高采样率可以工作. A1:

Q2: 是否可以分别使用单独的 i2s 模块驱动 codec chip. A2: 支持. 可以参考 ESP32-LyraT-Mini V1.2 开发板的设计. 开发板使用 i2s0 驱动 DAC, 使用 i2s1 驱动 ADC, 并可以工作在不同采样率.

Q3: 使用 websocket 传输音频的方案是否合理. A3: 是可行的.

danielwibree commented 11 months ago

Hi PumpkinLin314 感谢您的回复, 目前我们才用上述两路pipeline设计,websocket透传基本可以行, 但是还碰到如下问题,麻烦您解答。 因为设计采用半双工方式,类似于PUSH To Talk 对讲机。所以在例如,倾听对方讲话的时候,我们设想是PAUSE或者STOP recording pipeline。
在创建两路pipeline之后(PIPELINE INIT 状态),我们没有立即开始RUN, 而是等WEBsocket建立之后,才根据需要RUN 对应的pipeline, 通话开始之后,会有上面描述的PUASE/STOP, RUN 状态之间的切换。 我们发现RECORDING pipeline, 如果直接使用PAUSE的方式,再次resume(先清空buffer)的时候,会启动不起来(表现为等待resume超时),当然也有能成功resume的时候,但是大部分时刻都是失败。 后来参照其他历程,我们使用如下方法(原理类似于触发第一级element I2S_READER,使其触发整个链路结束),如下 audio_element_set_ringbuf_done(rec_inst->i2s_stream_reader); audio_element_set_ringbuf_done(rec_inst->encoder); audio_element_set_ringbuf_done(rec_inst->raw_reader);

然后resume的时候采用如下方法: audio_pipeline_reset_ringbuffer(rec_inst->pipeline_ul); audio_pipeline_reset_elements(rec_inst->pipeline_ul); audio_pipeline_change_state(rec_inst->pipeline_ul, AEL_STATE_INIT); audio_pipeline_run(rec_inst->pipeline_ul);

这样操作,大部分状态都能重启成功,虽然偶尔还是会失败。

请问对于如下设计的两个链路 上行录音 I2S_Reader-->opus encoder -->raw reader -->(read task) websocek 下行播放 websocket (write task)--> raw writter-->opus decoder -->resampler -->I2S Writter

  1. 正确pause 和 resume的 方法是什么?
  2. 我们需要在resume的时候重置buffer, 因为如果不重置,opus ogg bitstream数据包可能对不起来,导致不能识别
  3. opus codec的lib是不是libopus&libogg组合(map文件可以看到相关api),如果我们不想用ogg bitstream封装,而是 原始opus数据包,目前的lib有没有API或者配置可以支持这样做?
jason-mao commented 2 months ago

@danielwibree 请问,这个问题还有更新吗?