2015-03-03 29 views
1

我使用MediaCodec API將視頻和音頻編碼爲mp4文件。數據編碼在單獨的線程中。有時在某些設備上,音頻編碼器會停止以返回任何可用的輸入緩衝區,因此MediaMuxer在嘗試停止時會崩潰。這裏是我的代碼:MediaCodec沒有任何可用的輸入緩衝區

配置媒體編解碼器:

public static final String MIME_TYPE_AUDIO = "audio/mp4a-latm"; 
public static final int SAMPLE_RATE = 44100; 
public static final int CHANNEL_COUNT = 1; 
public static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO; 
public static final int BIT_RATE_AUDIO = 128000; 
public static final int SAMPLES_PER_FRAME = 1024 * 2; 
public static final int FRAMES_PER_BUFFER = 24; 
public static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT; 
public static final int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC; 
public static final int MAX_INPUT_SIZE = 16384 * 4; 
public static final int MAX_SAMPLE_SIZE = 256 * 1024; 

private AudioRecord audioRecord; 
private ByteBuffer[] inputBuffers; 
private ByteBuffer inputBuffer; 
private MediaExtractor mediaExtractor; 

private boolean audioSended = false; 
private boolean completed = false; 
private int sampleCount; 
private int iBufferSize; 

public AudioEncoderCore(MovieMuxer muxer) throws IOException { 
    this.muxer = muxer; 
    bufferInfo = new MediaCodec.BufferInfo(); 

    MediaFormat mediaFormat = null; 

     mediaFormat = MediaFormat.createAudioFormat(MIME_TYPE_AUDIO, SAMPLE_RATE, CHANNEL_COUNT); 
     mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); 
     mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, MAX_INPUT_SIZE); 
     mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE_AUDIO); 

     encoder = MediaCodec.createEncoderByType(MIME_TYPE_AUDIO); 
     encoder.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
     encoder.start(); 
     iBufferSize = SAMPLES_PER_FRAME * FRAMES_PER_BUFFER; 

     // Ensure buffer is adequately sized for the AudioRecord 
     // object to initialize 
     int iMinBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT); 
     if (iBufferSize < iMinBufferSize) 
      iBufferSize = ((iMinBufferSize/SAMPLES_PER_FRAME) + 1) * SAMPLES_PER_FRAME * 2; 

     audioRecord = new AudioRecord(AUDIO_SOURCE, SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, iBufferSize); 
     audioRecord.startRecording(); 

} 

將數據發送到編碼器:

public void sendDataFromMic(boolean endOfStream) { 
    if (endOfStream) 
     Log.d(TAG, "sendDataFromMic end of stream"); 

    long audioPresentationTimeNs; 

    byte[] mTempBuffer = new byte[SAMPLES_PER_FRAME]; 
    audioPresentationTimeNs = System.nanoTime(); 

    int iReadResult = audioRecord.read(mTempBuffer, 0, mTempBuffer.length); 

    if (iReadResult == AudioRecord.ERROR_BAD_VALUE || iReadResult == AudioRecord.ERROR_INVALID_OPERATION || iReadResult == 0) { 
     Log.e(TAG, "audio buffer read error"); 
    } else { 
     // send current frame data to encoder 
     try { 
      if (inputBuffers == null) 
       inputBuffers = encoder.getInputBuffers(); 

      //Sometimes can't get any available input buffer 
      int inputBufferIndex = encoder.dequeueInputBuffer(100000); 
      Log.d(TAG, "inputBufferIndex = " + inputBufferIndex); 
      if (inputBufferIndex >= 0) { 
       inputBuffer = inputBuffers[inputBufferIndex]; 
       inputBuffer.clear(); 
       inputBuffer.put(mTempBuffer, 0, iReadResult); 

       Log.d(TAG, "sending frame to audio encoder " + iReadResult + " bytes"); 
       encoder.queueInputBuffer(inputBufferIndex, 0, iReadResult, audioPresentationTimeNs/1000, 
         endOfStream ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 
      } 


     } catch (Throwable t) { 
      Log.e(TAG, "sendFrameToAudioEncoder exception"); 
      t.printStackTrace(); 
     } 
    } 
} 

漏編碼器:

public void drainEncoder() { 
    ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers(); 
    while (true) { 
     int encoderStatus = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_NSECS); 
     if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 
      Log.d(TAG, "no output available, spinning to await EOS"); 
      break; 
     } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) 
      encoderOutputBuffers = encoder.getOutputBuffers(); 
     else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
      MediaFormat newFormat = encoder.getOutputFormat(); 
      Log.d(TAG, "encoder format changed: " + newFormat); 
      trackIndex = muxer.addTrack(newFormat); 
     } else if (muxer.isMuxerStarted()) { 
      ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; 
      if (encodedData == null) 
       throw new RuntimeException("encoded buffer " + encoderStatus + " was null"); 

      if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { 
       Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG"); 
       bufferInfo.size = 0; 
      } 

      if (bufferInfo.size != 0) { 
       encodedData.position(bufferInfo.offset); 
       encodedData.limit(bufferInfo.offset + bufferInfo.size); 

       muxer.writeSampleData(trackIndex, encodedData, bufferInfo); 

       Log.d(TAG, "sent " + bufferInfo.size + " bytes to muxer, ts=" + 
         bufferInfo.presentationTimeUs + " track index=" + trackIndex); 
      } 

      encoder.releaseOutputBuffer(encoderStatus, false); 

      if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 
       Log.d(TAG, "end of stream reached"); 
       completed = true; 
       break;  // out of while 
      } 
     } 
    } 
} 

bug是穩定的再現上的HTC One, Galaxy S3,但在華爲榮耀3C上都可以正常工作。

回答

1

經過源代碼調查後,我找到了解決方案。當我將輸出緩衝區出隊時,複用器可能還沒有啓動,在這種情況下,緩衝區沒有被釋放。所以這裏是漏極編碼器的工作代碼:

public void drainEncoder() { 
    ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers(); 
    while (true) { 
     int encoderStatus = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_NSECS); 
     if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { 
      Log.d(TAG, "no output available, spinning to await EOS"); 
      break; 
     } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) 
      encoderOutputBuffers = encoder.getOutputBuffers(); 
     else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
      MediaFormat newFormat = encoder.getOutputFormat(); 
      Log.d(TAG, "encoder format changed: " + newFormat); 
      trackIndex = muxer.addTrack(newFormat); 
     } else if (muxer.isMuxerStarted()) { 
      ByteBuffer encodedData = encoderOutputBuffers[encoderStatus]; 
      if (encodedData == null) 
       throw new RuntimeException("encoded buffer " + encoderStatus + " was null"); 

      if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { 
       Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG"); 
       bufferInfo.size = 0; 
      } 

      if (bufferInfo.size != 0) { 
       encodedData.position(bufferInfo.offset); 
       encodedData.limit(bufferInfo.offset + bufferInfo.size); 

       muxer.writeSampleData(trackIndex, encodedData, bufferInfo); 
       Log.d(TAG, "sent " + bufferInfo.size + " bytes to muxer, ts=" + 
         bufferInfo.presentationTimeUs + " track index=" + trackIndex); 
      } 

      encoder.releaseOutputBuffer(encoderStatus, false); 

      if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 
       Log.d(TAG, "end of stream reached"); 
       completed = true; 
       break;  // out of while 
      } 
     } 
     else{ 
      //Muxer not ready, release buffer 
      encoder.releaseOutputBuffer(encoderStatus, false); 
      Log.d(TAG, "muxer not ready, skip data"); 
     } 
    } 
} 
+0

我的問題與您的答案完全無關,案件。謝謝! – Devsil 2016-01-28 19:01:27