【问题标题】:play audio in real-time using opensl es and the android ndk使用opensl es和android ndk实时播放音频
【发布时间】:2012-12-23 20:52:36
【问题描述】:

我想做的是录制一个声音,处理它就像为录制的音频添加一些效果一样,然后使用 opensl 尽可能快地重放它。我无法在网上找到有关执行此类操作的信息,所以我实际上有几个问题。

我看过 android 原生音频示例,以及以 android ndk 开头的书,这让我开始使用 opensl,但它们并没有真正帮助我很好地理解录音。

1) 首先,要立即播放录制的音频,我能否在录制线程写入缓冲区的同时从播放音频的线程读取缓冲区?我知道有人可能会为这个想法发疯,因为同时通过两个不同的线程访问内存中的某个对象的想法可能会导致问题,但如果确保记录线程总是在之前写入内存播放音频线程,那有可能吗?

2) 或者实时回放,我将使用带有两个或三个非常小的缓冲区的缓冲区队列,以及每次填充一个缓冲区时调用的回调,然后在填充下一个缓冲区时播放该缓冲区通过录制音频线程?但我正在阅读,有人说回调并不总是被调用(我发现的最有用的链接是:https://groups.google.com/forum/#!msg/android-ndk/hLSygQrmcPI/qtwB76JNa_EJ)。此外,这意味着录制的音频和播放的音频之间的时间差将是缓冲区的大小加上回调让录制对象知道使用下一个缓冲区开始录制所需的时间。我认为录音机停止录音和重新开始录音之间会有一个间隙。

3) android 录制缓冲区队列也是我无法理解的地方。我必须在android中使用缓冲区队列来录制音频吗?或者是否可以在不使用缓冲区队列的情况下直接记录到缓冲区?我遇到了这个问题,因为SLAndroidSimpleBufferQueueItf 的 clear 方法实际上似乎不起作用。显然它是一个错误。而且我似乎无法在缓冲区已填充数据后对其进行记录。由于 clear 方法似乎不起作用,我如何告诉记录器要记录到队列中的哪个缓冲区?

我知道这有很多问题要问,我并没有说出我尝试过的所有内容,但我希望有人对此有一些经验,可以为我和其他任何有问题的人提供一些启示使用opensl实时播放录制的音频。

【问题讨论】:

  • 您好,您成功实施了吗?我也有同样的问题。
  • 关于这项工作的任何帖子或对社区的贡献??

标签: android android-ndk opensl


【解决方案1】:

我几乎完全找到了我正在寻找的东西。实时回放录制的音频的一种方法是使用称为循环缓冲区(或循环缓冲区)的东西,基本上录制的音频被读入其中,而回放的音频读取所有内容,直到录制的音频正在写入。

http://audioprograming.wordpress.com/

Android 中的 OpenSL ES 仍然不完全支持“低延迟”,因为仍然有一点延迟,但它仍然工作得很好,只是听起来更像是一个轻微的回声

【讨论】:

  • 由于它取决于低级别的实现,因此您的延迟会因设备型号而异 - 可能从某些设备可接受到其他设备完全不可接受。在同一设备上运行时,它也会略有不同。
  • 这是一个很好的观点。我将不得不进行测试,以确定哪些设备可以接受,哪些不可以,或者让用户自己决定是否使用它
  • 抱歉来晚了,请查看这个关于如何使用 openSLES 的视频,主要是优化延迟。希望对您有所帮助:youtube.com/watch?v=d3kfEeMZ65c
  • 链接现在转到私人网站
【解决方案2】:

我意识到这个问题已经得到解答,这是一个老问题。但是,澄清一下,在我的播放队列中,我不使用Clear()。我只是停止播放,然后在循环中使用GetState(state) 等待队列耗尽,如下所示:

SLresult res;
SLAndroidSimpleBufferQueueState state = {0};
res = (*playbackBufferQueue)->GetState(playbackBufferQueue, &state);

while (state.count && (res == SL_RESULT_SUCCESS))  
{  
    prevState = state;
    res = (*m_playbackBufferQueue)->GetState(m_playbackBufferQueue, &state); 
    // Otherwise this will peg the CPU
    sleep(1);
}

为了支持低延迟,您可以使用从 Android 查询的“最佳”速率和样本大小。您可以使用本机 API 调用来做到这一点吗?当然不是,别傻了。在 Java 中执行并将其传递回本机:

int getBestPlaybackRate() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        return Integer.parseInt(audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE));
    }
    return 0;
}
int getBestBufferSize() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        return Integer.parseInt(audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER));
    }
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-04
    • 2013-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-28
    • 2011-06-08
    • 2016-10-31
    相关资源
    最近更新 更多