Created
August 5, 2016 09:10
-
-
Save fengye/99c084b96da839d8f93eb7dcdbd6666b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.media.MediaCodec; | |
import android.media.MediaCodecInfo; | |
import android.media.MediaFormat; | |
import android.util.Log; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.FileNotFoundException; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.nio.ByteBuffer; | |
/** | |
* Created by 8i on 5/08/2016. | |
*/ | |
public class OfflineAudioEncoder { | |
static String TAG = "OfflineAudioEncoder"; | |
static int FILE_READ_BUF_SIZE = 512; | |
private MediaCodec mediaCodec; | |
public OfflineAudioEncoder(String mimeType, int sampleRate, int channels) throws IOException | |
{ | |
// audio/mp4a-latm | |
// "audio/aac" | |
mediaCodec = MediaCodec.createEncoderByType(mimeType); | |
MediaFormat mediaFormat = MediaFormat.createAudioFormat(mimeType, sampleRate, channels); | |
//mediaFormat.setString(MediaFormat.KEY_MIME, mimeType); | |
mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); | |
//mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRate); | |
//mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, channels); | |
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 128 * 1024); | |
mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1024 * 16); | |
/* | |
byte[] bytes = new byte[]{(byte) 0x11, (byte)0x90}; | |
ByteBuffer bb = ByteBuffer.wrap(bytes); | |
mediaFormat.setByteBuffer("csd-0", bb); | |
*/ | |
/* | |
int profile = 2; //AAC LC | |
int freqIdx = 4; //44.1KHz | |
int channelIdx = 2; | |
ByteBuffer csd = ByteBuffer.allocate(2); | |
csd.put(0, (byte) (profile << 3 | freqIdx >> 1)); | |
csd.put(1, (byte)((freqIdx & 0x01) << 7 | channelIdx << 3)); | |
mediaFormat.setByteBuffer("csd-0", csd); | |
*/ | |
Log.d(TAG, "INITIAL FORMAT: " + mediaFormat.toString()); | |
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); | |
} | |
public void syncEncode(File source, File destination) throws FileNotFoundException, IOException { | |
mediaCodec.start(); | |
Log.d(TAG, "Input: " + source.getPath()); | |
Log.d(TAG, "Output: " + destination.getPath()); | |
FileInputStream inputStream = new FileInputStream(source); | |
inputStream.skip(44); // skip header? | |
FileOutputStream outputStream = new FileOutputStream(destination); | |
byte[] fileBuf = new byte[FILE_READ_BUF_SIZE]; | |
int fileBufPtr = 0; | |
long timestamp = 1000; | |
try { | |
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); | |
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers(); | |
// read the first buf (could read to the end) | |
int fileBufLength = inputStream.read(fileBuf); | |
do { | |
fileBufPtr = 0; | |
// let's finish with the current filebuf | |
while (fileBufPtr < fileBufLength) { | |
//Log.d(TAG, "fileBufPtr: " + fileBufPtr); | |
int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1); | |
if (inputBufferIndex >= 0) { | |
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; | |
inputBuffer.clear(); | |
int inputBufLength = inputBuffer.capacity(); | |
int effectiveLength; | |
// if the remaining in fileBuf data is beyond the capacity of input buffer | |
if ((fileBufLength - fileBufPtr) > inputBufLength) { | |
inputBuffer.put(fileBuf, fileBufPtr, inputBufLength); | |
fileBufPtr += inputBufLength; | |
effectiveLength = inputBufLength; | |
Log.d(TAG, "Input buffer too short, effectiveLength: " + effectiveLength); | |
} else { | |
effectiveLength = fileBufLength - fileBufPtr; | |
inputBuffer.put(fileBuf, fileBufPtr, effectiveLength); | |
fileBufPtr += effectiveLength; | |
Log.d(TAG, "Input buffer large enough, effectiveLength: " + effectiveLength); | |
} | |
// feed the codec with the input buffer | |
mediaCodec.queueInputBuffer(inputBufferIndex, 0, effectiveLength, timestamp, 0); | |
timestamp += 1000 * 16; | |
} | |
//Log.d(TAG, "Waiting for dequeue output buffer"); | |
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); | |
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); | |
Log.d(TAG, "BufferInfo flag: " + bufferInfo.flags); | |
//Log.d(TAG, "Output buffer arrived"); | |
while (outputBufferIndex >= 0) { | |
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) | |
{ | |
Log.d(TAG, "BUFFER_FLAG_CODE_CONFIG found, bufferInfo.size:" + bufferInfo.size ); | |
// goto next | |
mediaCodec.releaseOutputBuffer(outputBufferIndex, false); | |
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); | |
continue; | |
} | |
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; | |
Log.d(TAG, "Output buffer#" + outputBufferIndex + " buffer info offset: " + bufferInfo.offset + " size: " + bufferInfo.size); | |
Log.d(TAG, "Output buffer pos: " + outputBuffer.position()); | |
outputBuffer.position(bufferInfo.offset); | |
//outputBuffer.limit(bufferInfo.offset + bufferInfo.size); | |
byte[] outData = new byte[bufferInfo.size]; | |
outputBuffer.get(outData, 0, bufferInfo.size); | |
// write outData into out stream | |
outputStream.write(outData); | |
Log.d(TAG, "Written to output file stream"); | |
mediaCodec.releaseOutputBuffer(outputBufferIndex, false); | |
//Log.d(TAG, "Release output buffer"); | |
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); | |
//Log.d(TAG, "Dequeue another output buffer"); | |
//Log.d(TAG, "BufferInfo flag: " + bufferInfo.flags); | |
} | |
if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) | |
{ | |
//Log.d(TAG, "No output buffer ready, try again later!"); | |
} | |
else | |
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { | |
// re-get the output buffer as requested | |
Log.d(TAG, "Re-get output buffers!"); | |
outputBuffers = mediaCodec.getOutputBuffers(); | |
} | |
else | |
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { | |
MediaFormat mediaFormat = mediaCodec.getOutputFormat(); | |
Log.d(TAG, "OUTPUT FORMAT CHANGED: " + mediaFormat.toString()); | |
// restart the input stream? | |
inputStream.close(); | |
inputStream = new FileInputStream(source); | |
inputStream.skip(44); // skip header? | |
outputStream.close(); | |
outputStream = new FileOutputStream(destination); | |
} | |
} | |
fileBufLength = inputStream.read(fileBuf); | |
} while (fileBufLength > 0); | |
// finalize by sending eos flag | |
//ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); | |
//ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers(); | |
// signal the end of stream | |
Log.d(TAG, "Signal end of stream!!!"); | |
int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1); | |
mediaCodec.queueInputBuffer(inputBufferIndex, 0, 0, timestamp, MediaCodec.BUFFER_FLAG_END_OF_STREAM); | |
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); | |
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, -1); | |
Log.d(TAG, "BufferInfo flag: " + bufferInfo.flags); | |
for(;;) { | |
boolean bailout = false; | |
while (outputBufferIndex >= 0) { | |
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; | |
Log.d(TAG, "Output buffer#" + outputBufferIndex + " buffer info offset: " + bufferInfo.offset + " size: " + bufferInfo.size); | |
Log.d(TAG, "Output buffer pos: " + outputBuffer.position()); | |
if (bufferInfo.size == 0) | |
{ | |
Log.d(TAG, "Zero size Finally!"); | |
bailout = true; | |
break; | |
} | |
byte[] outData = new byte[bufferInfo.size]; | |
outputBuffer.position(bufferInfo.offset); | |
//outputBuffer.limit(bufferInfo.offset + bufferInfo.size); | |
outputBuffer.get(outData, 0, bufferInfo.size); | |
// write outData into out stream | |
outputStream.write(outData); | |
mediaCodec.releaseOutputBuffer(outputBufferIndex, false); | |
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { | |
Log.d(TAG, "Flag Finally!"); | |
bailout = true; | |
break; | |
} | |
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, -1); | |
Log.d(TAG, "BufferInfo flag: " + bufferInfo.flags); | |
} | |
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { | |
// re-get the output buffer as requested | |
Log.d(TAG, "Re-get output buffers!"); | |
outputBuffers = mediaCodec.getOutputBuffers(); | |
} | |
else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { | |
MediaFormat mediaFormat = mediaCodec.getOutputFormat(); | |
Log.d(TAG, "OUTPUT FORMAT CHANGED: " + mediaFormat.toString()); | |
} | |
if (bailout) | |
{ | |
break; | |
} | |
} | |
Log.d(TAG, "Closing the output stream"); | |
// close out stream | |
inputStream.close(); | |
outputStream.close(); | |
} catch (IOException e) { | |
Log.e(TAG, "IOException occurred during encoding"); | |
e.printStackTrace(); | |
} | |
mediaCodec.stop(); | |
} | |
public void release() | |
{ | |
mediaCodec.release(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment