前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MediaCodec硬编码pcm2aac

MediaCodec硬编码pcm2aac

作者头像
曾大稳
发布2018-09-11 10:32:22
1.4K0
发布2018-09-11 10:32:22
举报
文章被收录于专栏:曾大稳的博客

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

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

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

代码语言:javascript
复制


//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;
           }
       }
   }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档