2013-03-07 15 views
22

Uso la classe MediaCodec fornita da Android SDK dal livello API 16 con il codificatore OMX.SEC.aac.enc per codificare l'audio in un file. Ottengo l'input audio dalla classe AudioRecord. La mia istanza della classe AudioRecord è configurato in questo modo:Codificatore AAC Android MediaCodec

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); 

Sono in grado di riprodurre i dati grezzi dall'istanza AudioRecord, quindi il problema non risiede lì.

Scrivo l'uscita dall'istanza AudioRecord a un'istanza ByteBuffer e la passiamo a un buffer di input disponibile dall'encoder. L'uscita dal codificatore viene scritta su un file sulla scheda SD.

Questi sono i parametri di configurazione per il mio MediaCodec esempio:

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 mi dice che non ci sono corsi d'acqua nel mio file AAC. Il comando FFMPEG -i @[email protected] mi dà il seguente errore: Dati non validi trovati durante l'elaborazione dell'ingresso. Nessuno dei mediaplayer che ho provato è in grado di riprodurre il mio file.

Perché non riesco a riprodurre il mio file? Non ricevo errori OpenMAX in LogCat e l'applicazione non si arresta in modo anomalo durante la codifica. Ho scritto un codificatore video che funziona sullo stesso principio e funziona.

Questo è il codice per leggere i dati da l'istanza AudioRecord in un buffer:

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(); 

la funzione Add dalle mie copie encoder il contenuto di un buffer all'altro:

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"); 
    } 
} 

Il il seguente codice viene utilizzato per occupare il buffer di ingresso dell'encoder:

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(); 

scrivo l'uscita dal codificatore in un file locale come questo:

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

pubblica il metodo completo in cui lo fai. con start() stop() .. grazie. –

+1

Inoltre, dai un'occhiata a questo thread - https://code.google.com/p/spydroid-ipcamera/issues/detail?id=43 sembra che abbiano un pezzo di codice funzionante per registrare AAC lì. –

+0

@Sergey Benner, Non usano la classe MediaCodec. La classe MediaRecorder è usata in Spydroid. Quella classe sarà la mia seconda scelta se la classe MediaCodec non funziona. –

risposta

2

Credo che vi siete persi la classe MediaMauxer. Ne hai bisogno se vuoi scrivere qualcosa ottenuto da MediaCodec in un file, ad esempio.

+0

OP chiedeva API 16, MediaMuxer è stato introdotto con API 18 – muetzenflo

Problemi correlati