2011-03-13 8 views
10

Gram w WAV na moim telefonie z Androidem, ładując plik i podając bajty do ścieżki AudioTrack.write() za pomocą metody FileInputStream> BufferedInputStream> DataInputStream. Dźwięk odtwarza się dobrze, a gdy jest, mogę łatwo dostosować częstotliwość próbkowania, głośność itp. W locie z dobrą wydajnością. Jednak odtworzenie ścieżki trwa około dwóch pełnych sekund. Wiem, że AudioTrack ma nieuniknione opóźnienie, ale to niedorzeczne. Za każdym razem, gdy gram utwór, otrzymuję to:Opóźnienie audioTrack: uzyskanie przekroczenia limitu czasu przez Buffera

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 

Zauważyłem, że Opóźniony zapis count wzrasta o jeden za każdym razem, gdy gram utwór - nawet w wielu sesjach - od czasu, gdy telefon ma został włączony. Czas blokowania wynosi zawsze 230 - 240 ms, co ma sens biorąc pod uwagę minimalny rozmiar bufora 9600 na tym urządzeniu (9600/44100). Widziałem tę wiadomość w niezliczonych wyszukiwaniach w Internecie, ale zazwyczaj wydaje się, że jest ona związana z brakiem odtwarzania dźwięku lub z pominięciem dźwięku. W moim przypadku jest to tylko opóźniony start.

Uruchomiłem cały mój kod w wątku o wysokim priorytecie. Oto skrócona, ale działająca wersja tego, co robię. To jest wywołanie zwrotne wątku w mojej klasie odtwarzania. Znowu to działa (odtwarzane są teraz tylko 16-bitowe, 44,1 kHz, pliki stereo), po prostu trwa to zawsze, a za każdym razem otrzymujemy komunikat o błędzie/opóźnieniu zapisu.

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

Jeśli mogę ominąć sztucznie długim opóźnieniem, mogę łatwo uporać się z opóźnieniami dziedziczyć zaczynając mój zapis w późniejszym, przewidywalnym miejscu (tj pominąć minimalnej długości bufora kiedy rozpocząć odtwarzanie pliku).

+1

mam podobny problem w tej chwili. Czy znalazłeś jakieś rozwiązanie? – sunside

+1

jeszcze niestety. :/ – BTR

Odpowiedz

2

Wystąpił podobny problem, chociaż używałem RandomAccessFile, zamiast BufferedInputStream, aby odczytać dane PCM. Problem polegał na tym, że plik I/O był zbyt wolny. Podejrzewam, że będziesz miał ten problem nawet ze zbuforowanym strumieniem, ponieważ operacje we/wy są nadal wykonywane w tym samym wątku, co przetwarzanie dźwięku.

Rozwiązaniem jest posiadanie dwóch wątków: wątku, który odczytuje bufory z pliku i umieszcza je w pamięci, oraz inny wątek odczytujący z tej kolejki i zapisujący na sprzęcie audio. Użyłem ConcurrentLinkedQueue, aby to osiągnąć.

Użyłem tej samej techniki nagrywania, używając AudioRecord, ale w odwrotnym kierunku. Kluczem jest umieszczenie pliku I/O w osobnym wątku.

+0

Dzięki! Ten projekt został wstrzymany, czekając na rozwiązanie. Spróbuję tego. – BTR

1

Nieco spóźniony na przyjęcie, ale na wypadek, gdyby to pomogło komuś w przyszłości - wpadłem na ten problem z kodem podobnym do kodu w pytaniu, gdzie AudioTrack jest tworzony i ustawiony do odtwarzania, ale nie zapisano go natychmiast.

Zauważyłem, że utworzenie ścieżki dźwiękowej bezpośrednio przed rozpoczęciem pisania spowodowało, że opóźnienie zniknęło. Z jakiegoś powodu AudioTrack nie lubi siedzieć z pustym buforem.

chodzi o powyższym kodzie, że chcesz coś zrobić jak

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; 
} 
Powiązane problemy