Android pcm编码为aac

对于音频最重要的是采样率  采样大小(位深) 声道数

音频相关的文章

https://blog.csdn.net/qq_15255121/article/details/115168456

https://blog.csdn.net/qq_15255121/article/details/115348454

android相关的API

MediaCodec.java
 /**
     * Instantiate the preferred encoder supporting output data of the given mime type.
     *
     * <strong>Note:</strong> It is preferred to use {@link MediaCodecList#findEncoderForFormat}
     * and {@link #createByCodecName} to ensure that the resulting codec can handle a
     * given format.
     *
     * @param type The desired mime type of the output data.
     * @throws IOException if the codec cannot be created.
     * @throws IllegalArgumentException if type is not a valid mime type.
     * @throws NullPointerException if type is null.
     */
    @NonNull
    public static MediaCodec createEncoderByType(@NonNull String type)
            throws IOException {
        return new MediaCodec(type, true /* nameIsType */, true /* encoder */);
    }

这个api用来创建编码器

type为我们需要的编码器 

MediaFormat.MIMETYPE_AUDIO_AAC
    public static final String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";

创建encode后我们需要对encode进行配置

MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC,
                mSampleRate,
                mChannelCount);
        format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC);
        format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
        format.setInteger(MediaFormat.KEY_BIT_RATE, 100000);
        format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, mBufferSize);

        mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

MediaFormat.KEY_AAC_PROFILE 我们选择MediaCodecInfo.CodecProfileLevel.AACObjectLC,

通过下图

我么知道

AAC LC 码率为128K 

AAC HE V1 64K 

AAC HE V2  32K 

我们这里选择LC ,码率设置为100K

Encode的创建代码

public void init(Context context, int sampleRate,
                     int channelCount, int sampleFormat,
                     String srcPath, String dstPath,
                     IHanlderCallback callback) {

        mSampleRate = sampleRate;
        mChannelCount = channelCount;
        mSampleFormat = sampleFormat;
        mSrcFilePath = srcPath;
        mDstFilePath = dstPath;
        mExecutorService = Executors.newCachedThreadPool();
        mCallback = callback;
        mBufferSize = AudioRecord.getMinBufferSize(mSampleRate,
                mInChannel, mSampleFormat);
        Log.e("yuanBuffer", "buffesize=" + mBufferSize + " nbsample=" + mBufferSize / 4);
        try {
            mEncorder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
        } catch (Exception e) {
            e.printStackTrace();
        }
        MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC,
                mSampleRate,
                mChannelCount);
        format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC);
        format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
        format.setInteger(MediaFormat.KEY_BIT_RATE, 100000);
        format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, mBufferSize);

        mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    }

编码和解码一样都是给MediaCodec的inputBuffer输入数据,通过outbutter来读取编码后的数据。

编码后的aac文件我们需要加ADTS头

ADTS相关信息查看

https://wiki.multimedia.cx/index.php?title=ADTS

/**
     * Add ADTS header at the beginning of each and every AAC packet.
     * This is needed as MediaCodec encoder generates a packet of raw
     * AAC data.
     * <p>
     * Note the packetLen must count in the ADTS header itself !!! .
     * 注意,这里的packetLen参数为raw aac Packet Len + 7; 7 bytes adts header
     **/
    private void addADTStoPacket(byte[] packet, int packetLen) {
        int profile = 2;  //AAC LC,MediaCodecInfo.CodecProfileLevel.AACObjectLC;
        int freqIdx = 4;  //见后面注释avpriv_mpeg4audio_sample_rates中32000对应的数组下标,来自ffmpeg源码
        int chanCfg = 1;  //见后面注释channel_configuration,AudioFormat.CHANNEL_IN_MONO 单声道(声道数量)

        /*int avpriv_mpeg4audio_sample_rates[] = {96000, 88200, 64000, 48000, 44100, 32000,24000, 22050, 16000, 12000, 11025, 8000, 7350};
        channel_configuration: 表示声道数chanCfg
        0: Defined in AOT Specifc Config
        1: 1 channel: front-center
        2: 2 channels: front-left, front-right
        3: 3 channels: front-center, front-left, front-right
        4: 4 channels: front-center, front-left, front-right, back-center
        5: 5 channels: front-center, front-left, front-right, back-left, back-right
        6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
        7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
        8-15: Reserved
        */

        // fill in ADTS data
        packet[0] = (byte) 0xFF;
        //packet[1] = (byte)0xF9;
        packet[1] = (byte) 0xF1;//解决ios 不能播放问题
        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;
    }

完整代码

package com.yuanxuzhen.androidmedia.encode;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.yuanxuzhen.androidmedia.IHanlderCallback;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AacEncoder {
    private int mSampleRate = 44100;
    private int mInChannel = AudioFormat.CHANNEL_IN_STEREO;
    private int mSampleFormat = AudioFormat.ENCODING_PCM_16BIT;
    private int mChannelCount = 2;
    private String mDstFilePath = null;
    private String mSrcFilePath = null;
    MediaCodec mEncorder;
    ExecutorService mExecutorService;
    private int mBufferSize;
    private IHanlderCallback mCallback;
    private boolean isEncodeing = false;


    public void init(Context context, int sampleRate,
                     int channelCount, int sampleFormat,
                     String srcPath, String dstPath,
                     IHanlderCallback callback) {

        mSampleRate = sampleRate;
        mChannelCount = channelCount;
        mSampleFormat = sampleFormat;
        mSrcFilePath = srcPath;
        mDstFilePath = dstPath;
        mExecutorService = Executors.newCachedThreadPool();
        mCallback = callback;
        mBufferSize = AudioRecord.getMinBufferSize(mSampleRate,
                mInChannel, mSampleFormat);
        Log.e("yuanBuffer", "buffesize=" + mBufferSize + " nbsample=" + mBufferSize / 4);
        try {
            mEncorder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
        } catch (Exception e) {
            e.printStackTrace();
        }
        MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC,
                mSampleRate,
                mChannelCount);
        format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AAC);
        format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
        format.setInteger(MediaFormat.KEY_BIT_RATE, 100000);
        format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, mBufferSize);

        mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    }
    public void startEncording() {
        mExecutorService.execute(new Runnable() {
            @Override
            public void run() {
                startEncordingV1();
            }
        });
    }

    /**
     * 开始编码
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void startEncordingV1() {
        if (mEncorder == null) {
            isEncodeing = false;
            if (mCallback != null) {
                mCallback.onFail();
            }
            return;
        }
        isEncodeing = true;
        mEncorder.start();
        try {
            FileInputStream inputStream = new FileInputStream(mSrcFilePath);
            FileOutputStream mFileStream = new FileOutputStream(mDstFilePath);
            MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
            while (true) {
                long a = System.currentTimeMillis();
                // 从队列中取出录音的一帧音频数据
                byte[] byteArray = new byte[mBufferSize/2];
                int readSize = inputStream.read(byteArray);
                if (readSize <= 0) {
                    break;
                }
                ByteBuffer buf = ByteBuffer.wrap(byteArray);
                // 取出InputBuffer,填充音频数据,然后输送到编码器进行编码
                int inputBufferIndex = mEncorder.dequeueInputBuffer(0);
                if (inputBufferIndex >= 0) {
                    ByteBuffer inputBuffer = mEncorder.getInputBuffer(inputBufferIndex);
                    inputBuffer.clear();
                    inputBuffer.put(buf);
                    mEncorder.queueInputBuffer(inputBufferIndex, 0, readSize, System.nanoTime(), 0);
                }

                // 取出编码好的一帧音频数据,然后给这一帧添加ADTS头
                int outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
                while (outputBufferIndex >= 0) {
                    ByteBuffer outputBuffer = mEncorder.getOutputBuffer(outputBufferIndex);
                    int outBufferSize = outputBuffer.limit() + 7;
                    byte[] aacBytes = new byte[outBufferSize];
                    addADTStoPacket(aacBytes, outBufferSize);
                    outputBuffer.get(aacBytes, 7, outputBuffer.limit());
                    mFileStream.write(aacBytes);

                    mEncorder.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
                }

                long b = System.currentTimeMillis() - a;
                Log.i("AudioEncode", "编码耗时-毫秒==" + b);
            }
            if (mFileStream != null) {
                try {
                    mFileStream.flush();
                    mFileStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (inputStream != null) {
                try {
                    mFileStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (mEncorder != null) {
                mEncorder.stop();
            }
            if (mCallback != null) {
                mCallback.onSuccess();
            }
        } catch (FileNotFoundException e) {
            if (mCallback != null) {
                mCallback.onFail();
            }
            e.printStackTrace();
        } catch (IOException e) {
            if (mCallback != null) {
                mCallback.onFail();
            }
            e.printStackTrace();
        } finally {
            isEncodeing = false;
        }
    }


    /**
     * Add ADTS header at the beginning of each and every AAC packet.
     * This is needed as MediaCodec encoder generates a packet of raw
     * AAC data.
     * <p>
     * Note the packetLen must count in the ADTS header itself !!! .
     * 注意,这里的packetLen参数为raw aac Packet Len + 7; 7 bytes adts header
     **/
    private void addADTStoPacket(byte[] packet, int packetLen) {
        int profile = 2;  //AAC LC,MediaCodecInfo.CodecProfileLevel.AACObjectLC;
        int freqIdx = 4;  //见后面注释avpriv_mpeg4audio_sample_rates中32000对应的数组下标,来自ffmpeg源码
        int chanCfg = 1;  //见后面注释channel_configuration,AudioFormat.CHANNEL_IN_MONO 单声道(声道数量)

        /*int avpriv_mpeg4audio_sample_rates[] = {96000, 88200, 64000, 48000, 44100, 32000,24000, 22050, 16000, 12000, 11025, 8000, 7350};
        channel_configuration: 表示声道数chanCfg
        0: Defined in AOT Specifc Config
        1: 1 channel: front-center
        2: 2 channels: front-left, front-right
        3: 3 channels: front-center, front-left, front-right
        4: 4 channels: front-center, front-left, front-right, back-center
        5: 5 channels: front-center, front-left, front-right, back-left, back-right
        6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
        7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
        8-15: Reserved
        */

        // fill in ADTS data
        packet[0] = (byte) 0xFF;
        //packet[1] = (byte)0xF9;
        packet[1] = (byte) 0xF1;//解决ios 不能播放问题
        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;
    }

    public void release() {
        if (mEncorder == null) {
            return;
        }
        mEncorder.release();
        mEncorder = null;
        isEncodeing = false;
    }

    public boolean isEncodeing() {
        return isEncodeing;
    }
}

gitee地址:

https://gitee.com/creat151/android-media.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值