android - AudioTrack 延迟 : obtainBuffer timed out

标签 android audio buffer delay lag

我通过 FileInputStream > BufferedInputStream > DataInputStream 方法加载文件并将字节送入 AudioTrack.write() 来在我的 Android 手机上播放 WAV。音频播放良好,当它播放时,我可以轻松地动态调整采样率、音量等,性能也不错。但是,轨道开始播放大约需要两秒。我知道 AudioTrack 有不可避免的延迟,但这是荒谬的。每次我播放轨道时,我都会得到这个:

03-13 14:55:57.100: WARN/AudioTrack(3454): obtainBuffer timed out (is the CPU pegged?) 0x2e9348 user=00000960,     server=00000000
03-13 14:55:57.340: WARN/AudioFlinger(72): write blocked for 233 msecs, 9 delayed writes, thread 0xba28

我注意到,每次我播放一首轨道时,延迟写入计数都会增加一个——即使是在多个 session 中——从手机打开时起。阻塞时间始终为 230 - 240 毫秒,考虑到此设备上的最小缓冲区大小为 9600 (9600/44100),这是有意义的。我在 Internet 上无数次搜索中看到过此消息,但它通常似乎与根本不播放音频或跳过音频有关。就我而言,这只是一个延迟的开始。

我在高优先级线程中运行所有代码。这是我正在做的事情的截断但功能的版本。这是我的播放类中的线程回调。同样,这有效(现在只播放 16 位、44.1kHz 的立体声文件),它只需要很长时间才能开始,并且每次都有 obtainBuffer/delayed write 消息。

public void run() {

    // Load file
    FileInputStream mFileInputStream;
    try {
        // mFile is instance of custom file class -- this is correct, 
        // so don't sweat this line
        mFileInputStream = new FileInputStream(mFile.path());
    } catch (FileNotFoundException e) {
        // log
    }

    BufferedInputStream mBufferedInputStream = new BufferedInputStream(mFileInputStream, mBufferLength);
    DataInputStream mDataInputStream = new DataInputStream(mBufferedInputStream);

    // Skip header
    try {
        if (mDataInputStream.available() > 44) {
            mDataInputStream.skipBytes(44);
        }
    } catch (IOException e) {
        // log
    }

    // Initialize device
    mAudioTrack = new AudioTrack(
        AudioManager.STREAM_MUSIC, 
        ConfigManager.SAMPLE_RATE, 
        AudioFormat.CHANNEL_CONFIGURATION_STEREO, 
        AudioFormat.ENCODING_PCM_16BIT, 
        ConfigManager.AUDIO_BUFFER_LENGTH,
        AudioTrack.MODE_STREAM
    );
    mAudioTrack.play();

    // Initialize buffer
    byte[] mByteArray = new byte[mBufferLength];
    int mBytesToWrite = 0;
    int mBytesWritten = 0;

    // Loop to keep thread running
    while (mRun) {

        // This flag is turned on when the user presses "play"
        while (mPlaying) {

            try {
                // Check if data is available
                if (mDataInputStream.available() > 0) {

                    // Read data from file and write to audio device
                    mBytesToWrite = mDataInputStream.read(mByteArray, 0, mBufferLength);
                    mBytesWritten += mAudioTrack.write(mByteArray, 0, mBytesToWrite);

                }
            }
            catch (IOException e){
                // log
            }                
        }   
    }
}

如果我可以克服人为的长延迟,我可以通过在较晚的可预测位置开始写入(即,当我开始播放文件时跳过最小缓冲区长度)来轻松处理继承延迟。

最佳答案

我遇到了类似的问题,尽管我使用的是 RandomAccessFile 而不是 BufferedInputStream 来读取 PCM 数据。问题是文件 I/O 太慢了。我怀疑即使使用缓冲流也会遇到此问题,因为 I/O 仍在与音频处理相同的线程上进行。

解决方案是有两个线程:一个线程从文件中读取缓冲区并将它们排队到内存中,另一个线程从该队列中读取并写入音频硬件。我使用了 ConcurrentLinkedQueue 来完成此任务。

我使用相同的录音技术,使用 AudioRecord,但方向相反。关键是将文件 I/O 放在单独的线程上。

关于android - AudioTrack 延迟 : obtainBuffer timed out,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5293025/

相关文章:

android - 谷歌地图驾驶导航有官方API吗?

android - Firebase Android 查询和 Java 方法

c++ - std::bad_alloc:确定 Eigen 中的缓冲区大小

javascript - 当输入流通过管道传输到多个输出流时,缓冲区级别会发生什么?

android - 如何更改 Spinner 运行时的文本大小

android - 何时删除 SharedPreferences?

shell - FFMPEG:向以前的 amix 输出添加更多轨道

python - 使用pygame从缓冲区播放声音

javascript - AudioJS - 停止使用 javascript?

logging - Golang : Does logging into file using log. Println 负责并发访问