AudioTrack lag: истекло время ожидания буфера - PullRequest
10 голосов
/ 14 марта 2011

Я играю WAV на моем телефоне Android, загружая файл и передавая байты в AudioTrack.write () с помощью метода FileInputStream> 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-240 мс, что имеет смысл, учитывая минимальный размер буфера 9600 на этом устройстве (9600/44100).Я видел это сообщение в бесчисленных поисках в интернете, но обычно оно связано с тем, что оно вообще не воспроизводит звук или пропускает звук.В моем случае это просто отложенный запуск.

Я выполняю весь мой код в потоке с высоким приоритетом.Вот усеченная, но функциональная версия того, что я делаю.Это обратный вызов потока в моем классе воспроизведения.Опять же, это работает (сейчас воспроизводятся только 16-битные, 44,1 кГц, стереофайлы), для запуска требуется вечность, и каждый раз появляется сообщение о получении буфера / отложенной записи.

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 ]

2 голосов
/ 22 августа 2011

Я столкнулся с подобной проблемой, хотя для чтения данных PCM я использовал RandomAccessFile вместо BufferedInputStream. Проблема заключалась в том, что файловый ввод / вывод был слишком медленным. Я подозреваю, что у вас будет эта проблема даже с буферизованным потоком, потому что ввод / вывод все еще происходит в том же потоке, что и обработка аудио.

Решение состоит в том, чтобы иметь два потока: поток, который читает буферы из файла и помещает их в очередь в память, и другой поток, который читает из этой очереди и записывает в аудиооборудование. Для этого я использовал ConcurrentLinkedQueue.

Я использовал ту же технику для записи, используя AudioRecord, но в обратном направлении. Ключ заключается в том, чтобы разместить файл ввода / вывода в отдельном потоке.

1 голос
/ 06 июня 2012

Немного поздно, чтобы участник ответил на это, но в случае, если это поможет кому-либо в будущем - я столкнулся с этой точной проблемой с кодом, очень похожим на код в вопросе, где 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;
}
...