Skip to content

Instantly share code, notes, and snippets.

@fengye
Created August 5, 2016 09:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fengye/99c084b96da839d8f93eb7dcdbd6666b to your computer and use it in GitHub Desktop.
Save fengye/99c084b96da839d8f93eb7dcdbd6666b to your computer and use it in GitHub Desktop.
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