AudioTrack lag: getBuffer отключен
Я играю 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 кГц, стереофайлы), это просто начинается навсегда, и каждый раз получает сообщение getBuffer/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
}
}
}
}
Если я смогу пройти искусственно длинное отставание, я могу легко справиться с задержкой наследования, начав писать в более поздней, предсказуемой позиции (т.е. пропустить минимальную длину буфера при начале воспроизведения файла).
Ответы
Ответ 1
У меня возникла аналогичная проблема, хотя я использовал RandomAccessFile вместо BufferedInputStream для чтения данных PCM. Проблема заключалась в том, что ввод/вывод файлов был слишком медленным. Я подозреваю, что у вас будет эта проблема даже с буферизованным потоком, потому что ввод-вывод все еще происходит в том же потоке, что и обработка звука.
Решение состоит в том, чтобы иметь два потока: поток, который считывает буферы из файла и помещает их в память, и другой поток, который читает из этой очереди и записывает на аудиооборудование. Для этого я использовал ConcurrentLinkedQueue.
Я использовал тот же метод для записи, используя AudioRecord, но в обратном направлении. Ключ состоит в том, чтобы поместить файл ввода/вывода в отдельный поток.
Ответ 2
Немного поздно, чтобы сторона ответила на это, но в случае, если это поможет кому угодно в будущем - я столкнулся с этой точной проблемой с кодом, очень похожим на код в вопросе, где 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;
}