2013-03-07 29 views
22

Używam klasy MediaCodec dostarczonej przez zestaw Android SDK od poziomu interfejsu API 16 z koderem OMX.SEC.aac.enc do kodowania dźwięku do pliku. Otrzymuję dane wejściowe audio z klasy AudioRecord. Moje wystąpienie klasy AudioRecord jest skonfigurowany tak:Koder Android MediaCodec AAC

bufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT); 
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_DEFAULT, bufferSize); 

jestem w stanie grać surowych danych z instancji AudioRecord, więc problem nie istnieje zamieszkania.

Zapisuję dane wyjściowe z instancji AudioRecord do instancji ByteBuffer i przekazuję je do dostępnego bufora wejściowego z kodera. Dane wyjściowe z kodera są zapisywane w pliku na karcie SD.

Są to parametry konfiguracyjne dla mojego przykład MediaCodec:

codec = MediaCodec.createEncoderByType("audio/mp4a-latm"); 
MediaFormat format = new MediaFormat(); 
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm"); 
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024); 
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2); 
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100); 
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE); 
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 

VLC mówi mi, że nie istnieją żadne strumienie w moim pliku AAC. Komenda FFMPEG -i @[email protected] podaje następujący błąd: Znaleziono niepoprawne dane podczas przetwarzania wejścia. Żaden z testowanych mediaplayerów nie jest w stanie odtworzyć mojego pliku.

Dlaczego nie mogę odtworzyć mojego pliku? Nie otrzymuję żadnych błędów w OpenMAX i aplikacja nie ulega awarii podczas kodowania. Napisałem koder wideo, który działa na tej samej zasadzie i działa.

Jest to kod do odczytu danych z instancji AudioRecord do bufora:

new Thread() { 
     public void run() { 
      ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bufferSize); 
      int read = 0; 
      while (isRecording) { 
       read = recorder.read(byteBuffer, bufferSize); 
       if(AudioRecord.ERROR_INVALID_OPERATION != read){ 
        encoder.add(byteBuffer); 
       } 
      } 
      recorder.stop(); 
     } 
    }.start(); 

Funkcja dodać z moich kopii enkodera zawartość jednego bufora do innego:

public void add(ByteBuffer input) { 
    if (!isRunning) 
     return; 

    if (tmpInputBuffer == null) 
     tmpInputBuffer = ByteBuffer.allocate(input.capacity()); 

    if (!tmpBufferClear) 
     Log.e("audio encoder", "deadline missed"); //TODO lower bit rate 

    synchronized (tmpInputBuffer) { 
     tmpInputBuffer.clear(); 
     tmpInputBuffer.put(input); 
     tmpInputBuffer.notifyAll(); 
     Log.d("audio encoder", "pushed data into tmpInputBuffer"); 
    } 
} 

The następujący kod służy do zajmowania bufora wejściowego kodera:

new Thread() { 
    public void run() { 
     while (isRunning) { 
      if (tmpInputBuffer == null) 
       continue; 
      synchronized (tmpInputBuffer) { 
       if (tmpBufferClear) { 
        try { 
         Log.d("audio encoder", "falling asleep"); 
         tmpInputBuffer.wait(); //wait when no input is available 
        } 
        catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
       } 

       ByteBuffer[] inputBuffers = codec.getInputBuffers(); 
       int inputBufferIndex; 
       do 
        inputBufferIndex = codec.dequeueInputBuffer(-1); 
       while (inputBufferIndex < 0); 
       ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 
       inputBuffer.clear(); 
       Log.d("input buffer size", String.valueOf(inputBuffer.capacity())); 
       Log.d("tmp input buffer size", String.valueOf(tmpInputBuffer.capacity())); 
       inputBuffer.put(tmpInputBuffer.array()); 
       tmpInputBuffer.clear(); 
       codec.queueInputBuffer(inputBufferIndex, 0, tmpInputBuffer.capacity(), 0, 0); 
       tmpBufferClear = true; 
       Log.d("audio encoder", "added to input buffer"); 
      } 
     } 
    } 
}.start(); 

Piszę wyjście z kodera w pliku lokalnym tak:

new Thread() { 
     public void run() { 
      while (isRunning) { 
       ByteBuffer[] outputBuffers = codec.getOutputBuffers(); 
       MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); 
       int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, -1); 
       while (outputBufferIndex >= 0) { 
        ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; 
        byte[] outData = new byte[bufferInfo.size]; 
        outputBuffer.get(outData); 

        try { 
         fileWriter.write(outData, 0, outData.length); 
        } 
        catch (IOException e) { 
         e.printStackTrace(); 
        } 
        codec.releaseOutputBuffer(outputBufferIndex, false); 
        outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 0); 
        Log.d("audio encoder", "removed from output buffer"); 
       } 
      } 
      codec.stop(); 

      try { 
       fileWriter.close(); 
      } 
      catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    }.start(); 
       tmpBufferClear = true; 
       Log.d("audio encoder", "added to input buffer"); 
      } 
     } 
    } 
}.start(); 
+0

umieść pełną metodę, gdy to zrobisz. z funkcją start() stop() .. dziękuję. –

+1

Zobacz także ten wątek - https://code.google.com/p/spydroid-ipcamera/issues/detail?id=43 Wydaje się, że mają działający fragment kodu do nagrywania AAC. –

+0

@Sergey Benner, Nie używają klasy MediaCodec. Klasa MediaRecorder jest używana w Spydroid. Ta klasa będzie moim drugim wyborem, jeśli klasa MediaCodec nie zadziała. –

Odpowiedz

2

Chyba przegapiłeś klasę MediaMauxer. Potrzebujesz go, jeśli chcesz na przykład napisać coś, co dostałeś z MediaCodec do pliku.

+0

OP prosi o API 16, MediaMuxer został wprowadzony z API 18 – muetzenflo