AudioTrack 滞后:获取缓冲区超时
我在 Android 手机上播放 WAV,方法是加载文件并通过 FileInputStream > 将字节输入 AudioTrack.write() 中。缓冲输入流>数据输入流方法。音频播放得很好,当它播放时,我可以轻松地动态调整采样率、音量等,并具有良好的性能。然而,一首曲目需要大约两秒的时间才能开始播放。我知道 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.1kHz、立体声文件),只是需要很长时间才能启动,并且每次都会收到 acquireBuffer/延迟写入消息。
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
}
}
}
}
如果我能够克服人为的长延迟,我可以通过在稍后的可预测位置开始写入(即,当我开始播放文件时跳过最小缓冲区长度)来轻松处理继承延迟。
I'm playing WAVs on my Android phone by loading the file and feeding the bytes into AudioTrack.write() via the FileInputStream > BufferedInputStream > DataInputStream method. The audio plays fine and when it is, I can easily adjust sample rate, volume, etc on the fly with nice performance. However, it's taking about two full seconds for a track to start playing. I know AudioTrack has an inescapable delay, but this is ridiculous. Every time I play a track, I get this:
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
I've noticed that the delayed write count increases by one every time I play a track -- even across multiple sessions -- from the time the phone has been turned on. The block time is always 230 - 240ms, which makes sense considering a minimum buffer size of 9600 on this device (9600 / 44100). I've seen this message in countless searches on the Internet, but it usually seems to be related to not playing audio at all or skipping audio. In my case, it's just a delayed start.
I'm running all my code in a high priority thread. Here's a truncated-yet-functional version of what I'm doing. This is the thread callback in my playback class. Again, this works (only playing 16-bit, 44.1kHz, stereo files right now), it just takes forever to start and has that obtainBuffer/delayed write message every time.
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
}
}
}
}
If I can get past the artificially long lag, I can easily deal with the inherit latency by starting my write at a later, predictable position (ie, skip past the minimum buffer length when I start playing a file).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我遇到了类似的问题,尽管我使用 RandomAccessFile 而不是 BufferedInputStream 来读取 PCM 数据。问题是文件 I/O 太慢。我怀疑即使使用缓冲流也会遇到这个问题,因为 I/O 仍然在与音频处理相同的线程上进行。
解决方案是有两个线程:一个线程从文件中读取缓冲区并将它们排队到内存中,另一个线程从此队列中读取数据并将其写入音频硬件。我使用 ConcurrentLinkedQueue 来完成此任务。
我使用相同的技术进行录音,使用 AudioRecord,但方向相反。关键是将文件 I/O 放在单独的线程上。
I ran into a similar problem, although I was using a RandomAccessFile, instead of a BufferedInputStream, to read the PCM data. The issue was that the file I/O was too slow. I suspect you will have this problem even with a buffered stream, because the I/O is still taking place on the same thread as audio processing.
The solution is to have two threads: A thread that reads buffers from a file and queues them into memory, and another thread that reads from this queue and writes to the audio hardware. I used a ConcurrentLinkedQueue to accomplish this.
I used the same technique for recording, using AudioRecord, but in the reverse direction. The key is to place the file I/O on a separate thread.
派对回答这个问题有点晚了,但以防万一它对将来的任何人有帮助 - 我遇到了这个确切的问题,代码与问题中的代码非常相似,其中创建了 AudioTrack 并设置为播放,但未写入立即地。
我发现在开始写入之前立即创建 AudioTrack 可以消除延迟。由于某种原因,AudioTrack 似乎不喜欢坐在空缓冲区中。
就上面的代码而言,您想做类似的事情
A bit late to the party answering this, but in case it helps anyone in the future - I ran into this exact problem with code pretty similar to the code in the question, where the AudioTrack is created and set to play, but not written to immediately.
I found that creating the AudioTrack immediately before you start writing to it made the delay go away. For some reason AudioTrack doesn't seem to like sitting around with an empty buffer.
In terms of the code above, you'd want to do something like