Oboe-Android使用回调读取音频流数据及相关开发问题咨询
问题1:使用read函数持续读取音频数据的实现方式
read是同步阻塞接口,调用后会等待指定帧数的音频数据准备完成才会返回,因此必须放在独立的工作线程中执行,不能占用主线程:
- 首先在
HelloOboeEngine类中新增一个线程成员变量,比如std::thread mReadThread;,同时保留你现有的isRecording标志位作为循环终止条件 - 在
start()方法中启动流成功后,启动工作线程执行读取循环 - 工作线程内的逻辑:只要
isRecording为true且流处于活跃状态,就循环调用mRecordingStream->read(),每次读取建议按流的burst size大小(调用stream->getFramesPerBurst()获取)来设置读取帧数,避免过高延迟 - 停止录音时先把
isRecording设为false,再调用join()等待工作线程退出,再执行流关闭逻辑
示例的线程循环代码:
void HelloOboeEngine::readLoop() { constexpr int kBufferSize = 1024; // 也可以用getFramesPerBurst()动态获取 auto* buffer = new uint8_t[kBufferSize * oboe::StreamFormat::getSizeInBytes(mFormat) * mChannelCount]; while (isRecording) { auto result = mRecordingStream->read(buffer, kBufferSize, 1e9 /* 超时时间1秒 */); if (result != oboe::Result::OK) { // 处理读取错误,比如流断开的情况 LOGE("Read error: %s", oboe::convertToText(result.error())); break; } int framesRead = result.value(); // 这里处理读取到的buffer数据,长度是framesRead * 单帧字节数 } delete[] buffer; } // start方法里新增启动线程逻辑 void HelloOboeEngine::start() { isRecording = true; oboe::Result result = createStream(); if (result != oboe::Result::OK) { closeStream(mRecordingStream); return; } result = mRecordingStream->requestStart(); if (result != oboe::Result::OK) { closeStream(mRecordingStream); return; } // 启动读取线程 mReadThread = std::thread(&HelloOboeEngine::readLoop, this); } // 停止逻辑新增线程等待 void HelloOboeEngine::stop() { isRecording = false; if (mReadThread.joinable()) { mReadThread.join(); } closeStream(mRecordingStream); }
问题2:回调方式获取音频数据的实现步骤
回调是Oboe更推荐的低延迟实现方式,不需要你自己维护线程,Oboe会自动在高优先级音频线程中调用回调接口传递数据,实现步骤如下:
- 让你的
HelloOboeEngine类继承oboe::AudioStreamDataCallback,重写onAudioReady方法:
class HelloOboeEngine : public oboe::AudioStreamDataCallback { // 其他原有成员... oboe::DataCallbackResult onAudioReady(oboe::AudioStream *stream, void *audioData, int32_t numFrames) override; };
- 在
createStream方法的构建器中设置回调对象:
oboe::Result HelloOboeEngine::createStream() { oboe::AudioStreamBuilder builder; return builder.setPerformanceMode(oboe::PerformanceMode::LowLatency) ->setDirection(oboe::Direction::Input) ->setFormat(mFormat) ->setFormatConversionAllowed(true) ->setChannelConversionAllowed(true) ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium) ->setDataCallback(this) // 新增这行设置回调 ->openStream(mRecordingStream); }
- 在
onAudioReady方法中直接处理音频数据即可,audioData就是当前批次的麦克风输入数据,numFrames是本次回调的帧数:
oboe::DataCallbackResult HelloOboeEngine::onAudioReady(oboe::AudioStream *stream, void *audioData, int32_t numFrames) { // 直接处理audioData中的音频数据,注意不要做耗时操作、不要加锁阻塞,否则会出现音频卡顿 // 处理完成后返回Continue让流继续工作 return oboe::DataCallbackResult::Continue; }
注意回调运行在实时音频线程中,不要执行内存分配、文件读写、锁等待这类耗时操作,如果需要长时间处理数据可以先把数据拷贝到环形缓冲区,再交给其他工作线程处理。
内容的提问来源于stack exchange,提问作者Santiago Alejandro Torres Arag
相关产品推荐
相关产品推荐

