对于音频最重要的是采样率 采样大小(位深) 声道数
音频相关的文章
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地址: