【问题标题】:Android OpenSL Audio Buffer underrun with native sample rate/buffer size (only some devices)Android OpenSL Audio Buffer underrun with native sample rate/buffer size(仅限某些设备)
【发布时间】:2016-10-18 19:29:07
【问题描述】:

即使在设置了设备的首选采样率和缓冲区之后,我似乎也遇到了噼啪声/故障音频的问题(基本上是当缓冲区没有被填满/回调没有及时完成/缓冲区欠载的情况下)使用 OpenSL ES 时的大小。

我已经在 Nexus 7(2013 型号)和 Moto X Pure Edition 上进行了测试,它们都可以正常工作(分别使用 48 kHz 的采样率和 240 和 960 的缓冲区大小)。也许每分钟左右都有一个小故障,但我已经接受它的生活太少了。但是,当我使用三星 Galaxy S7 时,我每秒都会收到大量的音频故障(这也是 48 kHz 和每个缓冲区 960 帧)。我已经检查过了,音频回调总是在正确的时间(20 毫秒)内完成;因此我可以说没有缓冲区不足。

奇怪的问题是,如果我将三星设置为使用 44.1 kHz 的采样率和 128 的缓冲区大小,则不会出现音频故障(除了我猜想处理不正确的原生采样率的小中断) .

为什么会发生这种情况,我该如何解决它以使本机音频属性正常工作?

谢谢!

编辑:也许我混淆了术语(缓冲区不足和溢出),所以如果有人想进来纠正我,请继续!!!学习就是力量

【问题讨论】:

  • 您是否使用回调来排队新缓冲区?
  • @WLGfx 我很确定我正在这样做(可能是错的,我承认我对一切工作原理的理解需要改进)。我一直在使用这个类来设置回调和音频流:github.com/superpoweredSDK/audioIO/blob/master/…
  • 在缓冲区完成后不会调用回调,只是在缓冲区即将完成之前,让您有机会排队另一个。您将不得不手动添加另一个队列,然后让回调再次启动。尽管在排队缓冲区时调用了回调,但您可能没有足够的时间来准备另一个缓冲区。在我的项目中,每次屏幕更新我都会检查 sles 是否正在播放,如果没有,我会手动将另一个缓冲区添加到队列中。
  • @WLGfx 我将如何检查每个屏幕更新?谢谢!

标签: android c++ audio android-ndk opensl


【解决方案1】:

我不确定你如何能够根据自己的目的调整它,但我是这样做的:

SLES 回调函数:

void AudioManager::sles_callback(SLAndroidSimpleBufferQueueItf itf, void *ptr) {
    AudioManager *manager = (AudioManager*)ptr;
    manager->audio_queue_playing = false;
    manager->check_audio_playback(); // the actual function to queue buffers
}

检查另一个音频缓冲区的可用性:

void AudioManager::check_audio_playback() {
    // mutex used to only allow one thread access at a time
    pthread_mutex_lock(&audio_callback_mutex); // block access to renderer or callback

    if (audio_queue_playing || playback_pos == decode_pos) {
        pthread_mutex_unlock(&audio_callback_mutex);
        return;
    }

    double audio_pts = pts_start_time;
    double render_time = Display.get_current_render_time();

    AudioBuffer *buffer = nullptr;

    if (last_played_buffer) {
        last_played_buffer->used = false;
        buffer = &buffers[playback_pos];
        //(*buffer_interface)->Enqueue(buffer_interface, buffer->data, (SLuint32)(buffer->nsamples << 2));
        //audio_queue_playing = true;
        last_played_buffer = buffer;
        playback_pos = (++playback_pos) % MAX_AUD_BUFFERS;
        //return;
    }

    if (audio_pts < 0.0) {
        if (ffmpeg.vid_stream.stream_id == -1) {    // start playback
            pts_start_time = buffers[playback_pos].frame_time;
            audio_pts = pts_start_time;
            sys_start_time = render_time;
        }
    }

    AudioBuffer *temp = nullptr;

    double current_time = render_time - sys_start_time;

    bool bounds = false;
    double temp_time;

    while (!bounds) {
        if (playback_pos == decode_pos) bounds = true;
        else {
            temp = &buffers[playback_pos];
            temp_time = temp->frame_time - audio_pts;

            if (temp_time < current_time) {
                if (buffer) buffer->used = false;
                buffer = temp;
                playback_pos = (++playback_pos) % MAX_AUD_BUFFERS;
            } else {
                bounds = true;
            }
        }
    }

    if (buffer) {
        audio_queue_playing = true;
        (*buffer_interface)->Enqueue(buffer_interface, buffer->data, (SLuint32)(buffer->nsamples << 2));
        last_played_buffer = buffer;
    } else {
        last_played_buffer = nullptr;
    }

    pthread_mutex_unlock(&audio_callback_mutex);
}

然后在每帧更新时(即在我的屏幕渲染器中每秒 60 次)我调用 check_audio_playback()。如果缓冲区正在播放,则函数退出。

手指交叉这将帮助你。

【讨论】:

  • 谢谢!等我弄明白了,我会向你汇报的!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-27
  • 1970-01-01
  • 1970-01-01
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多