2013-03-07 6 views
22

API 레벨 16부터 OMX.SEC.aac.enc 인코더를 사용하여 Android SDK에서 제공하는 MediaCodec 클래스를 사용하여 오디오를 파일로 인코딩합니다. 오디오 입력을 AudioRecord 클래스에서 가져옵니다.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); 

내가 AudioRecord 인스턴스에서 원시 데이터를 재생할 수 있어요, 그래서 문제가 존재하지 않습니다 다음 AudioRecord 클래스의 내 인스턴스는 다음과 같이 구성되어 있습니다.

AudioRecord 인스턴스의 결과를 ByteBuffer 인스턴스에 쓰고이를 인코더에서 사용 가능한 입력 버퍼로 전달합니다. 인코더의 출력은 SD 카드의 파일에 기록됩니다.

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 내 AAC 파일에는 스트림이 없음을 알려줍니다 :

이 내 MediaCodec 인스턴스에 대한 구성 매개 변수입니다. 명령 FFMPEG -i @[email protected]이 다음 오류를 표시합니다. 입력을 처리 할 때 유효하지 않은 데이터가 발견되었습니다.. 테스트 한 미디어 플레이어 중 누구도 내 파일을 재생할 수 없습니다.

파일을 재생할 수없는 이유는 무엇입니까? LogCatOpenMAX 오류가 표시되지 않으며 인코딩 할 때 응용 프로그램이 중단되지 않습니다. 나는 같은 원리로 작동하는 비디오 인코더를 썼다.

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

함수는 다른 내 인코더 복사본으로부터 하나의 버퍼의 콘텐츠를 추가 :

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

버퍼에 AudioRecord 인스턴스로부터의 데이터를 판독하는 코드 다음 코드는 인코더의 입력 버퍼를 점유하는 데 사용됩니다.

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

나는이 같은 로컬 파일로 인코더의 출력을 쓰기 :

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

전체 방법을 게시하십시오. start()와 함께 stop() .. 감사합니다. –

+1

또한이 스레드 (https://code.google.com/p/spydroid-ipcamera/issues/detail?id=43)에서 AAC를 기록 할 수있는 코드가있는 것 같습니다. –

+0

@Sergey Benner, 그들은 MediaCodec 클래스를 사용하지 않습니다. MediaRecorder 클래스는 Spydroid에서 사용됩니다. MediaCodec 클래스가 작동하지 않으면 해당 클래스가 두 번째 선택 항목이됩니다. –

답변

2

난 당신이 MediaMauxer 클래스를 놓친 것 같아요. MediaCodec에서 가져온 파일을 파일에 쓰고 싶다면이 파일이 필요합니다.

+0

OP가 API 16을 요구하고 MediaMuxer가 API 18을 도입했습니다. – muetzenflo