2012-02-21 63 views
2

有什麼方法可以使用標準的Android SDK將PCM wav文件重新編碼爲另一種編碼?將Wav文件重新編碼爲AAC LC,AMR WB/NB

我可以看到,可以直接從麥克風錄製成這些格式,但我正在寫的應用程序必須先在PCM中錄製。 由於許可限制,ffmpeg不是可用選項。


我現在對Jelly Bean有以下代碼,但輸出不可被任何媒體播放器讀取。

從AOSP怯場代碼似乎暗示一個MPEG4容器

profile.nSampleRate = sampleRate; 
profile.nBitRate = bitRate; 
profile.nAudioBandWidth = 0; 
profile.nFrameLength = 0; 
profile.nAACtools = OMX_AUDIO_AACToolAll; 
profile.nAACERtools = OMX_AUDIO_AACERNone; 
profile.eAACProfile = (OMX_AUDIO_AACPROFILETYPE) aacProfile; 
profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; 

但是從機器人代碼輸出不可讀。

根據需要,輸入wav文件是32khz,16位帶符號單聲道。

public void doConvert(View v) 
{ 
    new AsyncTask<Void, Void, Void>() 
    { 

     @Override 
     protected Void doInBackground(Void... params) 
     { 
      try 
      { 
       int codecCount = MediaCodecList.getCodecCount(); 

       for (int i=0; i < codecCount; i++) 
       { 
        MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); 
        Logger.getLogger(MainActivity.class.getSimpleName()).log(Level.INFO, info.getName()); 
        for (String type : info.getSupportedTypes()) 
        { 
         Logger.getLogger(MainActivity.class.getSimpleName()).log(Level.INFO, type); 
        } 

       } 

       File inputFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Media/Report-test5.wav"); 
       FileInputStream fis = new FileInputStream(inputFile); 
       fis.skip(44);//remove wav header 

       File outputFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Media/out.mp4"); 
       if (outputFile.exists()) outputFile.delete(); 

       FileOutputStream fos = new FileOutputStream(outputFile); 

       MediaCodec codec = MediaCodec.createEncoderByType("audio/mp4a-latm"); 

       MediaFormat outputFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", 32000, 1); 
       outputFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); 
       //outputFormat.setInteger(MediaFormat.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_OUT_MONO); 
       outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, 48000); 
       //outputFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 64000); 
       double durationInMs = (inputFile.length()/64.0)*1000.0; 

       outputFormat.setLong(MediaFormat.KEY_DURATION, (long)durationInMs); 
       //Logger.getLogger(MainActivity.class.getSimpleName()).log(Level.INFO, codec.getOutputFormat().toString()); 

       codec.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
       codec.start(); 

       ByteBuffer[] inputBuffers = codec.getInputBuffers(); 
       ByteBuffer[] outputBuffer = codec.getOutputBuffers(); 

       boolean hasMoreData = true; 
       MediaCodec.BufferInfo outBuffInfo = new BufferInfo(); 
       byte readBuffer[] = new byte[64000]; 
       byte writeBuffer[] = new byte[64000]; 

       do 
       { 
        int nextBuffer = codec.dequeueInputBuffer(1000); 
        logger.log(Level.INFO,"nextInputBuffer = "+nextBuffer); 

        if (nextBuffer >= 0) 
        { 



         ByteBuffer inBuf = inputBuffers[nextBuffer]; 
         inBuf.clear(); 
         int bytesRead = fis.read(readBuffer,0, inBuf.capacity()); 
         logger.log(Level.INFO,"Read = "+bytesRead); 

         if (bytesRead < inBuf.capacity()) 
         { 
          hasMoreData = false; 
         } 

         inBuf.put(readBuffer, 0, bytesRead); 

         codec.queueInputBuffer(nextBuffer, 0, bytesRead, 0, hasMoreData?0:MediaCodec.BUFFER_FLAG_END_OF_STREAM); 
        } 


        int outputBufferIndex = codec.dequeueOutputBuffer(outBuffInfo, 1000); 
        logger.log(Level.INFO,"nextOutputBuffer = "+outputBufferIndex); 
        logger.log(Level.INFO,"outBuffInfo offset = "+outBuffInfo.offset); 
        logger.log(Level.INFO,"outBuffInfo size = "+outBuffInfo.size); 
        logger.log(Level.INFO,"outBuffInfo flags = "+outBuffInfo.flags); 


        //while (outputBufferIndex > -1) 
        //{ 

         outputBuffer[outputBufferIndex].position(outBuffInfo.offset); 
         outputBuffer[outputBufferIndex].get(writeBuffer,0,outBuffInfo.size); 

         fos.write(writeBuffer,0, outBuffInfo.size); 
         logger.log(Level.INFO,"Writing = "+outBuffInfo.size+" bytes"); 


         outputBuffer[outputBufferIndex].clear(); 

         codec.releaseOutputBuffer(outputBufferIndex, false); 

         if (outBuffInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) 
         { 
          codec.flush(); 
          codec.stop(); 
          codec.release(); 
          break; 
         } 

         //outputBufferIndex = codec.dequeueOutputBuffer(outBuffInfo, 1000); 
         //logger.log(Level.INFO,"nextOutputBuffer = "+outputBufferIndex); 
        //} 

       } while (outBuffInfo.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM); 

       fis.close(); 
       fos.flush(); 
       fos.close(); 



      } 
      catch (Exception e) 
      { 
       Logger.getLogger(MainActivity.class.getSimpleName()).log(Level.INFO, "Codec Error",e); 
      } 

      logger.log(Level.INFO,"Done"); 

      return null; 
     } 

    }.execute(); 
} 
+0

你讓你的AAC文件可讀?你能分享嗎?...? – 2013-02-14 11:44:57

+0

不幸的是,我確定我錯過了一些標題信息,但發現什麼是相當難以捉摸的。 – 2013-02-20 05:43:24

回答

1

基本上,你寫的所有生成的編碼緩衝到一個文件,但缺少各種元數據相關曲目/樣品/文件等

的我猜我的解決方案可以想到的是:或者你可以找到一個好的muxer庫來將緩衝區寫入正確的文件格式,或者你必須等待,看看未來的Android是否會爲你提供這樣的API。

0

該文件無法播放,因爲它不包含任何標題信息。一個簡單的(ish)方法來使這個可播放的是添加ADTS標頭到原始AAC幀。你可以找到一個描述here

祝你好運!

1

爲它選擇一個容器。我更喜歡adts,但flv/mp4也可以。

將有效載荷數據複製到一個足夠大的容器中,只需添加你的位。所以在網上淘了我的解決方案後,我工作的一些片斷到位

profile =(configParams[0]>>3)&0x1f; 

    frequency_index = (this.configParams[0]&0x7) <<1 | (this.configParams[1]>>7) &0x1; 

    channel_config = (this.configParams[1]>>3) &0xf; 

    int finallength = encoded_length + 7;  
    ENCodedByteArray[0] = (byte) 0xff; 
    ENCodedByteArray[1] = (byte) 0xf1; 
    ENCodedByteArray[2] = (byte) (((profile - 1) << 6) + (frequency_index << 2) +(channel_config >> 2)); 
    ENCodedByteArray[3] = (byte) (((channel_config & 0x3) << 6) + (finallength >> 11)); 
    ENCodedByteArray[4] = (byte)((finallength & 0x7ff) >> 3); 
    ENCodedByteArray[5] = (byte) (((finallength & 7) << 5) + 0x1f) ; 
    ENCodedByteArray[6] = (byte) 0xfc; 

使用類似下面,調用上述

  byte chunkADTS[]=new byte[info.size + 7]; 
      fillInADTSHeader(chunkADTS,info.size); 
      outputBuffers[bR].get(chunkADTS,7,info.size); 
      buffer.pushData(chunkADTS); 
3

要回答我的問題,

在Android中4.3及以上版本是MediaMuxer類,它爲音頻和視頻流創建MPEG4容器。

public boolean encode(File outputFile) 

{

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) 
{ 
    try 
    { 
     mInputWav.close(); 
    } 
    catch (IOException e) 
    { 
     logger.log(Level.WARNING,"Unable to close Input Wav File ",e); 
    } 

    throw new UnsupportedOperationException("Only Available on Android 4.3 and Above"); 
} 

try 
{ 

    int sampleRate = mInputWav.getSampleRate(); 
    int percentComplete = 0; 
    int fileSize = mInputWav.getDataLength(); 
    int totalBytesRead = 0; 

    if (outputFile.exists()) outputFile.delete(); 

    MediaMuxer mux = new MediaMuxer(outputFile.getAbsolutePath(), OutputFormat.MUXER_OUTPUT_MPEG_4); 

    MediaCodec codec = MediaCodec.createEncoderByType("audio/mp4a-latm"); 

    MediaFormat outputFormat = MediaFormat.createAudioFormat("audio/mp4a-latm",sampleRate,1); 
    outputFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); 
    outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, 128000); 

    codec.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
    codec.start(); 

    ByteBuffer[] inputBuffers = codec.getInputBuffers(); 
    ByteBuffer[] outputBuffer = codec.getOutputBuffers(); 

    boolean hasMoreData = true; 
    MediaCodec.BufferInfo outBuffInfo = new BufferInfo(); 
    byte readBuffer[] = new byte[64000]; 
    double presentationTimeUs=0; 
    int audioTrackIdx=0; 

    do 
    { 

     int nextBuffer = 0; 
     while (nextBuffer != -1 && hasMoreData) 
     { 
      nextBuffer = codec.dequeueInputBuffer(1000); 

      if (nextBuffer >= 0) 
      { 
       ByteBuffer inBuf = inputBuffers[nextBuffer]; 
       inBuf.clear(); 
       int bytesRead = mInputWav.read(readBuffer,0, inBuf.limit()); 

       if (bytesRead == -1) 
       { 
        hasMoreData = false;         
        codec.queueInputBuffer(nextBuffer, 0, 0, (long)presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM); 
       } 
       else 
       { 

        totalBytesRead += bytesRead;        
        inBuf.put(readBuffer, 0, bytesRead); 

        codec.queueInputBuffer(nextBuffer, 0, bytesRead, (long)presentationTimeUs, 0); 

        presentationTimeUs = 1000000l * (totalBytesRead)/2/sampleRate; 
       } 
      } 

     } 


     //Drain audio 
     int outputBufferIndex = 0; 
     while (outputBufferIndex != MediaCodec.INFO_TRY_AGAIN_LATER) 
     { 
      outputBufferIndex = codec.dequeueOutputBuffer(outBuffInfo, 10000); 
      if (outputBufferIndex >= 0) 
      { 

       ByteBuffer encodedData = outputBuffer[outputBufferIndex]; 
       encodedData.position(outBuffInfo.offset); 
       encodedData.limit(outBuffInfo.offset + outBuffInfo.size); 

       if ((outBuffInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)!= 0 && outBuffInfo.size != 0) 
       { 
        logger.log(Level.FINE, "video encoder: codec config buffer"); 
        // Simply ignore codec config buffers. 
        codec.releaseOutputBuffer(outputBufferIndex, false); 

       } 
       else 
       { 
        mux.writeSampleData(audioTrackIdx, outputBuffer[outputBufferIndex], outBuffInfo); 
        codec.releaseOutputBuffer(outputBufferIndex, false); 
       } 

      } 
      else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) 
      { 
       logger.info("Output Format Changed :"+codec.getOutputFormat().toString()); 
       outputFormat = codec.getOutputFormat(); 
       audioTrackIdx = mux.addTrack(outputFormat); 
       mux.start(); 

      } 
      else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) 
      { 
       logger.info("Output Buffers Changed: shouldn't happen on an encode "); 
      } 
      else if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) 
      { 
       logger.info("Encoder Timed Out"); 
      } 
      else 
      { 
       logger.info("Unkown return code from dequeueOutputBuffer "+outputBufferIndex); 
      } 
     } 

     percentComplete = (int)Math.round(((float)totalBytesRead/(float)fileSize)*100.0); 
     logger.info("Percent Complete "+percentComplete+"%"); 

     if (mListener != null) 
     { 
      mListener.onProgressUpdate(percentComplete); 
     } 

    } while (outBuffInfo.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM && !mCanceled); 

    mInputWav.close(); 
    mux.stop(); 
    mux.release(); 

    logger.info("Finished"); 



} 
catch (Exception e) 
{ 
    logger.log(Level.INFO, "Codec Error",e); 
} 


logger.log(Level.INFO,"Done"); 

return true; 

}