AudioTrack lag:obtainBuffer超时

时间:2011-03-13 22:51:45

标签: android audio buffer delay lag

我正在我的Android手机上播放WAV,方法是加载文件并通过FileInputStream>将字节输入AudioTrack.write()。 BufferedInputStream> DataInputStream方法。音频播放效果很好,如果是,我可以轻松调整采样率,音量等,性能良好。然而,一首曲目开始播放大约需要两秒钟。我知道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

我注意到,每次播放曲目时,延迟的写入次数会增加一次 - 即使是在多个会话中 - 从手机开启时开始。块时间始终为230 - 240ms,考虑到此设备上的最小缓冲区大小为9600(9600/44100),这是有意义的。我在互联网上的无数次搜索中看到了这个消息,但它通常似乎与不播放音频或跳过音频有关。就我而言,这只是一个延迟的开始。

我正在高优先级线程中运行所有代码。这是我正在做的截断但功能正常的版本。这是我的播放类中的线程回调。再次,这是有效的(现在只播放16位,44.1kHz,立体声文件),它只需要永远启动并且每次都有获取缓冲/延迟写入消息。

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
            }                
        }   
    }
}

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

2 个答案:

答案 0 :(得分:2)

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

解决方案是拥有两个线程:一个从文件读取缓冲区并将它们排入内存的线程,另一个线程从该队列读取并写入音频硬件。我使用了ConcurrentLinkedQueue来实现这一目标。

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

答案 1 :(得分:1)

派对回答这个问题有点晚了,但万一它会帮助将来的任何人 - 我遇到了这个问题的代码非常类似于问题中的代码,其中AudioTrack被创建并设置为播放,但是不是立刻写的。

我发现在开始写入之前立即创建AudioTrack会导致延迟消失。出于某种原因,AudioTrack似乎不喜欢坐着空缓冲区。

就上述代码而言,您需要执行类似

的操作
mAudioTrack=null;
while (mRun) 
{ 

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

        try 
        { 
            if (mAudioTrack==null) {   
                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();   
            }
            // Rest of the playback code here
        }
    }
    mAudioTrack=null;
}