MediaCodec硬编码pcm2aac

MediaCodecAndroid(api>=16)提供的一个多媒体硬解编码库,能实现音视频的编解码。

工作原理:其内部有2个队列,一个是输入队列,一个是输出队列。输入队列负责存储编 解码前的原始数据存储,并输送给MediaCodec处理;输出队列负责存储编解码后 的新数据,可以直接处理或保存到文件中。

AAC 的头部信息介绍 :https://blog.csdn.net/jay100500/article/details/52955232


//mediacodec
   private MediaFormat encoderFormat = null;
   private MediaCodec encoder = null;
   private FileOutputStream outputStream = null;
   private MediaCodec.BufferInfo info = null;
   private int perpcmsize = 0;
   private byte[] outByteBuffer = null;
   private int aacsamplerate = 4;
   private double recordTime = 0;
   private int audioSamplerate = 0;

   private void initMediacodec(int samperate, File outfile)
   {
       try {
           aacsamplerate = getADTSsamplerate(samperate);
           //立体声
           encoderFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, samperate, 2);
           //96kbps fm音质
           encoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);
           encoderFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
           encoderFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 4096);
           encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
           info = new MediaCodec.BufferInfo();
           if(encoder == null)
           {
               MyLog.d("craete encoder wrong");
               return;
           }
           recordTime = 0;
           encoder.configure(encoderFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
           outputStream = new FileOutputStream(outfile);
           encoder.start();
       } catch (IOException e) {
           e.printStackTrace();
       }
   }

   private void encodecPcmToAAc(int size, byte[] buffer)
   {
       if(buffer != null && encoder != null)
       {
           //录音时间  size/ 采样率*声道数 * bits/8
           recordTime += size * 1.0 / (audioSamplerate * 2 * (16 / 8));
           MyLog.d("recordTime = " + recordTime);
           //回掉
           if(wlOnRecordTimeListener != null)
           {
               wlOnRecordTimeListener.onRecordTime((int) recordTime);
           }

           int inputBufferindex = encoder.dequeueInputBuffer(0);
           if(inputBufferindex >= 0)
           {
               ByteBuffer byteBuffer = encoder.getInputBuffers()[inputBufferindex];
               byteBuffer.clear();
               byteBuffer.put(buffer);
               encoder.queueInputBuffer(inputBufferindex, 0, size, 0, 0);
           }

           int index = encoder.dequeueOutputBuffer(info, 0);
           while(index >= 0)
           {
               try {
                   perpcmsize = info.size + 7;
                   outByteBuffer = new byte[perpcmsize];

                   ByteBuffer byteBuffer = encoder.getOutputBuffers()[index];
                   byteBuffer.position(info.offset);
                   byteBuffer.limit(info.offset + info.size);

                   addADtsHeader(outByteBuffer, perpcmsize, aacsamplerate);

                   byteBuffer.get(outByteBuffer, 7, info.size);
                   byteBuffer.position(info.offset);
                   outputStream.write(outByteBuffer, 0, perpcmsize);

                   encoder.releaseOutputBuffer(index, false);
                   index = encoder.dequeueOutputBuffer(info, 0);
                   outByteBuffer = null;
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
   }

   private void addADtsHeader(byte[] packet, int packetLen, int samplerate)
   {
       int profile = 2; // AAC LC
       int freqIdx = samplerate; // samplerate
       int chanCfg = 2; // CPE

       packet[0] = (byte) 0xFF; // 0xFFF(12bit) 这里只取了8位,所以还差4位放到下一个里面
       packet[1] = (byte) 0xF9; // 第一个t位放F
       packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
       packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
       packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
       packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
       packet[6] = (byte) 0xFC;
   }

   private int getADTSsamplerate(int samplerate)
   {
       int rate = 4;
       switch (samplerate)
       {
           case 96000:
               rate = 0;
               break;
           case 88200:
               rate = 1;
               break;
           case 64000:
               rate = 2;
               break;
           case 48000:
               rate = 3;
               break;
           case 44100:
               rate = 4;
               break;
           case 32000:
               rate = 5;
               break;
           case 24000:
               rate = 6;
               break;
           case 22050:
               rate = 7;
               break;
           case 16000:
               rate = 8;
               break;
           case 12000:
               rate = 9;
               break;
           case 11025:
               rate = 10;
               break;
           case 8000:
               rate = 11;
               break;
           case 7350:
               rate = 12;
               break;
       }
       return rate;
   }

   private void releaseMedicacodec()
   {
       if(encoder == null)
       {
           return;
       }
       try {
           recordTime = 0;
           outputStream.close();
           outputStream = null;
           encoder.stop();
           encoder.release();
           encoder = null;
           encoderFormat = null;
           info = null;
           initmediacodec = false;

           MyLog.d("录制完成...");
       } catch (IOException e) {
           e.printStackTrace();
       }
       finally {
           if(outputStream != null)
           {
               try {
                   outputStream.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
               outputStream = null;
           }
       }
   }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

viewpager循环滚动和自动轮播的问题

ViewPager是一个常用的android组件,不过通常我们使用ViewPager的时候不能实现左右无限循环滑动,在滑到边界的时候会看到一个不能翻页的动画,...

35860
来自专栏一个会写诗的程序员的博客

关于Webview如何自动登录保存登录信息

10830
来自专栏向治洪

android电话拦截

其实大家可以下载 xxx卫士看下,它设置来电拒接模式后,都是会启动设置MMI指令的界面。然后再去“设置->通话设置->来电转接”,看看 “占线时转接” 设置好的...

55190
来自专栏向治洪

android来电归属地提醒

现在市面上常用的一些拨号软件的一个功能,来电归属地。拨号的时候,会在拨号界面出现一个号码归属地的小框框。效果如下:而且这个小窗体还可以自定义风格,并且可以自由移...

26370
来自专栏极客猴

聊聊Activity那些事

Activity身为四大组件之一,在整个App中扮演着向用户呈现界面的角色。在平常的开发中,我们会自定义一个类去继承Activity去实现界面。而Activit...

8620
来自专栏Android中高级开发

Android开发之漫漫长途 XV——RecyclerView

该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索...

19420
来自专栏Android 开发学习

PagerAdapter闪屏坑的修复

32620
来自专栏向治洪

仿淘宝收货地址,本地数据库

谁说咱们攻城狮不能写出既幽默又能懂的博客呢,本人想推出一系列博文,可以给刚接触Android开发的做一个参考,也可以与接触Android已久的各路大神比较一下,...

1.3K70
来自专栏developerHaoz 的安卓之旅

手把手教你从零开始做一个好看的 APP - Day four

本文为 手把手教你从零开始做一个好看的 APP - Day four ,如果想看该系列的其他文章,请点击以下连接

9620
来自专栏java初学

android入门 — ProgressDialog/DatePickerDialog/TimePickerDialog

299110

扫码关注云+社区

领取腾讯云代金券