/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media; import android.media.MediaCrypto; import android.media.MediaFormat; import android.view.Surface; import java.nio.ByteBuffer; import java.util.Map; /** * MediaCodec class can be used to access low-level media codec, i.e. * encoder/decoder components. * *
MediaCodec is generally used like this: *
* MediaCodec codec = MediaCodec.createDecoderByType(type); * codec.configure(format, ...); * codec.start(); * ByteBuffer[] inputBuffers = codec.getInputBuffers(); * ByteBuffer[] outputBuffers = codec.getOutputBuffers(); * Map* * Each codec maintains a number of input and output buffers that are * referred to by index in API calls. * The contents of these buffers is represented by the ByteBuffer[] arrays * accessible through getInputBuffers() and getOutputBuffers(). * * After a successful call to {@link #start} the client "owns" neither * input nor output buffers, subsequent calls to {@link #dequeueInputBuffer} * and {@link #dequeueOutputBuffer} then transfer ownership from the codec * to the client.format = codec.getOutputFormat(); * for (;;) { * int inputBufferIndex = codec.dequeueInputBuffer(timeoutUs); * if (inputBufferIndex >= 0) { * // fill inputBuffers[inputBufferIndex] with valid data * ... * codec.queueInputBuffer(inputBufferIndex, ...); * } * * int outputBufferIndex = codec.dequeueOutputBuffer(timeoutUs); * if (outputBufferIndex >= 0) { * // outputBuffer is ready to be processed or rendered. * ... * codec.releaseOutputBuffer(outputBufferIndex, ...); * } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { * outputBuffers = codec.getOutputBuffers(); * } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { * // Subsequent data will conform to new format. * format = codec.getOutputFormat(); * ... * } * } * codec.stop(); * codec.release(); * codec = null; *
* The client is not required to resubmit/release buffers immediately * to the codec, the sample code above simply does this for simplicity's sake.
* Once the client has an input buffer available it can fill it with data * and submit it it to the codec via a call to {@link #queueInputBuffer}.
* The codec in turn will return an output buffer to the client in response * to {@link #dequeueOutputBuffer}. After the output buffer has been processed * a call to {@link #releaseOutputBuffer} will return it to the codec. * If a video surface has been provided in the call to {@link #configure}, * {@link #releaseOutputBuffer} optionally allows rendering of the buffer * to the surface.
* * Input buffers (for decoders) and Output buffers (for encoders) contain * encoded data according to the format's type. For video types this data * is all the encoded data representing a single moment in time, for audio * data this is slightly relaxed in that a buffer may contain multiple * encoded frames of audio. In either case, buffers do not start and end on * arbitrary byte boundaries, this is not a stream of bytes, it's a stream * of access units.
*
* Most formats also require the actual data to be prefixed by a number
* of buffers containing setup data, or codec specific data, i.e. the
* first few buffers submitted to the codec object after starting it must
* be codec specific data marked as such using the flag {@link #BUFFER_FLAG_CODEC_CONFIG}
* in a call to {@link #queueInputBuffer}.
*
* Once the client reaches the end of the input data it signals the end of
* the input stream by specifying a flag of {@link #BUFFER_FLAG_END_OF_STREAM} in the call to
* {@link #queueInputBuffer}. The codec will continue to return output buffers
* until it eventually signals the end of the output stream by specifying
* the same flag ({@link #BUFFER_FLAG_END_OF_STREAM}) on the BufferInfo returned in
* {@link #dequeueOutputBuffer}.
*
* In order to start decoding data that's not adjacent to previously submitted
* data (i.e. after a seek) it is necessary to {@link #flush} the decoder.
* Any input or output buffers the client may own at the point of the flush are
* immediately revoked, i.e. after a call to {@link #flush} the client does not
* own any buffers anymore.
* Note that the format of the data submitted after a flush must not change,
* flush does not support format discontinuities,
* for this a full stop(), configure(), start() cycle is necessary.
*
*/
final public class MediaCodec {
/**
* Per buffer metadata includes an offset and size specifying
* the range of valid data in the associated codec buffer.
*/
public final static class BufferInfo {
public void set(
int newOffset, int newSize, long newTimeUs, int newFlags) {
offset = newOffset;
size = newSize;
presentationTimeUs = newTimeUs;
flags = newFlags;
}
public int offset;
public int size;
public long presentationTimeUs;
public int flags;
};
// The follow flag constants MUST stay in sync with their equivalents
// in MediaCodec.h !
/**
* This indicates that the buffer marked as such contains the data
* for a sync frame.
*/
public static final int BUFFER_FLAG_SYNC_FRAME = 1;
/**
* This indicated that the buffer marked as such contains codec
* initialization / codec specific data instead of media data.
*/
public static final int BUFFER_FLAG_CODEC_CONFIG = 2;
/**
* This signals the end of stream, i.e. no buffers will be available
* after this, unless of course, {@link #flush} follows.
*/
public static final int BUFFER_FLAG_END_OF_STREAM = 4;
/**
* Instantiate a decoder supporting input data of the given mime type.
* @param type The mime type of the input data.
*/
public static MediaCodec createDecoderByType(String type) {
return new MediaCodec(type, true /* nameIsType */, false /* encoder */);
}
/**
* Instantiate an encoder supporting output data of the given mime type.
* @param type The desired mime type of the output data.
*/
public static MediaCodec createEncoderByType(String type) {
return new MediaCodec(type, true /* nameIsType */, true /* encoder */);
}
/**
* If you know the exact name of the component you want to instantiate
* use this method to instantiate it. Use with caution.
* Likely to be used with information obtained from {@link android.media.MediaCodecList}
* @param name The name of the codec to be instantiated.
*/
public static MediaCodec createByCodecName(String name) {
return new MediaCodec(
name, false /* nameIsType */, false /* unused */);
}
private MediaCodec(
String name, boolean nameIsType, boolean encoder) {
native_setup(name, nameIsType, encoder);
}
@Override
protected void finalize() {
native_finalize();
}
/**
* Make sure you call this when you're done to free up any opened
* component instance instead of relying on the garbage collector
* to do this for you at some point in the future.
*/
public native final void release();
/**
* If this codec is to be used as an encoder, pass this flag.
*/
public static final int CONFIGURE_FLAG_ENCODE = 1;
/**
* Configures a component.
*
* @param format The format of the input data (decoder) or the desired
* format of the output data (encoder).
* @param surface Specify a surface on which to render the output of this
* decoder.
* @param crypto Specify a crypto object to facilitate secure decryption
* of the media data.
* @param flags Specify {@link #CONFIGURE_FLAG_ENCODE} to configure the
* component as an encoder.
*/
public void configure(
MediaFormat format,
Surface surface, MediaCrypto crypto, int flags) {
Map