mediaMuxer = MediaMuxer(mSavedFile!!.absolutePath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
if (mAudioPath != "") {
encodeAudio(mediaMuxer)
VideoEncoder(this, mSavedFile!!, 25, mediaMuxer,audioCodec).start()
} else {
VideoEncoder(this, mSavedFile!!, 25, mediaMuxer,null).start()
}
private fun encodeAudio(mediaMuxer: MediaMuxer) {
audioCodec = AudioEncoder.newInstance()
audioCodec.setEncodeType(MediaFormat.MIMETYPE_AUDIO_AAC)
val file = File("/storage/emulated/0/Download/out1.aac")
if (!file.exists()) {
file.createNewFile()
}
audioCodec.setMuxer(mediaMuxer)
audioCodec.setTotalTime(mTotalTime)
audioCodec.setIOPath(mAudioPath)
audioCodec.prepare()
audioCodec.setOnCompleteListener { audioCodec.release() }
Handler().postDelayed({ audioCodec.startAsync(mAudioPath) }, 1000)
// AudioCodec.getPCMFromAudio(mAudioPath, mAudioPath+".pcm", object : AudioDecodeListener {
// override fun decodeOver() {
// Log.e("xxx","mp3 转成 pcm")
// AudioCodec.PcmToAudio(mAudioPath+".pcm", mAudioPath+".aac", object : AudioDecodeListener {
// override fun decodeOver() {
// Log.e("xxx","pcm 转成 aac")
// Log.e("xxx", " 音频转换完成 ")
// }
//
// override fun decodeFail() {}
// })
// }
//
// override fun decodeFail() {}
// })
}
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import androidx.annotation.RequiresApi;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
public class AudioEncoder {
private static final String TAG = "AudioCodec";
private String encodeType;
private String srcPath;
private MediaCodec mMp3Decoder;
private MediaCodec mAacEncoder;
private MediaExtractor extractor;
private ByteBuffer[] decodeInputBuffers;
private ByteBuffer[] decodeOutputBuffers;
private ByteBuffer[] encodeInputBuffers;
private ByteBuffer[] encodeOutputBuffers;
private MediaCodec.BufferInfo decodeBufferInfo;
private MediaCodec.BufferInfo encodeBufferInfo;
private FileOutputStream fos;
private BufferedOutputStream bos;
private ArrayList chunkPCMDataContainer;//PCM数据块容器
private OnCompleteListener onCompleteListener;
private OnProgressListener onProgressListener;
private long fileTotalSize;
private long decodeSize;
private MediaMuxer muxer;
private int audioTrack;
private boolean codeOver = false;
private long mAudioPts = 0;
private long presentationTimeUs;
ArrayList tempBufferDataList = new ArrayList();
private int sampleRate = 44100;
private int channelCount = 1;
public void setMuxerStart(boolean muxerStart) {
this.muxerStart = muxerStart;
}
private boolean muxerStart = false;
public void setTotalTime(int mTotalTime) {
this.mTotalTime = mTotalTime;
}
private int mTotalTime;
public static AudioEncoder newInstance() {
return new AudioEncoder();
}
/**
* 设置编码器类型
*
* @param encodeType
*/
public void setEncodeType(String encodeType) {
this.encodeType = encodeType;
}
/**
* 设置输入输出文件位置
*
* @param srcPath
*/
public void setIOPath(String srcPath) {
this.srcPath = srcPath;
}
public void setMuxer(MediaMuxer muxer) {
this.muxer = muxer;
}
public void setAudioTrack(int audioTrack) {
this.audioTrack = audioTrack;
}
/**
* 此类已经过封装
* 调用prepare方法 会初始化Decode 、Encode 、输入输出流 等一些列操作
*/
public void prepare() {
if (encodeType == null) {
throw new IllegalArgumentException("encodeType can't be null");
}
if (srcPath == null) {
throw new IllegalArgumentException("srcPath can't be null");
}
chunkPCMDataContainer = new ArrayList<>();
initMediaDecoder();//解码器
}
/**
* 初始化解码器
*/
private void initMediaDecoder() {
try {
extractor = new MediaExtractor();//此类可分离视频文件的音轨和视频轨道
extractor.setDataSource(srcPath);//媒体文件的位置
for (int i = 0; i < extractor.getTrackCount(); i++) {//遍历媒体轨道 此处我们传入的是音频文件,所以也就只有一条轨道
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime != null && mime.startsWith("audio")) {//获取音频轨道
// format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 200 * 1024);
extractor.selectTrack(i);//选择此音频轨道
mMp3Decoder = MediaCodec.createDecoderByType(mime);//创建Decode解码器
mMp3Decoder.configure(format, null, null, 0);
sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
if (mMp3Decoder == null) {
Log.e(TAG, "create mMediaDecoder failed");
return;
}
mMp3Decoder.start();//启动MediaCodec ,等待传入数据
decodeInputBuffers = mMp3Decoder.getInputBuffers();//MediaCodec在此ByteBuffer[]中获取输入数据
decodeOutputBuffers = mMp3Decoder.getOutputBuffers();//MediaCodec将解码后的数据放到此ByteBuffer[]中 我们可以直接在这里面得到PCM数据
decodeBufferInfo = new MediaCodec.BufferInfo();//用于描述解码得到的byte[]数据的相关信息
showLog("decode buffers:" + decodeInputBuffers.length);
//拿到音频的sourceSampleRate之后再初始化编码器
mAacEncoder = initAACMediaEncode();//AAC编码器
if (mAacEncoder == null) {
Log.e(TAG, "create mMediaEncoder failed");
return;
}
mAacEncoder.start();
encodeInputBuffers = mAacEncoder.getInputBuffers();
encodeOutputBuffers = mAacEncoder.getOutputBuffers();
encodeBufferInfo = new MediaCodec.BufferInfo();
new Thread(new EncodeRunnable()).start();
}
/**
* 初始化AAC编码器
*
* @return
*/
private MediaCodec initAACMediaEncode() {
try {
MediaFormat encodeFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, channelCount);//参数对应-> mime type、采样率、声道数
encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, 96000);//比特率
encodeFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
encodeFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 100 * 1024);
MediaCodec mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
mediaCodec.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
return mediaCodec;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 开始转码
* 音频数据{@link #srcPath}先解码成PCM PCM数据在编码成想要得到的{@link #encodeType}音频格式
* mp3->PCM->aac
*/
public void startAsync(String audioPath) {
showLog("start");
new Thread(new DecodeRunnable()).start();
// new Thread(new Runnable() {
// @Override
// public void run() {
// audioToAAC(audioPath, -1, -1);
// }
// }).start();
// final String pcmPath = audioPath + ".pcm";
// final String aacPath = audioPath + ".aac";
//
// AudioCodec.getPCMFromAudio(audioPath, pcmPath, new AudioCodec.AudioDecodeListener() {
// @Override
// public void decodeOver() {
// Log.e(TAG, "音频解码完成" + pcmPath);
// AudioCodec.PcmToAudio(pcmPath, aacPath, new AudioCodec.AudioDecodeListener() {
// @Override
// public void decodeOver() {
// Log.e(TAG, "音频编码完成");
// }
//
// @Override
// public void decodeFail() {
// Log.e(TAG, "音频编码失败");
// }
// });
// }
//
// @Override
// public void decodeFail() {
// Log.e(TAG, "音频解码失败");
// }
// });
}
//尝试将mp3保存成aac,看能不能正常播放 https://blog.csdn.net/qq_29364417/article/details/87989965
public String audioToAAC(String audioPath, long audioStartTimeUs, long audioEndTimeUs) {
long a = System.currentTimeMillis();
int audioExtractorTrackIndex = -1;
int audioMuxerTrackIndex = -1;
int channelCount = 1;
int sourceSampleRate = 0;
int bitRate = 0;
String newAudioAAc = "";
long sourceDuration = 0;
try {
File tempFlie = new File(audioPath);
String tempName = tempFlie.getName();
String suffix = tempName.substring(tempName.lastIndexOf(".") + 1);
// newAudioAAc= tempFlie.getParentFile().getAbsolutePath() + "/" + tempName.replace(suffix, "aac");
newAudioAAc = audioPath + ".aac";
// muxer = new MediaMuxer(newAudioAAc, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
//音频信息获取
MediaExtractor audioExtractor = new MediaExtractor();
audioExtractor.setDataSource(audioPath);
int trackCount = audioExtractor.getTrackCount();
MediaFormat sourceFormat = null;
String sourceMimeType = "";
int timeOutUs = 300;
for (int i = 0; i < trackCount; i++) {
sourceFormat = audioExtractor.getTrackFormat(i);
sourceMimeType = sourceFormat.getString(MediaFormat.KEY_MIME);
channelCount = sourceFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
sourceSampleRate = sourceFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
bitRate = sourceFormat.getInteger(MediaFormat.KEY_BIT_RATE);
Log.e("xxx-sourceSampleRate", sourceSampleRate + "");
Log.e("xxx-bitRate", bitRate + "");
sourceDuration = sourceFormat.getLong(MediaFormat.KEY_DURATION);
if (sourceMimeType.startsWith("audio/")) { //找到音轨
audioExtractorTrackIndex = i;
break;
}
}
//初始化解码器
MediaCodec audioDecoder = null;
audioDecoder = MediaCodec.createDecoderByType(sourceMimeType);
audioDecoder.configure(sourceFormat, null, null, 0);
audioDecoder.start();
//初始化编码
MediaCodec mEncorder = null;
mEncorder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sourceSampleRate, channelCount);
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
Log.e("xxx-sourceSampleRate", sourceSampleRate + "");
Log.e("xxx-bitRate", bitRate + "");
format.setInteger(MediaFormat.KEY_BIT_RATE, 96000); //重新编码的比特率和原音频一样
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1024 * 1024 * 10);
mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mEncorder.start();
//audioExtractor.selectTrack(audioExtractorTrackIndex);
encodeInputBuffers = mEncorder.getInputBuffers();
encodeOutputBuffers = mEncorder.getOutputBuffers();
encodeBufferInfo = new MediaCodec.BufferInfo();
MediaCodec.BufferInfo sourceAudioBufferInfo = new MediaCodec.BufferInfo();
MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();
ByteBuffer audioByteBuffer = ByteBuffer.allocate(1024 * 1024 * 10);
FileOutputStream mFilePcm = new FileOutputStream(audioPath+".pcm");
FileOutputStream mFileStream = new FileOutputStream(newAudioAAc);
while (true) {
int readSampleSize = audioExtractor.readSampleData(audioByteBuffer, 0);
if (readSampleSize < 0) {
audioExtractor.unselectTrack(audioExtractorTrackIndex);
break;
}
long audioSampleTime = audioExtractor.getSampleTime();
Log.e("xxx-audioSampleTime",audioSampleTime+"");
//可以做进度回调
if (audioStartTimeUs != -1 && audioSampleTime < audioStartTimeUs) {
audioExtractor.advance();
continue;
}
if (audioEndTimeUs != -1 && audioSampleTime > audioEndTimeUs) {
break;
}
int audioSampleFlags = audioExtractor.getSampleFlags();
//解码
int sourceInputBufferIndex = audioDecoder.dequeueInputBuffer(timeOutUs);
if (sourceInputBufferIndex >= 0) {
ByteBuffer sourceInputBuffer = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
sourceInputBuffer = audioDecoder.getInputBuffer(sourceInputBufferIndex);
}
sourceInputBuffer.clear();
sourceInputBuffer.put(audioByteBuffer);
audioDecoder.queueInputBuffer(sourceInputBufferIndex, 0, readSampleSize, audioSampleTime, audioSampleFlags);
}
int sourceOutputBufferIndex = audioDecoder.dequeueOutputBuffer(sourceAudioBufferInfo, timeOutUs);
if (sourceOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// 后续输出格式变化
}
while (sourceOutputBufferIndex >= 0) {
ByteBuffer decoderOutputBuffer = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
decoderOutputBuffer = audioDecoder.getOutputBuffer(sourceOutputBufferIndex);
}
//编码
int inputBufferIndex = mEncorder.dequeueInputBuffer(timeOutUs);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
inputBuffer = mEncorder.getInputBuffer(inputBufferIndex);
}
inputBuffer.clear();
inputBuffer.put(decoderOutputBuffer);
mEncorder.queueInputBuffer(inputBufferIndex, 0, decoderOutputBuffer.limit(), audioSampleTime, audioSampleFlags);
}
int outputBufferIndex = mEncorder.dequeueOutputBuffer(audioBufferInfo, timeOutUs);
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// 后续输出格式变化
}
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
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(audioBufferInfo, timeOutUs);
}
audioDecoder.releaseOutputBuffer(sourceOutputBufferIndex, false);
sourceOutputBufferIndex = audioDecoder.dequeueOutputBuffer(sourceAudioBufferInfo, timeOutUs);
}
audioExtractor.advance();
}
//释放资源
mEncorder.stop();
mFileStream.flush();
mFileStream.close();
long b = System.currentTimeMillis() - a;
Log.i(TAG, "编码结束==" + b);
} catch (Exception e) {
e.printStackTrace();
}
return newAudioAAc;
}
/**
* 将PCM数据存入{@link #chunkPCMDataContainer}
*
* @param pcmChunk PCM数据块
*/
private void putPCMData(byte[] pcmChunk) {
synchronized (AudioEncoder.class) {//记得加锁
chunkPCMDataContainer.add(pcmChunk);
}
}
/**
* 在Container中{@link #chunkPCMDataContainer}取出PCM数据
*
* @return PCM数据块
*/
private byte[] getPCMData() {
synchronized (AudioEncoder.class) {//记得加锁
showLog("getPCM:" + chunkPCMDataContainer.size());
if (chunkPCMDataContainer.isEmpty()) {
return null;
}
byte[] pcmChunk = chunkPCMDataContainer.get(0);//每次取出index 0 的数据
chunkPCMDataContainer.remove(pcmChunk);//取出后将此数据remove掉 既能保证PCM数据块的取出顺序 又能及时释放内存
return pcmChunk;
}
}
/**
* 解码{@link #srcPath}音频文件 得到PCM数据块
*
* @return 是否解码完所有数据
*/
private void mp3ToPCM() throws IOException {
fos = new FileOutputStream( srcPath+".pcm");
for (int i = 0; i < decodeInputBuffers.length - 1; i++) {
int inputIndex = mMp3Decoder.dequeueInputBuffer(-1);//获取可用的inputBuffer -1代表一直等待,0表示不等待 建议-1,避免丢帧
if (inputIndex < 0) {
codeOver = true;
return;
}
ByteBuffer inputBuffer = decodeInputBuffers[inputIndex];//拿到inputBuffer
inputBuffer.clear();//清空之前传入inputBuffer内的数据
int sampleSize = extractor.readSampleData(inputBuffer, 0);//MediaExtractor读取数据到inputBuffer中
long timestampTemp = extractor.getSampleTime();
if (sampleSize < 0 || timestampTemp > 1000 * mTotalTime) {//小于0 代表所有数据已读取完成
codeOver = true;
} else {
mMp3Decoder.queueInputBuffer(inputIndex, 0, sampleSize, 0, 0);//通知MediaDecode解码刚刚传入的数据
extractor.advance();//MediaExtractor移动到下一取样处
decodeSize += sampleSize;
}
}
//获取解码得到的byte[]数据 参数BufferInfo上面已介绍 10000同样为等待时间 同上-1代表一直等待,0代表不等待。此处单位为微秒
//此处建议不要填-1 有些时候并没有数据输出,那么他就会一直卡在这 等待
int outputIndex = mMp3Decoder.dequeueOutputBuffer(decodeBufferInfo, 10000);
// showLog("decodeOutIndex:" + outputIndex);
ByteBuffer outputBuffer;
byte[] chunkPCM;
while (outputIndex >= 0) {//每次解码完成的数据不一定能一次吐出 所以用while循环,保证解码器吐出所有数据
outputBuffer = decodeOutputBuffers[outputIndex];//拿到用于存放PCM数据的Buffer
chunkPCM = new byte[decodeBufferInfo.size];//BufferInfo内定义了此数据块的大小
outputBuffer.get(chunkPCM);//将Buffer内的数据取出到字节数组中
outputBuffer.clear();//数据取出后一定记得清空此Buffer MediaCodec是循环使用这些Buffer的,不清空下次会得到同样的数据
//保存到文件中
fos.write(chunkPCM);
fos.flush();
putPCMData(chunkPCM);//自己定义的方法,供编码器所在的线程获取数据,下面会贴出代码
mMp3Decoder.releaseOutputBuffer(outputIndex, false);//此操作一定要做,不然MediaCodec用完所有的Buffer后 将不能向外输出数据
outputIndex = mMp3Decoder.dequeueOutputBuffer(decodeBufferInfo, 10000);//再次获取数据,如果没有数据输出则outputIndex=-1 循环结束
}
fos.close();
}
/**
* 编码PCM数据 得到{@link #encodeType}格式的音频文件,并保存到{@link }
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void pcmToAac() {
int inputIndex;
ByteBuffer inputBuffer;
int outputIndex;
ByteBuffer outputBuffer;
byte[] chunkAudio;
int outBitSize;
int outPacketSize;
byte[] chunkPCM;
// showLog("doEncode");
for (int i = 0; i < encodeInputBuffers.length - 1; i++) {
chunkPCM = getPCMData();//获取解码器所在线程输出的数据 代码后边会贴上
if (chunkPCM == null) {
break;
}
inputIndex = mAacEncoder.dequeueInputBuffer(-1);//同解码器
inputBuffer = encodeInputBuffers[inputIndex];//同解码器
inputBuffer.clear();//同解码器
inputBuffer.limit(chunkPCM.length);
inputBuffer.put(chunkPCM);//PCM数据填充给inputBuffer
//***************问题在这儿,之前这里写死成441000,导致播放时长变短,音频声音变急促***************/
presentationTimeUs += (long) (1.0 * chunkPCM.length / ( sampleRate * channelCount * (16 / 8)) * 1000000.0);
mAacEncoder.queueInputBuffer(inputIndex, 0, chunkPCM.length, presentationTimeUs, 0);//通知编码器 编码
}
outputIndex = mAacEncoder.dequeueOutputBuffer(encodeBufferInfo, 10000);//同解码器
if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
audioTrack = muxer.addTrack(mAacEncoder.getOutputFormat());
} else {
while (outputIndex >= 0) {//同解码器
outBitSize = encodeBufferInfo.size;
outPacketSize = outBitSize + 7;//7为ADTS头部的大小
outputBuffer = encodeOutputBuffers[outputIndex];//拿到输出Buffer
outputBuffer.position(encodeBufferInfo.offset);
outputBuffer.limit(encodeBufferInfo.offset + outBitSize);
chunkAudio = new byte[outPacketSize];
addADTStoPacket(chunkAudio, outPacketSize);//添加ADTS 代码后面会贴上
outputBuffer.get(chunkAudio, 7, outBitSize);//将编码得到的AAC数据 取出到byte[]中 偏移量offset=7 你懂得
outputBuffer.position(encodeBufferInfo.offset);
while (!muxerStart) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
//将数据写到混合器的audioTrack轨道中去
muxer.writeSampleData(audioTrack, outputBuffer, encodeBufferInfo);
mAacEncoder.releaseOutputBuffer(outputIndex, false);
outputIndex = mAacEncoder.dequeueOutputBuffer(encodeBufferInfo, 10000);
}
}
}
public static ByteBuffer clone(ByteBuffer original) {
ByteBuffer clone = ByteBuffer.allocate(original.capacity());
original.rewind();//copy from the beginning
clone.put(original);
original.rewind();
clone.flip();
return clone;
}
/**
* 添加ADTS头
*
* @param packet
* @param packetLen
*/
private void addADTStoPacket(byte[] packet, int packetLen) {
int profile = 2; // AAC LC
int freqIdx = 4; // 44.1KHz
int chanCfg = 2; // CPE
// fill in ADTS data
packet[0] = (byte) 0xFF;
packet[1] = (byte) 0xF9;
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() {
try {
if (bos != null) {
bos.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
bos = null;
}
}
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
fos = null;
}
if (mAacEncoder != null) {
mAacEncoder.stop();
mAacEncoder.release();
mAacEncoder = null;
}
if (mMp3Decoder != null) {
mMp3Decoder.stop();
mMp3Decoder.release();
mMp3Decoder = null;
}
if (extractor != null) {
extractor.release();
extractor = null;
}
if (onCompleteListener != null) {
onCompleteListener = null;
}
if (onProgressListener != null) {
onProgressListener = null;
}
showLog("release");
}
/**
* 解码线程
*/
private class DecodeRunnable implements Runnable {
@Override
public void run() {
while (!codeOver) {
try {
mp3ToPCM();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 编码线程
*/
private class EncodeRunnable implements Runnable {
@Override
public void run() {
long t = System.currentTimeMillis();
while (!codeOver || !chunkPCMDataContainer.isEmpty()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
pcmToAac();
}
}
if (onCompleteListener != null) {
onCompleteListener.completed();
}
showLog("size:" + fileTotalSize + " decodeSize:" + decodeSize + "time:" + (System.currentTimeMillis() - t));
}
}
/**
* 转码完成回调接口
*/
public interface OnCompleteListener {
void completed();
}
/**
* 转码进度监听器
*/
public interface OnProgressListener {
void progress();
}
class TempBufferData {
public TempBufferData(ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo) {
this.byteBuffer = byteBuffer;
this.bufferInfo = bufferInfo;
}
ByteBuffer byteBuffer;
MediaCodec.BufferInfo bufferInfo;
}
/**
* 设置转码完成监听器
*
* @param onCompleteListener
*/
public void setOnCompleteListener(OnCompleteListener onCompleteListener) {
this.onCompleteListener = onCompleteListener;
}
public void setOnProgressListener(OnProgressListener onProgressListener) {
this.onProgressListener = onProgressListener;
}
private void showLog(String msg) {
// Log.d("AudioCodec", msg);
}
}
/**
* 解码线程
*/
private class DecodeRunnable implements Runnable {
@Override
public void run() {
while (!codeOver) {
try {
mp3ToPCM();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 编码线程
*/
private class EncodeRunnable implements Runnable {
@Override
public void run() {
long t = System.currentTimeMillis();
while (!codeOver || !chunkPCMDataContainer.isEmpty()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
pcmToAac();
}
}
if (onCompleteListener != null) {
onCompleteListener.completed();
}
showLog("size:" + fileTotalSize + " decodeSize:" + decodeSize + "time:" + (System.currentTimeMillis() - t));
}
}
/**
* 解码{@link #srcPath}音频文件 得到PCM数据块
*
* @return 是否解码完所有数据
*/
private void mp3ToPCM() throws IOException {
fos = new FileOutputStream( srcPath+".pcm");
for (int i = 0; i < decodeInputBuffers.length - 1; i++) {
int inputIndex = mMp3Decoder.dequeueInputBuffer(-1);//获取可用的inputBuffer -1代表一直等待,0表示不等待 建议-1,避免丢帧
if (inputIndex < 0) {
codeOver = true;
return;
}
ByteBuffer inputBuffer = decodeInputBuffers[inputIndex];//拿到inputBuffer
inputBuffer.clear();//清空之前传入inputBuffer内的数据
int sampleSize = extractor.readSampleData(inputBuffer, 0);//MediaExtractor读取数据到inputBuffer中
long timestampTemp = extractor.getSampleTime();
if (sampleSize < 0 || timestampTemp > 1000 * mTotalTime) {//小于0 代表所有数据已读取完成
codeOver = true;
} else {
mMp3Decoder.queueInputBuffer(inputIndex, 0, sampleSize, 0, 0);//通知MediaDecode解码刚刚传入的数据
extractor.advance();//MediaExtractor移动到下一取样处
decodeSize += sampleSize;
}
}
//获取解码得到的byte[]数据 参数BufferInfo上面已介绍 10000同样为等待时间 同上-1代表一直等待,0代表不等待。此处单位为微秒
//此处建议不要填-1 有些时候并没有数据输出,那么他就会一直卡在这 等待
int outputIndex = mMp3Decoder.dequeueOutputBuffer(decodeBufferInfo, 10000);
// showLog("decodeOutIndex:" + outputIndex);
ByteBuffer outputBuffer;
byte[] chunkPCM;
while (outputIndex >= 0) {//每次解码完成的数据不一定能一次吐出 所以用while循环,保证解码器吐出所有数据
outputBuffer = decodeOutputBuffers[outputIndex];//拿到用于存放PCM数据的Buffer
chunkPCM = new byte[decodeBufferInfo.size];//BufferInfo内定义了此数据块的大小
outputBuffer.get(chunkPCM);//将Buffer内的数据取出到字节数组中
outputBuffer.clear();//数据取出后一定记得清空此Buffer MediaCodec是循环使用这些Buffer的,不清空下次会得到同样的数据
//保存到文件中
fos.write(chunkPCM);
fos.flush();
putPCMData(chunkPCM);//自己定义的方法,供编码器所在的线程获取数据,下面会贴出代码
mMp3Decoder.releaseOutputBuffer(outputIndex, false);//此操作一定要做,不然MediaCodec用完所有的Buffer后 将不能向外输出数据
outputIndex = mMp3Decoder.dequeueOutputBuffer(decodeBufferInfo, 10000);//再次获取数据,如果没有数据输出则outputIndex=-1 循环结束
}
fos.close();
}
/**
* 编码PCM数据 得到{@link #encodeType}格式的音频文件,并保存到{@link }
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void pcmToAac() {
int inputIndex;
ByteBuffer inputBuffer;
int outputIndex;
ByteBuffer outputBuffer;
byte[] chunkAudio;
int outBitSize;
int outPacketSize;
byte[] chunkPCM;
// showLog("doEncode");
for (int i = 0; i < encodeInputBuffers.length - 1; i++) {
chunkPCM = getPCMData();//获取解码器所在线程输出的数据 代码后边会贴上
if (chunkPCM == null) {
break;
}
inputIndex = mAacEncoder.dequeueInputBuffer(-1);//同解码器
inputBuffer = encodeInputBuffers[inputIndex];//同解码器
inputBuffer.clear();//同解码器
inputBuffer.limit(chunkPCM.length);
inputBuffer.put(chunkPCM);//PCM数据填充给inputBuffer
//***************问题在这儿,之前这里写死成441000,导致播放时长变短,音频声音变急促***************/
presentationTimeUs += (long) (1.0 * chunkPCM.length / ( sampleRate * channelCount * (16 / 8)) * 1000000.0);
mAacEncoder.queueInputBuffer(inputIndex, 0, chunkPCM.length, presentationTimeUs, 0);//通知编码器 编码
}
outputIndex = mAacEncoder.dequeueOutputBuffer(encodeBufferInfo, 10000);//同解码器
if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
audioTrack = muxer.addTrack(mAacEncoder.getOutputFormat());
} else {
while (outputIndex >= 0) {//同解码器
outBitSize = encodeBufferInfo.size;
outPacketSize = outBitSize + 7;//7为ADTS头部的大小
outputBuffer = encodeOutputBuffers[outputIndex];//拿到输出Buffer
outputBuffer.position(encodeBufferInfo.offset);
outputBuffer.limit(encodeBufferInfo.offset + outBitSize);
chunkAudio = new byte[outPacketSize];
addADTStoPacket(chunkAudio, outPacketSize);//添加ADTS 代码后面会贴上
outputBuffer.get(chunkAudio, 7, outBitSize);//将编码得到的AAC数据 取出到byte[]中 偏移量offset=7 你懂得
outputBuffer.position(encodeBufferInfo.offset);
while (!muxerStart) {
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
//将数据写到混合器的audioTrack轨道中去
muxer.writeSampleData(audioTrack, outputBuffer, encodeBufferInfo);
mAacEncoder.releaseOutputBuffer(outputIndex, false);
outputIndex = mAacEncoder.dequeueOutputBuffer(encodeBufferInfo, 10000);
}
}
}