一. 编码器 MediaCodec
MediaCodec 是 Android 提供的用于对音频进行编解码的类,属于硬编解。MediaCodec 在编解码的过程中使用了一组缓冲区来处理数据。如下图所示:
基本使用流程如下:
// 1 创建编解码器
MediaCodec.createByCodecName() // createEncoderByType , createDecoderByType
// 2 配置编解码器
configure(@Nullable MediaFormat format, @Nullable Surface surface, @Nullable MediaCrypto crypto, int flags)
// 3 开始编解码
start
// 4 循环处理数据
while(true) {
dequeueInputBuffer// 获取可用的输入缓存区 buffer 的下标 inputIndex
getInputBuffers// 根据 inputIndex 获取可用的输入缓冲区 bytebuffer
bytebuffer.put // 放入数据
queueInputBuffer // 将数据放入输入缓冲区
dequeueOutputBuffer // 获取可用的输出缓存区 buffer 的下标 outputIndex
getOutPutBuffers // 根据 outputIndex 获取可用的输出缓冲区 bytebuffer
outputBuffer.get() // 获取数据
releaseOutputBuffer // 处理完成,释放 buffer
}
// 5 终止
stop
// 6 释放编码器使用的资源
release
二. MediaExtractor 媒体数据提取器
通过 MediaExtractor 可以将媒体文件的视频和音频数据分离,也可以获取对应的音频格式或者视频格式。主要 API 如下:
- setDataSource : 设置数据源
- getTrackCount:获取文件的通道数,音频通道和视频通道
- getTrackFormat : 获取指定通道的格式,比如音频的格式或者是视频的格式
- getSampleTime: 获取当前帧的时间戳
- readSampleData :将当前帧数据写入 byteBuffer
- advance : 读取下一帧
- release : 释放资源
基本使用流程如下:
// 1 设置数据源
setDataSource
// 2 获取对应的视频或者音频格式
getTrackFormat
// 3 定位到某条轨道
selectTrack
// 4 读取数据
while(true) {
readSampleData
advance
}
// 5 释放
release
三. 编码保存
特别指出,由于 aac 的格式问题,如果保存到本地需要对每一帧添加 ADTS ,见 addADTStoPacket 方法
public class AacAudioRecord {
private static final String TAG = "AacAudioRecord";
private AudioRecord mAudioRecord;
private MediaCodec mAudioEncoder;
private volatile boolean mIsRecording = false;
private ExecutorService mExecutorService;
private int mAudioSource;
private int mSampleRateInHz;
private int mChannelConfig;
private int mAudioFormat;
private int mBufferSizeInBytes;
private MediaFormat mMediaFormat;
private String mFilePath;
private File mFile;
private FileOutputStream mFileOutputStream;
private BufferedOutputStream mBufferedOutputStream;
private BlockingQueue<byte[]> mDataQueue;
public AacAudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes) {
mAudioSource = audioSource;
mSampleRateInHz = sampleRateInHz;
mChannelConfig = channelConfig