You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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会自动在高优先级音频线程中调用回调接口传递数据,实现步骤如下:

  1. 让你的HelloOboeEngine类继承oboe::AudioStreamDataCallback,重写onAudioReady方法:
class HelloOboeEngine : public oboe::AudioStreamDataCallback {
    // 其他原有成员...
    oboe::DataCallbackResult onAudioReady(oboe::AudioStream *stream, void *audioData, int32_t numFrames) override;
};
  1. 在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);
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 14:15:04