android MediaCodec 音频编解码的实现

来源:互联网 发布:淘宝电子琴小天吏 编辑:程序博客网 时间:2024/05/17 06:46
转载请标明出处http://blog.csdn.net/tinsanmr/article/details/51049179 ,本文出自:【Tinsan的博客】

从今天开始 每周不定期更新博客,把这一周在工作与学习中遇到的问题做个总结。俗话说:好记性不如写博客,善于总结的人才能走的更远。写博客这种利人利己的好处我就不一 一列举了,总之,谁做谁知道,哈哈。在文章中如果有什么问题或者错误,欢迎各位的讨论和指正。好了,步入正题,来看看我们今天要讲的MediaCodec

一、概述

由于项目的需要,需要将mp3文件转码为aac音频文件,起初打算移植FFmpeg到项目中,无奈FFmpeg过于庞大,项目中的音频转码只是一个辅助util,并不是主要功能。所以打算用MediaCodec来实现这一需求。网上关于MediaCodec的文章少的可怜,写demo的过程中踩到了无数的坑。不过,在http://blog.csdn.net/tn0521/article/details/44980183这篇文章的指引下,终于在耳机中听到了那美妙的旋律,激动的我把这一首歌听了一星期,因为他出自我的手。哈哈,开个玩笑!在此对这片文章的原创表示感谢,这也是我决定写博客的原因之一,利人利己。

二、转码实现原理

本篇文章以mp3转码成aac为例,转码实现原理:mp3->pcm->aac,首先将mp3解码成PCM,再将PCM编码成aac格式的音频文件。

PCM:可以将它理解为,未经过压缩的数字信号,mp3、aac等 理解为pcm压缩后的文件。播放器在播放mp3、aac等文件时要先将mp3等文件解码成PCM数据,然后再将PCM送到底层去处理播放

此处就好比 我要将rar压缩包内的文件改用zip压缩,->解压rar-->文件-->压缩zip

三、遇到问题

1、编解码过程中会卡主:此为参数设置引起的,下面代码中会提到

2、编码的aac音频不能播放:在编码过程中需要为aac音频添加ADTS  head,代码中有体现

3、最头痛的,转码速度太慢,转码一首歌长达5分钟。

      此问题究其原因,是由于MediaExtractor每次喂给MediaCodec的数据太少,每次只喂一帧的数据,通过打印的log发现size不到1k,严重影响效率,后来尝试不用MediaExtractor去读数据,直接开流 BufferInputStream设置200k ,每次循环喂给MediaCodec200k的数据 , 最终!!! 在三星手机上完美运行,一次转码由5分钟,直接降到10多秒,但是,注意但是!!!  此方法在其他测试机上全报错,泪奔。

  无奈,开线程,将解码和编码分别放到两个线程里面去执行,并且让MediaExtractor读取多次数据后再交给MediaCodec去处理,此方法转码一首歌大约1分钟左右(各位如果有好的方法不吝赐教,本人非常感激

四、代码实现

1)初始化解码器

  MediaExtractor:可用于分离视频文件的音轨和视频轨道,如果你只想要视频,那么用selectTrack方法选中视频轨道,然后用readSampleData读出数据,这样你就得到了一个没有声音的视频。此处我们传入的是一个音频文件(mp3),所以也就只有一个轨道,音频轨道

 mime:用来表示媒体文件的格式 mp3为audio/mpeg;aac为audio/mp4a-latm;mp4为video/mp4v-es 此处注意前缀 音频前缀为audio,视频前缀为video 我们可用此区别区分媒体文件内的音频轨道和视频轨道

mime的各种类型定义在MediaFormat静态常量中

  MediaCodec.createDecoderByType(mime) 创建对应格式的解码器  要解码mp3 那么mime="audio/mpeg" 或者MediaFormat.MIMETYPE_AUDIO_MPEG其它同理


[java] view plain copy
 print?
  1. /** 
  2.      * 初始化解码器 
  3.      */  
  4.     private void initMediaDecode() {  
  5.         try {  
  6.             mediaExtractor=new MediaExtractor();//此类可分离视频文件的音轨和视频轨道  
  7.             mediaExtractor.setDataSource(srcPath);//媒体文件的位置  
  8.             for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {//遍历媒体轨道 此处我们传入的是音频文件,所以也就只有一条轨道  
  9.                 MediaFormat format = mediaExtractor.getTrackFormat(i);  
  10.                 String mime = format.getString(MediaFormat.KEY_MIME);  
  11.                 if (mime.startsWith("audio")) {//获取音频轨道  
  12. //                    format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 200 * 1024);  
  13.                     mediaExtractor.selectTrack(i);//选择此音频轨道  
  14.                     mediaDecode = MediaCodec.createDecoderByType(mime);//创建Decode解码器  
  15.                     mediaDecode.configure(format, nullnull0);  
  16.                     break;  
  17.                 }  
  18.             }  
  19.         } catch (IOException e) {  
  20.             e.printStackTrace();  
  21.         }  
  22.   
  23.         if (mediaDecode == null) {  
  24.             Log.e(TAG, "create mediaDecode failed");  
  25.             return;  
  26.         }  
  27.         mediaDecode.start();//启动MediaCodec ,等待传入数据  
  28.         decodeInputBuffers=mediaDecode.getInputBuffers();//MediaCodec在此ByteBuffer[]中获取输入数据  
  29.         decodeOutputBuffers=mediaDecode.getOutputBuffers();//MediaCodec将解码后的数据放到此ByteBuffer[]中 我们可以直接在这里面得到PCM数据  
  30.         decodeBufferInfo=new MediaCodec.BufferInfo();//用于描述解码得到的byte[]数据的相关信息  
  31.         showLog("buffers:" + decodeInputBuffers.length);  
  32.     }  

2)初始化编码器 

编码器的创建于解码器的类似,只不过解码器的MediaFormat直接在音频文件内获取就可以了,编码器的MediaFormat需要自己来创建

[java] view plain copy
 print?
  1. /** 
  2.  * 初始化AAC编码器 
  3.  */  
  4. private void initAACMediaEncode() {  
  5.     try {  
  6.         MediaFormat encodeFormat = MediaFormat.createAudioFormat(encodeType, 441002);//参数对应-> mime type、采样率、声道数  
  7.         encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);//比特率  
  8.         encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);  
  9.         encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 100 * 1024);//作用于inputBuffer的大小  
  10.         mediaEncode = MediaCodec.createEncoderByType(encodeType);  
  11.         mediaEncode.configure(encodeFormat, nullnull, MediaCodec.CONFIGURE_FLAG_ENCODE);  
  12.     } catch (IOException e) {  
  13.         e.printStackTrace();  
  14.     }  
  15.   
  16.     if (mediaEncode == null) {  
  17.         Log.e(TAG, "create mediaEncode failed");  
  18.         return;  
  19.     }  
  20.     mediaEncode.start();  
  21.     encodeInputBuffers=mediaEncode.getInputBuffers();  
  22.     encodeOutputBuffers=mediaEncode.getOutputBuffers();  
  23.     encodeBufferInfo=new MediaCodec.BufferInfo();  
  24. }  


3)解码的实现


[java] view plain copy
 print?
  1. /** 
  2.      * 解码{@link #srcPath}音频文件 得到PCM数据块 
  3.      * @return 是否解码完所有数据 
  4.      */  
  5.     private void srcAudioFormatToPCM() {  
  6.         for (int i = 0; i < decodeInputBuffers.length-1; i++) {  
  7.         int inputIndex = mediaDecode.dequeueInputBuffer(-1);//获取可用的inputBuffer -1代表一直等待,0表示不等待 建议-1,避免丢帧  
  8.         if (inputIndex < 0) {  
  9.             codeOver =true;  
  10.             return;  
  11.         }  
  12.   
  13.         ByteBuffer inputBuffer = decodeInputBuffers[inputIndex];//拿到inputBuffer  
  14.         inputBuffer.clear();//清空之前传入inputBuffer内的数据  
  15.         int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);//MediaExtractor读取数据到inputBuffer中  
  16.         if (sampleSize <0) {//小于0 代表所有数据已读取完成  
  17.                 codeOver=true;  
  18.             }else {  
  19.                 mediaDecode.queueInputBuffer(inputIndex, 0, sampleSize, 00);//通知MediaDecode解码刚刚传入的数据  
  20.                 mediaExtractor.advance();//MediaExtractor移动到下一取样处  
  21.                 decodeSize+=sampleSize;  
  22.             }  
  23.         }  
  24.   
  25.         //获取解码得到的byte[]数据 参数BufferInfo上面已介绍 10000同样为等待时间 同上-1代表一直等待,0代表不等待。此处单位为微秒  
  26.         //此处建议不要填-1 有些时候并没有数据输出,那么他就会一直卡在这 等待  
  27.         int outputIndex = mediaDecode.dequeueOutputBuffer(decodeBufferInfo, 10000);  
  28.   
  29. //        showLog("decodeOutIndex:" + outputIndex);  
  30.         ByteBuffer outputBuffer;  
  31.         byte[] chunkPCM;  
  32.         while (outputIndex >= 0) {//每次解码完成的数据不一定能一次吐出 所以用while循环,保证解码器吐出所有数据  
  33.             outputBuffer = decodeOutputBuffers[outputIndex];//拿到用于存放PCM数据的Buffer  
  34.             chunkPCM = new byte[decodeBufferInfo.size];//BufferInfo内定义了此数据块的大小  
  35.             outputBuffer.get(chunkPCM);//将Buffer内的数据取出到字节数组中  
  36.             outputBuffer.clear();//数据取出后一定记得清空此Buffer MediaCodec是循环使用这些Buffer的,不清空下次会得到同样的数据  
  37.             putPCMData(chunkPCM);//自己定义的方法,供编码器所在的线程获取数据,下面会贴出代码  
  38.             mediaDecode.releaseOutputBuffer(outputIndex, false);//此操作一定要做,不然MediaCodec用完所有的Buffer后 将不能向外输出数据  
  39.             outputIndex = mediaDecode.dequeueOutputBuffer(decodeBufferInfo, 10000);//再次获取数据,如果没有数据输出则outputIndex=-1 循环结束  
  40.         }  
  41.   
  42.     }  


4)编码的实现


[java] view plain copy
 print?
  1. /** 
  2.      * 编码PCM数据 得到{@link #encodeType}格式的音频文件,并保存到{@link #dstPath} 
  3.      */  
  4.     private void dstAudioFormatFromPCM() {  
  5.   
  6.         int inputIndex;  
  7.         ByteBuffer inputBuffer;  
  8.         int outputIndex;  
  9.         ByteBuffer outputBuffer;  
  10.         byte[] chunkAudio;  
  11.         int outBitSize;  
  12.         int outPacketSize;  
  13.         byte[] chunkPCM;  
  14.   
  15. //        showLog("doEncode");  
  16.         for (int i = 0; i < encodeInputBuffers.length-1; i++) {  
  17.             chunkPCM=getPCMData();//获取解码器所在线程输出的数据 代码后边会贴上  
  18.             if (chunkPCM == null) {  
  19.                 break;  
  20.             }  
  21.             inputIndex = mediaEncode.dequeueInputBuffer(-1);//同解码器  
  22.             inputBuffer = encodeInputBuffers[inputIndex];//同解码器  
  23.             inputBuffer.clear();//同解码器  
  24.             inputBuffer.limit(chunkPCM.length);  
  25.             inputBuffer.put(chunkPCM);//PCM数据填充给inputBuffer  
  26.             mediaEncode.queueInputBuffer(inputIndex, 0, chunkPCM.length, 00);//通知编码器 编码  
  27.         }  
  28.   
  29.             outputIndex = mediaEncode.dequeueOutputBuffer(encodeBufferInfo, 10000);//同解码器  
  30.             while (outputIndex >= 0) {//同解码器  
  31.   
  32.                 outBitSize=encodeBufferInfo.size;  
  33.                 outPacketSize=outBitSize+7;//7为ADTS头部的大小  
  34.                 outputBuffer = encodeOutputBuffers[outputIndex];//拿到输出Buffer  
  35.                 outputBuffer.position(encodeBufferInfo.offset);  
  36.                 outputBuffer.limit(encodeBufferInfo.offset + outBitSize);  
  37.                 chunkAudio = new byte[outPacketSize];  
  38.                 addADTStoPacket(chunkAudio,outPacketSize);//添加ADTS 代码后面会贴上  
  39.                 outputBuffer.get(chunkAudio, 7, outBitSize);//将编码得到的AAC数据 取出到byte[]中 偏移量offset=7 你懂得  
  40.                 outputBuffer.position(encodeBufferInfo.offset);  
  41. //                showLog("outPacketSize:" + outPacketSize + " encodeOutBufferRemain:" + outputBuffer.remaining());  
  42.                 try {  
  43.                     bos.write(chunkAudio,0,chunkAudio.length);//BufferOutputStream 将文件保存到内存卡中 *.aac   
  44.                 } catch (IOException e) {  
  45.                     e.printStackTrace();  
  46.                 }  
  47.   
  48.                 mediaEncode.releaseOutputBuffer(outputIndex,false);  
  49.                 outputIndex = mediaEncode.dequeueOutputBuffer(encodeBufferInfo, 10000);  
  50.   
  51.             }  
  52.   
  53.     }  


[java] view plain copy
 print?
  1. /** 
  2.      * 添加ADTS头 
  3.      * @param packet 
  4.      * @param packetLen 
  5.      */  
  6.     private void addADTStoPacket(byte[] packet, int packetLen) {  
  7.         int profile = 2// AAC LC  
  8.         int freqIdx = 4// 44.1KHz  
  9.         int chanCfg = 2// CPE  
  10.   
  11.   
  12. // fill in ADTS data  
  13.         packet[0] = (byte0xFF;  
  14.         packet[1] = (byte0xF9;  
  15.         packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));  
  16.         packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));  
  17.         packet[4] = (byte) ((packetLen & 0x7FF) >> 3);  
  18.         packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);  
  19.         packet[6] = (byte0xFC;  
  20.     }  


5)完整代码

[java] view plain copy
 print?
  1. package com.example.tinsan.mediaparser;  
  2.   
  3.   
  4.         import android.media.MediaCodec;  
  5.         import android.media.MediaCodecInfo;  
  6.         import android.media.MediaExtractor;  
  7.         import android.media.MediaFormat;  
  8.         import android.util.Log;  
  9.   
  10.         import java.io.BufferedInputStream;  
  11.         import java.io.BufferedOutputStream;  
  12.         import java.io.File;  
  13.         import java.io.FileInputStream;  
  14.         import java.io.FileNotFoundException;  
  15.         import java.io.FileOutputStream;  
  16.         import java.io.IOException;  
  17.         import java.nio.ByteBuffer;  
  18.         import java.util.ArrayList;  
  19.   
  20. /** 
  21.  * Created by senshan_wang on 2016/3/31. 
  22.  */  
  23. public class AudioCodec {  
  24.   
  25.     private static final String TAG = "AudioCodec";  
  26.     private String encodeType;  
  27.     private String srcPath;  
  28.     private String dstPath;  
  29.     private MediaCodec mediaDecode;  
  30.     private MediaCodec mediaEncode;  
  31.     private MediaExtractor mediaExtractor;  
  32.     private ByteBuffer[] decodeInputBuffers;  
  33.     private ByteBuffer[] decodeOutputBuffers;  
  34.     private ByteBuffer[] encodeInputBuffers;  
  35.     private ByteBuffer[] encodeOutputBuffers;  
  36.     private MediaCodec.BufferInfo decodeBufferInfo;  
  37.     private MediaCodec.BufferInfo encodeBufferInfo;  
  38.     private FileOutputStream fos;  
  39.     private BufferedOutputStream bos;  
  40.     private FileInputStream fis;  
  41.     private BufferedInputStream bis;  
  42.     private ArrayList<byte[]> chunkPCMDataContainer;//PCM数据块容器  
  43.     private OnCompleteListener onCompleteListener;  
  44.     private OnProgressListener onProgressListener;  
  45.     private long fileTotalSize;  
  46.     private long decodeSize;  
  47.   
  48.   
  49.     public static AudioCodec newInstance() {  
  50.         return new AudioCodec();  
  51.     }  
  52.   
  53.     /** 
  54.      * 设置编码器类型 
  55.      * @param encodeType 
  56.      */  
  57.     public void setEncodeType(String encodeType) {  
  58.         this.encodeType=encodeType;  
  59.     }  
  60.   
  61.     /** 
  62.      * 设置输入输出文件位置 
  63.      * @param srcPath 
  64.      * @param dstPath 
  65.      */  
  66.     public void setIOPath(String srcPath, String dstPath) {  
  67.         this.srcPath=srcPath;  
  68.         this.dstPath=dstPath;  
  69.     }  
  70.   
  71.     /** 
  72.      * 此类已经过封装 
  73.      * 调用prepare方法 会初始化Decode 、Encode 、输入输出流 等一些列操作 
  74.      */  
  75.     public void prepare() {  
  76.   
  77.         if (encodeType == null) {  
  78.             throw new IllegalArgumentException("encodeType can't be null");  
  79.         }  
  80.   
  81.         if (srcPath == null) {  
  82.             throw new IllegalArgumentException("srcPath can't be null");  
  83.         }  
  84.   
  85.         if (dstPath == null) {  
  86.             throw new IllegalArgumentException("dstPath can't be null");  
  87.         }  
  88.   
  89.         try {  
  90.             fos = new FileOutputStream(new File(dstPath));  
  91.             bos = new BufferedOutputStream(fos,200*1024);  
  92.             File file = new File(srcPath);  
  93.             fileTotalSize=file.length();  
  94.         } catch (IOException e) {  
  95.             e.printStackTrace();  
  96.         }  
  97.         chunkPCMDataContainer= new ArrayList<>();  
  98.         initMediaDecode();//解码器  
  99.   
  100.         if (encodeType == MediaFormat.MIMETYPE_AUDIO_AAC) {  
  101.             initAACMediaEncode();//AAC编码器  
  102.         }else if (encodeType == MediaFormat.MIMETYPE_AUDIO_MPEG) {  
  103.             initMPEGMediaEncode();//mp3编码器  
  104.         }  
  105.   
  106.     }  
  107.   
  108.     /** 
  109.      * 初始化解码器 
  110.      */  
  111.     private void initMediaDecode() {  
  112.         try {  
  113.             mediaExtractor=new MediaExtractor();//此类可分离视频文件的音轨和视频轨道  
  114.             mediaExtractor.setDataSource(srcPath);//媒体文件的位置  
  115.             for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {//遍历媒体轨道 此处我们传入的是音频文件,所以也就只有一条轨道  
  116.                 MediaFormat format = mediaExtractor.getTrackFormat(i);  
  117.                 String mime = format.getString(MediaFormat.KEY_MIME);  
  118.                 if (mime.startsWith("audio")) {//获取音频轨道  
  119. //                    format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 200 * 1024);  
  120.                     mediaExtractor.selectTrack(i);//选择此音频轨道  
  121.                     mediaDecode = MediaCodec.createDecoderByType(mime);//创建Decode解码器  
  122.                     mediaDecode.configure(format, nullnull0);  
  123.                     break;  
  124.                 }  
  125.             }  
  126.         } catch (IOException e) {  
  127.             e.printStackTrace();  
  128.         }  
  129.   
  130.         if (mediaDecode == null) {  
  131.             Log.e(TAG, "create mediaDecode failed");  
  132.             return;  
  133.         }  
  134.         mediaDecode.start();//启动MediaCodec ,等待传入数据  
  135.         decodeInputBuffers=mediaDecode.getInputBuffers();//MediaCodec在此ByteBuffer[]中获取输入数据  
  136.         decodeOutputBuffers=mediaDecode.getOutputBuffers();//MediaCodec将解码后的数据放到此ByteBuffer[]中 我们可以直接在这里面得到PCM数据  
  137.         decodeBufferInfo=new MediaCodec.BufferInfo();//用于描述解码得到的byte[]数据的相关信息  
  138.         showLog("buffers:" + decodeInputBuffers.length);  
  139.     }  
  140.   
  141.   
  142.     /** 
  143.      * 初始化AAC编码器 
  144.      */  
  145.     private void initAACMediaEncode() {  
  146.         try {  
  147.             MediaFormat encodeFormat = MediaFormat.createAudioFormat(encodeType, 441002);//参数对应-> mime type、采样率、声道数  
  148.             encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);//比特率  
  149.             encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);  
  150.             encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 100 * 1024);  
  151.             mediaEncode = MediaCodec.createEncoderByType(encodeType);  
  152.             mediaEncode.configure(encodeFormat, nullnull, MediaCodec.CONFIGURE_FLAG_ENCODE);  
  153.         } catch (IOException e) {  
  154.             e.printStackTrace();  
  155.         }  
  156.   
  157.         if (mediaEncode == null) {  
  158.             Log.e(TAG, "create mediaEncode failed");  
  159.             return;  
  160.         }  
  161.         mediaEncode.start();  
  162.         encodeInputBuffers=mediaEncode.getInputBuffers();  
  163.         encodeOutputBuffers=mediaEncode.getOutputBuffers();  
  164.         encodeBufferInfo=new MediaCodec.BufferInfo();  
  165.     }  
  166.   
  167.     /** 
  168.      * 初始化MPEG编码器 
  169.      */  
  170.     private void initMPEGMediaEncode() {  
  171.           
  172.     }  
  173.   
  174.     private boolean codeOver = false;  
  175.     /** 
  176.      * 开始转码 
  177.      * 音频数据{@link #srcPath}先解码成PCM  PCM数据在编码成想要得到的{@link #encodeType}音频格式 
  178.      * mp3->PCM->aac 
  179.      */  
  180.     public void startAsync() {  
  181.         showLog("start");  
  182.   
  183.         new Thread(new DecodeRunnable()).start();  
  184.         new Thread(new EncodeRunnable()).start();  
  185.   
  186.     }  
  187.   
  188.     /** 
  189.      * 将PCM数据存入{@link #chunkPCMDataContainer} 
  190.      * @param pcmChunk PCM数据块 
  191.      */  
  192.     private void putPCMData(byte[] pcmChunk) {  
  193.         synchronized (AudioCodec.class) {//记得加锁  
  194.             chunkPCMDataContainer.add(pcmChunk);  
  195.         }  
  196.     }  
  197.   
  198.     /** 
  199.      * 在Container中{@link #chunkPCMDataContainer}取出PCM数据 
  200.      * @return PCM数据块 
  201.      */  
  202.     private byte[] getPCMData() {  
  203.         synchronized (AudioCodec.class) {//记得加锁  
  204.             showLog("getPCM:"+chunkPCMDataContainer.size());  
  205.             if (chunkPCMDataContainer.isEmpty()) {  
  206.                 return null;  
  207.             }  
  208.   
  209.             byte[] pcmChunk = chunkPCMDataContainer.get(0);//每次取出index 0 的数据  
  210.             chunkPCMDataContainer.remove(pcmChunk);//取出后将此数据remove掉 既能保证PCM数据块的取出顺序 又能及时释放内存  
  211.             return pcmChunk;  
  212.         }  
  213.     }  
  214.   
  215.   
  216.     /** 
  217.      * 解码{@link #srcPath}音频文件 得到PCM数据块 
  218.      * @return 是否解码完所有数据 
  219.      */  
  220.     private void srcAudioFormatToPCM() {  
  221.         for (int i = 0; i < decodeInputBuffers.length-1; i++) {  
  222.         int inputIndex = mediaDecode.dequeueInputBuffer(-1);//获取可用的inputBuffer -1代表一直等待,0表示不等待 建议-1,避免丢帧  
  223.         if (inputIndex < 0) {  
  224.             codeOver =true;  
  225.             return;  
  226.         }  
  227.   
  228.         ByteBuffer inputBuffer = decodeInputBuffers[inputIndex];//拿到inputBuffer  
  229.         inputBuffer.clear();//清空之前传入inputBuffer内的数据  
  230.         int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);//MediaExtractor读取数据到inputBuffer中  
  231.         if (sampleSize <0) {//小于0 代表所有数据已读取完成  
  232.                 codeOver=true;  
  233.             }else {  
  234.                 mediaDecode.queueInputBuffer(inputIndex, 0, sampleSize, 00);//通知MediaDecode解码刚刚传入的数据  
  235.                 mediaExtractor.advance();//MediaExtractor移动到下一取样处  
  236.                 decodeSize+=sampleSize;  
  237.             }  
  238.         }  
  239.   
  240.         //获取解码得到的byte[]数据 参数BufferInfo上面已介绍 10000同样为等待时间 同上-1代表一直等待,0代表不等待。此处单位为微秒  
  241.         //此处建议不要填-1 有些时候并没有数据输出,那么他就会一直卡在这 等待  
  242.         int outputIndex = mediaDecode.dequeueOutputBuffer(decodeBufferInfo, 10000);  
  243.   
  244. //        showLog("decodeOutIndex:" + outputIndex);  
  245.         ByteBuffer outputBuffer;  
  246.         byte[] chunkPCM;  
  247.         while (outputIndex >= 0) {//每次解码完成的数据不一定能一次吐出 所以用while循环,保证解码器吐出所有数据  
  248.             outputBuffer = decodeOutputBuffers[outputIndex];//拿到用于存放PCM数据的Buffer  
  249.             chunkPCM = new byte[decodeBufferInfo.size];//BufferInfo内定义了此数据块的大小  
  250.             outputBuffer.get(chunkPCM);//将Buffer内的数据取出到字节数组中  
  251.             outputBuffer.clear();//数据取出后一定记得清空此Buffer MediaCodec是循环使用这些Buffer的,不清空下次会得到同样的数据  
  252.             putPCMData(chunkPCM);//自己定义的方法,供编码器所在的线程获取数据,下面会贴出代码  
  253.             mediaDecode.releaseOutputBuffer(outputIndex, false);//此操作一定要做,不然MediaCodec用完所有的Buffer后 将不能向外输出数据  
  254.             outputIndex = mediaDecode.dequeueOutputBuffer(decodeBufferInfo, 10000);//再次获取数据,如果没有数据输出则outputIndex=-1 循环结束  
  255.         }  
  256.   
  257.     }  
  258.   
  259.     /** 
  260.      * 编码PCM数据 得到{@link #encodeType}格式的音频文件,并保存到{@link #dstPath} 
  261.      */  
  262.     private void dstAudioFormatFromPCM() {  
  263.   
  264.         int inputIndex;  
  265.         ByteBuffer inputBuffer;  
  266.         int outputIndex;  
  267.         ByteBuffer outputBuffer;  
  268.         byte[] chunkAudio;  
  269.         int outBitSize;  
  270.         int outPacketSize;  
  271.         byte[] chunkPCM;  
  272.   
  273. //        showLog("doEncode");  
  274.         for (int i = 0; i < encodeInputBuffers.length-1; i++) {  
  275.             chunkPCM=getPCMData();//获取解码器所在线程输出的数据 代码后边会贴上  
  276.             if (chunkPCM == null) {  
  277.                 break;  
  278.             }  
  279.             inputIndex = mediaEncode.dequeueInputBuffer(-1);//同解码器  
  280.             inputBuffer = encodeInputBuffers[inputIndex];//同解码器  
  281.             inputBuffer.clear();//同解码器  
  282.             inputBuffer.limit(chunkPCM.length);  
  283.             inputBuffer.put(chunkPCM);//PCM数据填充给inputBuffer  
  284.             mediaEncode.queueInputBuffer(inputIndex, 0, chunkPCM.length, 00);//通知编码器 编码  
  285.         }  
  286.   
  287.             outputIndex = mediaEncode.dequeueOutputBuffer(encodeBufferInfo, 10000);//同解码器  
  288.             while (outputIndex >= 0) {//同解码器  
  289.   
  290.                 outBitSize=encodeBufferInfo.size;  
  291.                 outPacketSize=outBitSize+7;//7为ADTS头部的大小  
  292.                 outputBuffer = encodeOutputBuffers[outputIndex];//拿到输出Buffer  
  293.                 outputBuffer.position(encodeBufferInfo.offset);  
  294.                 outputBuffer.limit(encodeBufferInfo.offset + outBitSize);  
  295.                 chunkAudio = new byte[outPacketSize];  
  296.                 addADTStoPacket(chunkAudio,outPacketSize);//添加ADTS 代码后面会贴上  
  297.                 outputBuffer.get(chunkAudio, 7, outBitSize);//将编码得到的AAC数据 取出到byte[]中 偏移量offset=7 你懂得  
  298.                 outputBuffer.position(encodeBufferInfo.offset);  
  299. //                showLog("outPacketSize:" + outPacketSize + " encodeOutBufferRemain:" + outputBuffer.remaining());  
  300.                 try {  
  301.                     bos.write(chunkAudio,0,chunkAudio.length);//BufferOutputStream 将文件保存到内存卡中 *.aac  
  302.                 } catch (IOException e) {  
  303.                     e.printStackTrace();  
  304.                 }  
  305.   
  306.                 mediaEncode.releaseOutputBuffer(outputIndex,false);  
  307.                 outputIndex = mediaEncode.dequeueOutputBuffer(encodeBufferInfo, 10000);  
  308.   
  309.             }  
  310.     }  
  311.   
  312.     /** 
  313.      * 添加ADTS头 
  314.      * @param packet 
  315.      * @param packetLen 
  316.      */  
  317.     private void addADTStoPacket(byte[] packet, int packetLen) {  
  318.         int profile = 2// AAC LC  
  319.         int freqIdx = 4// 44.1KHz  
  320.         int chanCfg = 2// CPE  
  321.   
  322.   
  323. // fill in ADTS data  
  324.         packet[0] = (byte0xFF;  
  325.         packet[1] = (byte0xF9;  
  326.         packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));  
  327.         packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));  
  328.         packet[4] = (byte) ((packetLen & 0x7FF) >> 3);  
  329.         packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);  
  330.         packet[6] = (byte0xFC;  
  331.     }  
  332.   
  333.     /** 
  334.      * 释放资源 
  335.      */  
  336.     public void release() {  
  337.         try {  
  338.             if (bos != null) {  
  339.                 bos.flush();  
  340.             }  
  341.         } catch (IOException e) {  
  342.             e.printStackTrace();  
  343.         }finally {  
  344.             if (bos != null) {  
  345.                 try {  
  346.                     bos.close();  
  347.                 } catch (IOException e) {  
  348.                     e.printStackTrace();  
  349.                 }finally {  
  350.                     bos=null;  
  351.                 }  
  352.             }  
  353.         }  
  354.   
  355.         try {  
  356.             if (fos != null) {  
  357.                 fos.close();  
  358.             }  
  359.         } catch (IOException e) {  
  360.             e.printStackTrace();  
  361.         }finally {  
  362.             fos=null;  
  363.         }  
  364.   
  365.         if (mediaEncode != null) {  
  366.             mediaEncode.stop();  
  367.             mediaEncode.release();  
  368.             mediaEncode=null;  
  369.         }  
  370.   
  371.         if (mediaDecode != null) {  
  372.             mediaDecode.stop();  
  373.             mediaDecode.release();  
  374.             mediaDecode=null;  
  375.         }  
  376.   
  377.         if (mediaExtractor != null) {  
  378.             mediaExtractor.release();  
  379.             mediaExtractor=null;  
  380.         }  
  381.   
  382.         if (onCompleteListener != null) {  
  383.             onCompleteListener=null;  
  384.         }  
  385.   
  386.         if (onProgressListener != null) {  
  387.             onProgressListener=null;  
  388.         }  
  389.         showLog("release");  
  390.     }  
  391.   
  392.     /** 
  393.      * 解码线程 
  394.      */  
  395.     private class DecodeRunnable implements Runnable{  
  396.   
  397.         @Override  
  398.         public void run() {  
  399.             while (!codeOver) {  
  400.                 srcAudioFormatToPCM();  
  401.             }  
  402.         }  
  403.     }  
  404.   
  405.     /** 
  406.      * 编码线程 
  407.      */  
  408.     private class EncodeRunnable implements Runnable {  
  409.   
  410.         @Override  
  411.         public void run() {  
  412.             long t=System.currentTimeMillis();  
  413.             while (!codeOver || !chunkPCMDataContainer.isEmpty()) {  
  414.                 dstAudioFormatFromPCM();  
  415.             }  
  416.             if (onCompleteListener != null) {  
  417.                 onCompleteListener.completed();  
  418.             }  
  419.             showLog("size:"+fileTotalSize+" decodeSize:"+decodeSize+"time:"+(System.currentTimeMillis()-t));  
  420.         }  
  421.     }  
  422.   
  423.   
  424.     /** 
  425.      * 转码完成回调接口 
  426.      */  
  427.     public interface OnCompleteListener{  
  428.         void completed();  
  429.     }  
  430.   
  431.     /** 
  432.      * 转码进度监听器 
  433.      */  
  434.     public interface OnProgressListener{  
  435.         void progress();  
  436.     }  
  437.   
  438.     /** 
  439.      * 设置转码完成监听器 
  440.      * @param onCompleteListener 
  441.      */  
  442.     public void setOnCompleteListener(OnCompleteListener onCompleteListener) {  
  443.         this.onCompleteListener=onCompleteListener;  
  444.     }  
  445.   
  446.     public void setOnProgressListener(OnProgressListener onProgressListener) {  
  447.         this.onProgressListener = onProgressListener;  
  448.     }  
  449.   
  450.     private void showLog(String msg) {  
  451.         Log.e("AudioCodec", msg);  
  452.     }  
  453. }  


6)调用

此类已经过封装,可通过下面的方法调用

[java] view plain copy
 print?
  1. String path=Environment.getExternalStorageDirectory().getAbsolutePath();  
  2. AudioCodec audioCodec=AudioCodec.newInstance();  
  3. audioCodec.setEncodeType(MediaFormat.MIMETYPE_AUDIO_MPEG);  
  4. audioCodec.setIOPath(path + "/codec.aac", path + "/encode.mp3");  
  5. audioCodec.prepare();  
  6. audioCodec.startAsync();  
  7. audioCodec.setOnCompleteListener(new AudioCodec.OnCompleteListener() {  
  8.     @Override  
  9.     public void completed() {  
  10.         audioCodec.release();  
  11.     }  
  12. });  
原创粉丝点击