android 使用MediaCodec(根据设备状况硬编解码)来转码音频(MP3 to aac),并同时裁剪音频
来源:互联网 发布:蚁群算法 旅行商问题 编辑:程序博客网 时间:2024/05/16 10:04
第一次写这样的博文,写得不好,望大家及时指出博文中的错误,以便于相互学习。
本来主要介绍如何使用mediacodec来转码音频,同时实现音频的裁剪。对于mediacodec相信大家都不陌生,没做过也听过。
转码音频需要的知识点
1、音频基础知识,什么是pcm,什么是音频格式(MP3、aac等),这里给大家推荐一篇博文,http://blog.csdn.net/kevindgk/article/details/52924779。
2、mediacodec的基础知识和api调用,mediacodec是典型的CS模式,也就是客户端-服务器模式,建议大家多看看官网api,
这里给大家推荐一篇中文版的api,MediaCodec中文api
转码流程
解码线程:FILE—解码—PCM队列
编码线程:PCM队列—编码—FILE
系统版本和相关Api类
sdk版本>=16;MediaCodec、MediaExtractor
解码线程
主要流程是通过MediaExtractor读取一帧数据,然后进行解码并放入PCM队列,流程分为prepare—decode—release;
相关代码分别如下:
主要线程里面的流程:
@Override public void run() { TransAacHandlerPure.logMsg("decodec run"); if (listener != null) { listener.onStart(); } boolean isPrepare = false; try { prepare();//初始化 isPrepare = true; } catch (IOException e) { e.printStackTrace(); } TransAacHandlerPure.logMsg("decodec isPrepare " + isPrepare); if (isPrepare) { decode();//解码 } release();//释放资源 if (!isPrepare && listener != null) { listener.onFail(); } isFinish = true; }
prepare里面的代码如下:
private void prepare() throws IOException { extractor = new MediaExtractor(); extractor.setDataSource(srcFile); int numTracks = extractor.getTrackCount(); for (int i = 0; i < numTracks; i++) { MediaFormat format = extractor.getTrackFormat(i); String mine = format.getString(MediaFormat.KEY_MIME); if (!TextUtils.isEmpty(mine) && mine.startsWith("audio")) { extractor.selectTrack(i); try { duration = format.getInteger(MediaFormat.KEY_DURATION) / 1000; } catch (Exception e) { e.printStackTrace(); MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource(srcFile); mediaPlayer.prepare(); duration = mediaPlayer.getDuration(); mediaPlayer.release(); } codec = MediaCodec.createDecoderByType(mine); codec.configure(format, null, null, 0); codec.start(); TransAacHandlerPure.logMsg("New decode codec start:" + format.toString()); break; } }// createFile(outFile + ".pcm", true);//测试 输出pcm格式// mOutput = new DataOutputStream(new FileOutputStream(outFile + ".pcm")); }decode方法如下:
private void decode() { ByteBuffer[] inputBuffers = codec.getInputBuffers(); ByteBuffer[] outputBuffers = codec.getOutputBuffers(); MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); TransAacHandlerPure.logMsg("loopDecode start"); if (rangeStart > 0) {//如果有裁剪,seek到裁剪的地方 extractor.seekTo(rangeStart * 1000, MediaExtractor.SEEK_TO_CLOSEST_SYNC); } boolean isEOS = false; while (true) { long timestamp = 0; if (!isEOS) { int inIndex = codec.dequeueInputBuffer(TIME_OUT); if (inIndex >= 0) { ByteBuffer buffer = inputBuffers[inIndex]; int sampleSize = extractor.readSampleData(buffer, 0); long timestampTemp = extractor.getSampleTime(); timestamp = timestampTemp / 1000; TransAacHandlerPure.logMsg("loopDecode readSampleData end sampleSize " + sampleSize + " buffer.capacity()=" + buffer.capacity()); TransAacHandlerPure.logMsg("loopDecode readSampleData end timestamp" + timestamp); if (rangeEnd > 0 && timestamp > rangeEnd) { sampleSize = -1; } if (sampleSize <= 0) { codec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); isEOS = true; } else { codec.queueInputBuffer(inIndex, 0, sampleSize, timestampTemp, 0); extractor.advance(); } } } int outIndex = codec.dequeueOutputBuffer(info, TIME_OUT);// TransAacHandlerPure.logMsg(" switch (outIndex)"); switch (outIndex) { case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: outputBuffers = codec.getOutputBuffers(); TransAacHandlerPure.logMsg("dequeueOutputBuffer INFO_OUTPUT_BUFFERS_CHANGED!"); break; case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: MediaFormat mf = codec.getOutputFormat(); //开始编码线程 EncodeTask encodeTask = new EncodeTask(outFile, this, listener); int sampleRate = mf.getInteger(MediaFormat.KEY_SAMPLE_RATE); int pcmEncoding = mf.getInteger(MediaFormat.KEY_PCM_ENCODING); int channelCount = mf.getInteger(MediaFormat.KEY_CHANNEL_COUNT); encodeTask.setAudioParams(sampleRate, pcmEncoding, channelCount); new Thread(encodeTask).start(); TransAacHandlerPure.logMsg("New format " + mf.toString()); break; case MediaCodec.INFO_TRY_AGAIN_LATER: TransAacHandlerPure.logMsg("dequeueOutputBuffer timed out!"); break; default: if (last == 0) { last = System.currentTimeMillis(); } long now = System.currentTimeMillis(); TransAacHandlerPure.logMsg("解码时间:" + (now - last) + " info.size " + info.size); last = now; ByteBuffer buffer = outputBuffers[outIndex]; byte[] outData = new byte[info.size]; buffer.get(outData, 0, info.size); codec.releaseOutputBuffer(outIndex, true); try { mOutput.write(outData); } catch (IOException e) { e.printStackTrace(); } pushAvFrame(outData); if (listener != null) { listener.onProgress(rangeEnd > 0 ? (int) rangeEnd : duration, rangeStart > 0 ? (int) (timestamp - rangeStart) : (int) timestamp); } break; } // All decoded frames have been rendered, we can stop playing now if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { TransAacHandlerPure.logMsg("OutputBuffer BUFFER_FLAG_END_OF_STREAM"); break; } } }release相关代码
private void release() { if (extractor != null) { extractor.release(); extractor = null; } if (codec != null) { codec.stop(); codec.release(); codec = null; } }
编码线程
主要流程跟解码线程类似,需要说明的是编码线程开始是在解码返回MediaCodec.INFO_OUTPUT_FORMAT_CHANGED这个状态的时候,因为在这个时候可以拿到编码需要的一些必要参数,如采样率等等,虽然也可以在从MediaExtractor里面拿,但是这样可能有些机型拿不到一些数据,所以我认为在这里拿是比较合适的。闲话不多说,直接看代码:
run方法:
@Override public void run() { boolean isPrepare = false; try { prepare(); isPrepare = true; } catch (IOException e) { e.printStackTrace(); } if (isPrepare && obtain != null) { encode(); } release(); if (listener != null) { if (isPrepare) { listener.onSuccess(); } else { listener.onFail(); } } }prepare方法,关于比特率的设置,大家多参考之前关于音频基础知识的那篇博文,这个直接影响到文件大小和音质
private void prepare() throws IOException { String mime = MediaFormat.MIMETYPE_AUDIO_AAC; encoder = MediaCodec.createEncoderByType(mime); MediaFormat format = MediaFormat.createAudioFormat(mime, sampleRate, channelCount); format.setInteger(MediaFormat.KEY_BIT_RATE, 96000); format.setInteger(MediaFormat.KEY_PCM_ENCODING, pcmEncoding); format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 20 * 1024); format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); logMsg(" New " + format.toString()); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); encoder.start(); createFile(outFile, true); mOutput = new DataOutputStream(new FileOutputStream(outFile)); }
encode方法,要注意aac写入的时候需要加入头部分,关于aac格式介绍,大家参考这篇博文-aac格式介绍
private void encode() { boolean isFinish = false; while (true) { if (!isFinish) { byte[] rawData = obtain.getRawFrame(); if (rawData == null) { if (obtain.isFinish()) { isFinish = true; int inIndex = encoder.dequeueInputBuffer(TIME_OUT); encoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); } else { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } continue; } ByteBuffer[] inputBuffers = encoder.getInputBuffers(); int inIndex = encoder.dequeueInputBuffer(TIME_OUT); if (inIndex >= 0) { ByteBuffer inputBuffer = inputBuffers[inIndex]; inputBuffer.clear(); inputBuffer.put(rawData); encoder.queueInputBuffer(inIndex, 0, rawData.length, System.nanoTime(), 0); } } ByteBuffer[] outputBuffers = encoder.getOutputBuffers(); MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); int outIndex = encoder.dequeueOutputBuffer(info, TIME_OUT); if (outIndex >= 0) { if (last == 0) { last = System.currentTimeMillis(); } long now = System.currentTimeMillis(); TransAacHandlerPure.logMsg("编码码时间:" + (now - last) + " info.size " + info.size); last = now; while (outIndex >= 0) { ByteBuffer outputBuffer = outputBuffers[outIndex]; int len = info.size + 7; byte[] outData = new byte[len]; addADTStoPacket(outData, len); outputBuffer.get(outData, 7, info.size); encoder.releaseOutputBuffer(outIndex, false); try { mOutput.write(outData); } catch (Exception e) { e.printStackTrace(); } catch (Error e) { e.printStackTrace(); } outIndex = encoder.dequeueOutputBuffer(info, TIME_OUT); } } if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { VLog.d("encode OutputBuffer BUFFER_FLAG_END_OF_STREAM"); break; } } }
加入aac头方法
/** * 给编码出的aac裸流添加adts头字段 * * @param packet 要空出前7个字节,否则会搞乱数据 * @param packetLen */ private void addADTStoPacket(byte[] packet, int packetLen) { int profile = 2; //AAC LC int freqIdx = 4; //44.1KHz int chanCfg = 2; //CPE 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; }
release方法
private void release() { if (encoder != null) { encoder.stop(); encoder.release(); encoder = null; } if (mOutput != null) { try { mOutput.flush(); mOutput.close(); } catch (IOException e) { e.printStackTrace(); } mOutput = null; } }
几个需要注意的地方
1、编码线程初始化的地方,获取初始化所需的必要参数2、编解码线程之间的同步,由于解码线程一般比编码线程快,所以pcm的队列数据同步显得比较重要,当然了这个消费者模式的基础,相信大家很容易理解
3、aac文件的保存,需要添加头
纯净版MP3ToAAc(可以同时剪辑)所有代码
import android.media.MediaCodec;import android.media.MediaCodecInfo;import android.media.MediaExtractor;import android.media.MediaFormat;import android.media.MediaPlayer;import android.os.Build;import android.support.annotation.RequiresApi;import android.text.TextUtils;import android.util.Log;import java.io.DataOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import java.nio.ByteBuffer;import java.util.Queue;import java.util.concurrent.LinkedBlockingQueue;/** * 音频转换成为aac编码 * * @author jake * @since 2017/8/3 下午4:28 */@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)public class TransAacHandlerPure { private String srcFile; private String outFile; private long rangeStart = -1; private long rangeEnd = -1; private OnProgressListener listener; public TransAacHandlerPure(String srcFile, String outFile) { this(srcFile, outFile, null); } public TransAacHandlerPure(String srcFile, String outFile, OnProgressListener listener) { this(srcFile, outFile, -1, -1, listener); } public TransAacHandlerPure(String srcFile, String outFile, long rangeStart, long rangeEnd, OnProgressListener listener) { this.srcFile = srcFile; this.outFile = outFile; this.rangeStart = rangeStart; this.rangeEnd = rangeEnd; this.listener = listener; } public void start() { DecodeTask task = new DecodeTask(srcFile, outFile, listener); task.setRangeTime(rangeStart, rangeEnd); new Thread(task).start(); } public void setRangeTime(long rangeStart, long rangeEnd) { this.rangeStart = rangeStart; this.rangeEnd = rangeEnd; } public void setListener(OnProgressListener listener) { this.listener = listener; } private static class DecodeTask implements Runnable, IDataObtain { private static final long TIME_OUT = 5000; private Queue<byte[]> mRawQueue; private MediaExtractor extractor; private boolean isFinish = false; private String srcFile; private MediaCodec codec; private String outFile; private OnProgressListener listener; private long rangeStart; private long rangeEnd; private int duration = 0; private OutputStream mOutput; public void setRangeTime(long rangeStart, long rangeEnd) { this.rangeStart = rangeStart; this.rangeEnd = rangeEnd; } public DecodeTask(String srcFile, String outFile, OnProgressListener listener) { this.srcFile = srcFile; this.outFile = outFile; this.listener = listener; mRawQueue = new LinkedBlockingQueue<>(); } private void pushAvFrame(byte[] frame) { if (frame != null) { int len = mRawQueue.size(); while (len > 10) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } len = mRawQueue.size(); } synchronized (mRawQueue) { mRawQueue.offer(frame); } } } @Override public void run() { TransAacHandlerPure.logMsg("decodec run"); if (listener != null) { listener.onStart(); } boolean isPrepare = false; try { prepare(); isPrepare = true; } catch (IOException e) { e.printStackTrace(); } TransAacHandlerPure.logMsg("decodec isPrepare " + isPrepare); if (isPrepare) { decode(); } release(); if (!isPrepare && listener != null) { listener.onFail(); } isFinish = true; } private void release() { if (extractor != null) { extractor.release(); extractor = null; } if (codec != null) { codec.stop(); codec.release(); codec = null; } } private void prepare() throws IOException { extractor = new MediaExtractor(); extractor.setDataSource(srcFile); int numTracks = extractor.getTrackCount(); for (int i = 0; i < numTracks; i++) { MediaFormat format = extractor.getTrackFormat(i); String mine = format.getString(MediaFormat.KEY_MIME); if (!TextUtils.isEmpty(mine) && mine.startsWith("audio")) { extractor.selectTrack(i); try { duration = format.getInteger(MediaFormat.KEY_DURATION) / 1000; } catch (Exception e) { e.printStackTrace(); MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource(srcFile); mediaPlayer.prepare(); duration = mediaPlayer.getDuration(); mediaPlayer.release(); } codec = MediaCodec.createDecoderByType(mine); codec.configure(format, null, null, 0); codec.start(); TransAacHandlerPure.logMsg("New decode codec start:" + format.toString()); break; } } createFile(outFile + ".pcm", true);//测试 输出pcm格式 mOutput = new DataOutputStream(new FileOutputStream(outFile + ".pcm")); } long last; private void decode() { ByteBuffer[] inputBuffers = codec.getInputBuffers(); ByteBuffer[] outputBuffers = codec.getOutputBuffers(); MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); TransAacHandlerPure.logMsg("loopDecode start"); if (rangeStart > 0) {//如果有裁剪,seek到裁剪的地方 extractor.seekTo(rangeStart * 1000, MediaExtractor.SEEK_TO_CLOSEST_SYNC); } boolean isEOS = false; while (true) { long timestamp = 0; if (!isEOS) { int inIndex = codec.dequeueInputBuffer(TIME_OUT); if (inIndex >= 0) { ByteBuffer buffer = inputBuffers[inIndex]; int sampleSize = extractor.readSampleData(buffer, 0); long timestampTemp = extractor.getSampleTime(); timestamp = timestampTemp / 1000; TransAacHandlerPure.logMsg("loopDecode readSampleData end sampleSize " + sampleSize + " buffer.capacity()=" + buffer.capacity()); TransAacHandlerPure.logMsg("loopDecode readSampleData end timestamp" + timestamp); if (rangeEnd > 0 && timestamp > rangeEnd) { sampleSize = -1; } if (sampleSize <= 0) { codec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); isEOS = true; } else { codec.queueInputBuffer(inIndex, 0, sampleSize, timestampTemp, 0); extractor.advance(); } } } int outIndex = codec.dequeueOutputBuffer(info, TIME_OUT);// TransAacHandlerPure.logMsg(" switch (outIndex)"); switch (outIndex) { case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: outputBuffers = codec.getOutputBuffers(); TransAacHandlerPure.logMsg("dequeueOutputBuffer INFO_OUTPUT_BUFFERS_CHANGED!"); break; case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: MediaFormat mf = codec.getOutputFormat(); //开始编码线程 EncodeTask encodeTask = new EncodeTask(outFile, this, listener); int sampleRate = mf.getInteger(MediaFormat.KEY_SAMPLE_RATE); int pcmEncoding = mf.getInteger(MediaFormat.KEY_PCM_ENCODING); int channelCount = mf.getInteger(MediaFormat.KEY_CHANNEL_COUNT); encodeTask.setAudioParams(sampleRate, pcmEncoding, channelCount); new Thread(encodeTask).start(); TransAacHandlerPure.logMsg("New format " + mf.toString()); break; case MediaCodec.INFO_TRY_AGAIN_LATER: TransAacHandlerPure.logMsg("dequeueOutputBuffer timed out!"); break; default: if (last == 0) { last = System.currentTimeMillis(); } long now = System.currentTimeMillis(); TransAacHandlerPure.logMsg("解码时间:" + (now - last) + " info.size " + info.size); last = now; ByteBuffer buffer = outputBuffers[outIndex]; byte[] outData = new byte[info.size]; buffer.get(outData, 0, info.size); codec.releaseOutputBuffer(outIndex, true); try { mOutput.write(outData); } catch (IOException e) { e.printStackTrace(); } pushAvFrame(outData); if (listener != null) { listener.onProgress(rangeEnd > 0 ? (int) rangeEnd : duration, rangeStart > 0 ? (int) (timestamp - rangeStart) : (int) timestamp); } break; } // All decoded frames have been rendered, we can stop playing now if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { TransAacHandlerPure.logMsg("OutputBuffer BUFFER_FLAG_END_OF_STREAM"); break; } } } @Override public byte[] getRawFrame() { int len = mRawQueue.size(); if (len > 0) { synchronized (mRawQueue) { return mRawQueue.poll(); } } return null; } @Override public boolean isFinish() { return isFinish; } } private static void logMsg(String msg) { Log.d(TransAacHandlerPure.class.getSimpleName(), msg); } private static class EncodeTask implements Runnable { private static final long TIME_OUT = 5000; private IDataObtain obtain; private String outFile; private MediaCodec encoder; private OutputStream mOutput; private OnProgressListener listener; private long last; private int sampleRate; private int pcmEncoding; private int channelCount; public EncodeTask(String outFile, IDataObtain obtain, OnProgressListener listener) { this.obtain = obtain; this.outFile = outFile; this.listener = listener; } public void setAudioParams(int sampleRate, int pcmEncoding, int channelCount) { this.sampleRate = sampleRate; this.pcmEncoding = pcmEncoding; this.channelCount = channelCount; } @Override public void run() { boolean isPrepare = false; try { prepare(); isPrepare = true; } catch (IOException e) { e.printStackTrace(); } if (isPrepare && obtain != null) { encode(); } release(); if (listener != null) { if (isPrepare) { listener.onSuccess(); } else { listener.onFail(); } } } private void release() { if (encoder != null) { encoder.stop(); encoder.release(); encoder = null; } if (mOutput != null) { try { mOutput.flush(); mOutput.close(); } catch (IOException e) { e.printStackTrace(); } mOutput = null; } } private void encode() { boolean isFinish = false; while (true) { if (!isFinish) { byte[] rawData = obtain.getRawFrame(); if (rawData == null) { if (obtain.isFinish()) { isFinish = true; int inIndex = encoder.dequeueInputBuffer(TIME_OUT); encoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); } else { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } continue; } ByteBuffer[] inputBuffers = encoder.getInputBuffers(); int inIndex = encoder.dequeueInputBuffer(TIME_OUT); if (inIndex >= 0) { ByteBuffer inputBuffer = inputBuffers[inIndex]; inputBuffer.clear(); inputBuffer.put(rawData); encoder.queueInputBuffer(inIndex, 0, rawData.length, System.nanoTime(), 0); } } ByteBuffer[] outputBuffers = encoder.getOutputBuffers(); MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); int outIndex = encoder.dequeueOutputBuffer(info, TIME_OUT); if (outIndex >= 0) { if (last == 0) { last = System.currentTimeMillis(); } long now = System.currentTimeMillis(); TransAacHandlerPure.logMsg("编码码时间:" + (now - last) + " info.size " + info.size); last = now; while (outIndex >= 0) { ByteBuffer outputBuffer = outputBuffers[outIndex]; int len = info.size + 7; byte[] outData = new byte[len]; addADTStoPacket(outData, len); outputBuffer.get(outData, 7, info.size); encoder.releaseOutputBuffer(outIndex, false); try { mOutput.write(outData); } catch (Exception e) { e.printStackTrace(); } catch (Error e) { e.printStackTrace(); } outIndex = encoder.dequeueOutputBuffer(info, TIME_OUT); } } if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { VLog.d("encode OutputBuffer BUFFER_FLAG_END_OF_STREAM"); break; } } } /** * 给编码出的aac裸流添加adts头字段 * * @param packet 要空出前7个字节,否则会搞乱数据 * @param packetLen */ private void addADTStoPacket(byte[] packet, int packetLen) { int profile = 2; //AAC LC int freqIdx = 4; //44.1KHz int chanCfg = 2; //CPE 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; } private void prepare() throws IOException { String mime = MediaFormat.MIMETYPE_AUDIO_AAC; encoder = MediaCodec.createEncoderByType(mime); MediaFormat format = MediaFormat.createAudioFormat(mime, sampleRate, channelCount); format.setInteger(MediaFormat.KEY_BIT_RATE, 96000); format.setInteger(MediaFormat.KEY_PCM_ENCODING, pcmEncoding); format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 20 * 1024); format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); logMsg(" New " + format.toString()); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); encoder.start(); createFile(outFile, true); mOutput = new DataOutputStream(new FileOutputStream(outFile)); } } private static boolean createFile(String filePath, boolean recreate) { if (TextUtils.isEmpty(filePath)) { return false; } try { File file = new File(filePath); if (file.exists()) { if (recreate) { file.delete(); file.createNewFile(); } } else { // 如果路径不存在,先创建路径 File parentFile = file.getParentFile(); if (!parentFile.exists()) { parentFile.mkdirs(); } file.createNewFile(); } } catch (Exception e) { return false; } return true; } public interface IDataObtain { byte[] getRawFrame(); boolean isFinish(); } public static interface OnProgressListener { void onStart(); void onProgress(int max, int progress); void onSuccess(); void onFail(); }}
关于音频裁剪比较简单,如果相同格式不需要转码,直接用MediaExtractor读取相关片段后直接写入即可,这样效率比较高。关于这个转码算法的效率还是比较低,主要原因是编码比较慢,虽然多线程解决了部分问题,但是效率还是比较慢,大家有兴趣可以提出修改意见,咱们一起优化一下这个算法!
由于准备匆忙,很多知识点都没有跟大家说清楚,望大家见谅!希望代码写得不是很乱,大家能看得懂,有不懂的地方,欢迎跟我沟通!
阅读全文
0 0
- android 使用MediaCodec(根据设备状况硬编解码)来转码音频(MP3 to aac),并同时裁剪音频
- Android MediaCodec硬解码AAC音频文件(实时AAC音频帧)并播放
- Android MediaCodec硬解码AAC音频文件(实时AAC音频帧)并播放
- android MediaCodec 音频编解码的实现
- android AAC的音频硬解码
- 音频编解码(使用faac库)--WAV转至AAC(AAC编码)
- android MediaCodec 音频编解码的实现——转码(好文)
- Android MediaCodec硬解码AAC音频文件并播放
- android MediaCodec 音频编解码的实现——转码
- android MediaCodec 音频编解码的实现——转码
- android MediaCodec 音频编解码的实现——转码
- 音频编解码(1):MPEG1 MP3 系统算法分析
- Android硬编解码接口MediaCodec使用完全解析(一)
- android 音频裁剪(1)—MP3裁剪
- Android音频开发(5):音频数据的编解码
- Android音频开发(5):音频数据的编解码
- 利用MediaCodec对音频编解码
- Android音频AAC硬编码
- DBCP连接池原理分析
- 提高Web服务端并发效率的异步编程技术
- 使用Gradle查看Android依赖树图
- Codeforces785C (二分)
- 利用转换器将cad转pdf格式
- android 使用MediaCodec(根据设备状况硬编解码)来转码音频(MP3 to aac),并同时裁剪音频
- windows下搭建vue开发环境
- 剑指offer--和为S的两个数字
- eclipse启动服务器报错
- 微信日常小记-error48001
- Spring整合Hibernate使用懒加载报错Could not initialize proxy
- Eclipse背景色、主题、字体设置
- Mybatis JPA-集成方案+源码
- 结构型-桥接模式(Bridge)