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
原创粉丝点击