2015年3月31日 星期二

[Android] 使用AudioTrack()會何聲音會播播停停?

使用AudioTrack()時,如果聲音有點過尖或過低
那就可能是格式設定有錯,或者sample長度給的不對,

但如果是播播停停,尤其是遇到類似以下的錯誤訊息
W/AudioTrack(..): releaseBuffer() track 0x6eaf04d0 name=0x1 disabled, restarting
該怎麼處理呢?

會遇到上面的訊息,表示資料給的太慢,
如果是local的檔案,通常不會有問題,
但如果是網路streaming,因為資料來的速率不一,
有時可能會有buffer underflow的問題,
有二個作法,一個是在前端網路接收端加buffer,
一個是在後端要進AudioTrack時加buffer,
底下示範的方法就是第二個方法,

private static void queueAudioData(byte[] buf, int size) {  
 if((audioPcmBufferDataCount + size)> audioBufferSize)
 {
  return;  
 }
 if((audioPcmBufferFront + size) > audioBufferSize)
 {
  //rewind
  System.arraycopy(buf, 0, audioPCMData, audioPcmBufferFront, audioBufferSize - audioPcmBufferFront);
  audioPcmBufferDataCount += (audioBufferSize - audioPcmBufferFront);
  size -= (audioBufferSize - audioPcmBufferFront);
  audioPcmBufferFront = 0;      
 }
 System.arraycopy(buf, 0, audioPCMData, audioPcmBufferFront, size);
 audioPcmBufferFront += size;
 audioPcmBufferDataCount += size;
} 

audio decoder解出來的PCM data,
先透過queueAudioData()存到一個queue buffer,
之後再穩定的將資料write到AudioTrack
private class playAudio extends Thread {
 @Override
 public void run() {  
  super.run();
  int len = 512;
  while(!isInterrupted()) {
   try {
    if(audioPcmBufferDataCount < len)
    {
     Thread.sleep(10);
     continue;
    }

    if((audioPcmBufferEnd + len) > audioBufferSize)
     writeSize = audioBufferSize - audioPcmBufferEnd;
    else
     writeSize = len;
    playAudioTrack.write(audioPCMData, audioPcmBufferEnd, writeSize);
    Thread.sleep(1);
    audioPcmBufferDataCount -= writeSize;
    audioPcmBufferEnd += writeSize;
    if(audioPcmBufferEnd >= audioBufferSize)
     audioPcmBufferEnd=0;
    
    if(playAudioTrack.getPlayState()!=AudioTrack.PLAYSTATE_PLAYING) {     
          playAudioTrack.play();
       }
   } catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
   }       
  }
 }
}

沒有留言:

張貼留言