Skip to content

Instantly share code, notes, and snippets.

@DemonGiggle
Created January 9, 2013 15:30
Show Gist options
  • Save DemonGiggle/4494034 to your computer and use it in GitHub Desktop.
Save DemonGiggle/4494034 to your computer and use it in GitHub Desktop.
private void encodeDecodeVideo() {
int width = 328, height = 248;
int bitRate = 1000000;
int frameRate = 15;
String mimeType = "video/avc";
int threshold = 50;
int maxerror = 50;
MediaCodec encoder, decoder = null;
ByteBuffer[] encoderInputBuffers;
ByteBuffer[] encoderOutputBuffers;
ByteBuffer[] decoderInputBuffers = null;
ByteBuffer[] decoderOutputBuffers = null;
int numCodecs = MediaCodecList.getCodecCount();
MediaCodecInfo codecInfo = null;
for (int i = 0; i < numCodecs && codecInfo == null; i++) {
MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
if (!info.isEncoder()) {
continue;
}
String[] types = info.getSupportedTypes();
boolean found = false;
for (int j = 0; j < types.length && !found; j++) {
if (types[j].equals(mimeType))
found = true;
}
if (!found)
continue;
codecInfo = info;
}
Log.d(TAG, "Found " + codecInfo.getName() + " supporting " + mimeType);
int colorFormat = 0;
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
for (int i = 0; i < capabilities.colorFormats.length && colorFormat == 0; i++) {
int format = capabilities.colorFormats[i];
switch (format) {
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
colorFormat = format;
break;
default:
Log.d(TAG, "Skipping unsupported color format " + format);
break;
}
}
assertTrue("no supported color format", colorFormat != 0);
Log.d(TAG, "Using color format " + colorFormat);
if (codecInfo.getName().equals("OMX.TI.DUCATI1.VIDEO.H264E")) {
// This codec doesn't support a width not a multiple of 16,
// so round down.
width &= ~15;
}
int stride = width;
int sliceHeight = height;
if (codecInfo.getName().startsWith("OMX.Nvidia.")) {
stride = (stride + 15)/16*16;
sliceHeight = (sliceHeight + 15)/16*16;
}
encoder = MediaCodec.createByCodecName(codecInfo.getName());
MediaFormat inputFormat = MediaFormat.createVideoFormat(mimeType, width, height);
inputFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
inputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
inputFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
inputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 75);
inputFormat.setInteger("stride", stride);
inputFormat.setInteger("slice-height", sliceHeight);
Log.d(TAG, "Configuring encoder with input format " + inputFormat);
encoder.configure(inputFormat, null /* surface */, null /* crypto */, MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.start();
encoderInputBuffers = encoder.getInputBuffers();
encoderOutputBuffers = encoder.getOutputBuffers();
int chromaStride = stride/2;
int frameSize = stride*sliceHeight + 2*chromaStride*sliceHeight/2;
byte[] inputFrame = new byte[frameSize];
if (colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar ||
colorFormat == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int Y = (x + y) & 255;
int Cb = 255*x/width;
int Cr = 255*y/height;
inputFrame[y*stride + x] = (byte) Y;
inputFrame[stride*sliceHeight + (y/2)*chromaStride + (x/2)] = (byte) Cb;
inputFrame[stride*sliceHeight + chromaStride*(sliceHeight/2) + (y/2)*chromaStride + (x/2)] = (byte) Cr;
}
}
} else {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int Y = (x + y) & 255;
int Cb = 255*x/width;
int Cr = 255*y/height;
inputFrame[y*stride + x] = (byte) Y;
inputFrame[stride*sliceHeight + 2*(y/2)*chromaStride + 2*(x/2)] = (byte) Cb;
inputFrame[stride*sliceHeight + 2*(y/2)*chromaStride + 2*(x/2) + 1] = (byte) Cr;
}
}
}
// start encoding + decoding
final long kTimeOutUs = 5000;
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
boolean sawInputEOS = false;
boolean sawOutputEOS = false;
MediaFormat oformat = null;
int errors = -1;
int numInputFrames = 0;
while (!sawOutputEOS && errors < 0) {
if (!sawInputEOS) {
int inputBufIndex = encoder.dequeueInputBuffer(kTimeOutUs);
if (inputBufIndex >= 0) {
ByteBuffer dstBuf = encoderInputBuffers[inputBufIndex];
int sampleSize = frameSize;
long presentationTimeUs = 0;
if (numInputFrames >= 10) {
Log.d(TAG, "saw input EOS.");
sawInputEOS = true;
sampleSize = 0;
} else {
dstBuf.clear();
dstBuf.put(inputFrame);
presentationTimeUs = numInputFrames*1000000/frameRate;
numInputFrames++;
}
encoder.queueInputBuffer(
inputBufIndex,
0 /* offset */,
sampleSize,
presentationTimeUs,
sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
}
}
int res = encoder.dequeueOutputBuffer(info, kTimeOutUs);
if (res >= 0) {
int outputBufIndex = res;
ByteBuffer buf = encoderOutputBuffers[outputBufIndex];
buf.position(info.offset);
buf.limit(info.offset + info.size);
if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
decoder = MediaCodec.createDecoderByType(mimeType);
MediaFormat format = MediaFormat.createVideoFormat(mimeType, width, height);
format.setByteBuffer("csd-0", buf);
decoder.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
decoder.start();
decoderInputBuffers = decoder.getInputBuffers();
decoderOutputBuffers = decoder.getOutputBuffers();
} else {
int decIndex = decoder.dequeueInputBuffer(-1);
decoderInputBuffers[decIndex].clear();
decoderInputBuffers[decIndex].put(buf);
decoder.queueInputBuffer(decIndex, 0, info.size, info.presentationTimeUs, info.flags);
}
encoder.releaseOutputBuffer(outputBufIndex, false /* render */);
} else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
encoderOutputBuffers = encoder.getOutputBuffers();
Log.d(TAG, "encoder output buffers have changed.");
} else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat encformat = encoder.getOutputFormat();
Log.d(TAG, "encoder output format has changed to " + encformat);
}
if (decoder == null)
res = MediaCodec.INFO_TRY_AGAIN_LATER;
else
res = decoder.dequeueOutputBuffer(info, kTimeOutUs);
if (res >= 0) {
int outputBufIndex = res;
ByteBuffer buf = decoderOutputBuffers[outputBufIndex];
buf.position(info.offset);
buf.limit(info.offset + info.size);
if (info.size > 0) {
errors = checkFrame(buf, info, oformat, width, height, threshold);
}
decoder.releaseOutputBuffer(outputBufIndex, false /* render */);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d(TAG, "saw output EOS.");
sawOutputEOS = true;
}
} else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
decoderOutputBuffers = decoder.getOutputBuffers();
Log.d(TAG, "decoder output buffers have changed.");
} else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
oformat = decoder.getOutputFormat();
Log.d(TAG, "decoder output format has changed to " + oformat);
}
}
encoder.stop();
encoder.release();
decoder.stop();
decoder.release();
assertTrue("no frame decoded", errors >= 0);
assertTrue("decoding error too big: " + errors + "/" + maxerror, errors <= maxerror);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment