Created
January 9, 2013 15:30
-
-
Save DemonGiggle/4494034 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
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