MediaCodec Class
Definition
Important
Some information relates to prerelease product that may be substantially modified before it’s released. Microsoft makes no warranties, express or implied, with respect to the information provided here.
MediaCodec class can be used to access low-level media codecs, i.
[Android.Runtime.Register("android/media/MediaCodec", DoNotGenerateAcw=true)]
public sealed class MediaCodec : Java.Lang.Object
[<Android.Runtime.Register("android/media/MediaCodec", DoNotGenerateAcw=true)>]
type MediaCodec = class
inherit Object
- Inheritance
- Attributes
Remarks
MediaCodec class can be used to access low-level media codecs, i.e. encoder/decoder components. It is part of the Android low-level multimedia support infrastructure (normally used together with MediaExtractor
, MediaSync
, MediaMuxer
, MediaCrypto
, MediaDrm
, Image
, Surface
, and AudioTrack
.)
<center> <img src="../../../images/media/mediacodec_buffers.svg" style="width: 540px; height: 205px" alt="MediaCodec buffer flow diagram"> </center>
In broad terms, a codec processes input data to generate output data. It processes data asynchronously and uses a set of input and output buffers. At a simplistic level, you request (or receive) an empty input buffer, fill it up with data and send it to the codec for processing. The codec uses up the data and transforms it into one of its empty output buffers. Finally, you request (or receive) a filled output buffer, consume its contents and release it back to the codec.
<h3 id=qualityFloor>"qualityFloor">Minimum Quality Floor for Video Encoding</h3>
Beginning with android.os.Build.VERSION_CODES#S
, Android's Video MediaCodecs enforce a minimum quality floor. The intent is to eliminate poor quality video encodings. This quality floor is applied when the codec is in Variable Bitrate (VBR) mode; it is not applied when the codec is in Constant Bitrate (CBR) mode. The quality floor enforcement is also restricted to a particular size range; this size range is currently for video resolutions larger than 320x240 up through 1920x1080.
When this quality floor is in effect, the codec and supporting framework code will work to ensure that the generated video is of at least a "fair" or "good" quality. The metric used to choose these targets is the VMAF (Video Multi-method Assessment Function) with a target score of 70 for selected test sequences.
The typical effect is that some videos will generate a higher bitrate than originally configured. This will be most notable for videos which were configured with very low bitrates; the codec will use a bitrate that is determined to be more likely to generate an "fair" or "good" quality video. Another situation is where a video includes very complicated content (lots of motion and detail); in such configurations, the codec will use extra bitrate as needed to avoid losing all of the content's finer detail.
This quality floor will not impact content captured at high bitrates (a high bitrate should already provide the codec with sufficient capacity to encode all of the detail). The quality floor does not operate on CBR encodings. The quality floor currently does not operate on resolutions of 320x240 or lower, nor on videos with resolution above 1920x1080.
<h3>Data Types</h3>
Codecs operate on three kinds of data: compressed data, raw audio data and raw video data. All three kinds of data can be processed using ByteBuffer ByteBuffers
, but you should use a Surface
for raw video data to improve codec performance. Surface uses native video buffers without mapping or copying them to ByteBuffers; thus, it is much more efficient. You normally cannot access the raw video data when using a Surface, but you can use the ImageReader
class to access unsecured decoded (raw) video frames. This may still be more efficient than using ByteBuffers, as some native buffers may be mapped into ByteBuffer#isDirect direct ByteBuffers. When using ByteBuffer mode, you can access raw video frames using the Image
class and #getInputImage getInput
/#getOutputImage OutputImage(int)
.
<h4>Compressed Buffers</h4>
Input buffers (for decoders) and output buffers (for encoders) contain compressed data according to the MediaFormat#KEY_MIME format's type. For video types this is normally a single compressed video frame. For audio data this is normally a single access unit (an encoded audio segment typically containing a few milliseconds of audio as dictated by the format type), but this requirement is slightly relaxed in that a buffer may contain multiple encoded access units of audio. In either case, buffers do not start or end on arbitrary byte boundaries, but rather on frame/access unit boundaries unless they are flagged with #BUFFER_FLAG_PARTIAL_FRAME
.
<h4>Raw Audio Buffers</h4>
Raw audio buffers contain entire frames of PCM audio data, which is one sample for each channel in channel order. Each PCM audio sample is either a 16 bit signed integer or a float, in native byte order. Raw audio buffers in the float PCM encoding are only possible if the MediaFormat's MediaFormat#KEY_PCM_ENCODING is set to AudioFormat#ENCODING_PCM_FLOAT during MediaCodec #configure configure(…)
and confirmed by #getOutputFormat
for decoders or #getInputFormat
for encoders. A sample method to check for float PCM in the MediaFormat is as follows:
static boolean isPcmFloat(MediaFormat format) {
return format.getInteger(MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_16BIT)
== AudioFormat.ENCODING_PCM_FLOAT;
}
In order to extract, in a short array, one channel of a buffer containing 16 bit signed integer audio data, the following code may be used:
// Assumes the buffer PCM encoding is 16 bit.
short[] getSamplesForChannel(MediaCodec codec, int bufferId, int channelIx) {
ByteBuffer outputBuffer = codec.getOutputBuffer(bufferId);
MediaFormat format = codec.getOutputFormat(bufferId);
ShortBuffer samples = outputBuffer.order(ByteOrder.nativeOrder()).asShortBuffer();
int numChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
if (channelIx < 0 || channelIx >= numChannels) {
return null;
}
short[] res = new short[samples.remaining() / numChannels];
for (int i = 0; i < res.length; ++i) {
res[i] = samples.get(i * numChannels + channelIx);
}
return res;
}
<h4>Raw Video Buffers</h4>
In ByteBuffer mode video buffers are laid out according to their MediaFormat#KEY_COLOR_FORMAT color format. You can get the supported color formats as an array from #getCodecInfo
.
MediaCodecInfo#getCapabilitiesForType getCapabilitiesForType(…)
.
CodecCapabilities#colorFormats colorFormats
. Video codecs may support three kinds of color formats: <ul> <li><strong>native raw video format:</strong> This is marked by CodecCapabilities#COLOR_FormatSurface
and it can be used with an input or output Surface.</li> <li><strong>flexible YUV buffers</strong> (such as CodecCapabilities#COLOR_FormatYUV420Flexible
): These can be used with an input/output Surface, as well as in ByteBuffer mode, by using #getInputImage getInput
/#getOutputImage OutputImage(int)
.</li> <li><strong>other, specific formats:</strong> These are normally only supported in ByteBuffer mode. Some color formats are vendor specific. Others are defined in CodecCapabilities
. For color formats that are equivalent to a flexible format, you can still use #getInputImage getInput
/#getOutputImage OutputImage(int)
.</li> </ul>
All video codecs support flexible YUV 4:2:0 buffers since android.os.Build.VERSION_CODES#LOLLIPOP_MR1
.
<h4>Accessing Raw Video ByteBuffers on Older Devices</h4>
Prior to android.os.Build.VERSION_CODES#LOLLIPOP
and Image
support, you need to use the MediaFormat#KEY_STRIDE
and MediaFormat#KEY_SLICE_HEIGHT
output format values to understand the layout of the raw output buffers. <p class=note> Note that on some devices the slice-height is advertised as 0. This could mean either that the slice-height is the same as the frame height, or that the slice-height is the frame height aligned to some value (usually a power of 2). Unfortunately, there is no standard and simple way to tell the actual slice height in this case. Furthermore, the vertical stride of the U
plane in planar formats is also not specified or defined, though usually it is half of the slice height.
The MediaFormat#KEY_WIDTH
and MediaFormat#KEY_HEIGHT
keys specify the size of the video frames; however, for most encondings the video (picture) only occupies a portion of the video frame. This is represented by the 'crop rectangle'.
You need to use the following keys to get the crop rectangle of raw output images from the #getOutputFormat output format. If these keys are not present, the video occupies the entire video frame.The crop rectangle is understood in the context of the output frame <em>before</em> applying any MediaFormat#KEY_ROTATION rotation. <table style="width: 0%"> <thead> <tr> <th>Format Key</th> <th>Type</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>MediaFormat#KEY_CROP_LEFT
</td> <td>Integer</td> <td>The left-coordinate (x) of the crop rectangle</td> </tr><tr> <td>MediaFormat#KEY_CROP_TOP
</td> <td>Integer</td> <td>The top-coordinate (y) of the crop rectangle</td> </tr><tr> <td>MediaFormat#KEY_CROP_RIGHT
</td> <td>Integer</td> <td>The right-coordinate (x) <strong>MINUS 1</strong> of the crop rectangle</td> </tr><tr> <td>MediaFormat#KEY_CROP_BOTTOM
</td> <td>Integer</td> <td>The bottom-coordinate (y) <strong>MINUS 1</strong> of the crop rectangle</td> </tr><tr> <td colspan=3> The right and bottom coordinates can be understood as the coordinates of the right-most valid column/bottom-most valid row of the cropped output image. </td> </tr> </tbody> </table>
The size of the video frame (before rotation) can be calculated as such:
MediaFormat format = decoder.getOutputFormat(…);
int width = format.getInteger(MediaFormat.KEY_WIDTH);
if (format.containsKey(MediaFormat.KEY_CROP_LEFT)
&& format.containsKey(MediaFormat.KEY_CROP_RIGHT)) {
width = format.getInteger(MediaFormat.KEY_CROP_RIGHT) + 1
- format.getInteger(MediaFormat.KEY_CROP_LEFT);
}
int height = format.getInteger(MediaFormat.KEY_HEIGHT);
if (format.containsKey(MediaFormat.KEY_CROP_TOP)
&& format.containsKey(MediaFormat.KEY_CROP_BOTTOM)) {
height = format.getInteger(MediaFormat.KEY_CROP_BOTTOM) + 1
- format.getInteger(MediaFormat.KEY_CROP_TOP);
}
<p class=note> Also note that the meaning of BufferInfo#offset BufferInfo.offset
was not consistent across devices. On some devices the offset pointed to the top-left pixel of the crop rectangle, while on most devices it pointed to the top-left pixel of the entire frame.
<h3>States</h3>
During its life a codec conceptually exists in one of three states: Stopped, Executing or Released. The Stopped collective state is actually the conglomeration of three states: Uninitialized, Configured and Error, whereas the Executing state conceptually progresses through three sub-states: Flushed, Running and End-of-Stream.
<center> <img src="../../../images/media/mediacodec_states.svg" style="width: 519px; height: 356px" alt="MediaCodec state diagram"> </center>
When you create a codec using one of the factory methods, the codec is in the Uninitialized state. First, you need to configure it via #configure configure(…)
, which brings it to the Configured state, then call #start
to move it to the Executing state. In this state you can process data through the buffer queue manipulation described above.
The Executing state has three sub-states: Flushed, Running and End-of-Stream. Immediately after #start
the codec is in the Flushed sub-state, where it holds all the buffers. As soon as the first input buffer is dequeued, the codec moves to the Running sub-state, where it spends most of its life. When you queue an input buffer with the #BUFFER_FLAG_END_OF_STREAM end-of-stream marker, the codec transitions to the End-of-Stream sub-state. In this state the codec no longer accepts further input buffers, but still generates output buffers until the end-of-stream is reached on the output. For decoders, you can move back to the Flushed sub-state at any time while in the Executing state using #flush
. <p class=note> <strong>Note:</strong> Going back to Flushed state is only supported for decoders, and may not work for encoders (the behavior is undefined).
Call #stop
to return the codec to the Uninitialized state, whereupon it may be configured again. When you are done using a codec, you must release it by calling #release
.
On rare occasions the codec may encounter an error and move to the Error state. This is communicated using an invalid return value from a queuing operation, or sometimes via an exception. Call #reset
to make the codec usable again. You can call it from any state to move the codec back to the Uninitialized state. Otherwise, call #release
to move to the terminal Released state.
<h3>Creation</h3>
Use MediaCodecList
to create a MediaCodec for a specific MediaFormat
. When decoding a file or a stream, you can get the desired format from MediaExtractor#getTrackFormat MediaExtractor.getTrackFormat
. Inject any specific features that you want to add using MediaFormat#setFeatureEnabled MediaFormat.setFeatureEnabled
, then call MediaCodecList#findDecoderForFormat MediaCodecList.findDecoderForFormat
to get the name of a codec that can handle that specific media format. Finally, create the codec using #createByCodecName
. <p class=note> <strong>Note:</strong> On android.os.Build.VERSION_CODES#LOLLIPOP
, the format to MediaCodecList.findDecoder
/EncoderForFormat
must not contain a MediaFormat#KEY_FRAME_RATE frame rate. Use format.setString(MediaFormat.KEY_FRAME_RATE, null)
to clear any existing frame rate setting in the format.
You can also create the preferred codec for a specific MIME type using #createDecoderByType createDecoder
/#createEncoderByType EncoderByType(String)
. This, however, cannot be used to inject features, and may create a codec that cannot handle the specific desired media format.
<h4>Creating secure decoders</h4>
On versions android.os.Build.VERSION_CODES#KITKAT_WATCH
and earlier, secure codecs might not be listed in MediaCodecList
, but may still be available on the system. Secure codecs that exist can be instantiated by name only, by appending ".secure"
to the name of a regular codec (the name of all secure codecs must end in ".secure"
.) #createByCodecName
will throw an IOException
if the codec is not present on the system.
From android.os.Build.VERSION_CODES#LOLLIPOP
onwards, you should use the CodecCapabilities#FEATURE_SecurePlayback
feature in the media format to create a secure decoder.
<h3>Initialization</h3>
After creating the codec, you can set a callback using #setCallback setCallback
if you want to process data asynchronously. Then, #configure configure the codec using the specific media format. This is when you can specify the output Surface
for video producers – codecs that generate raw video data (e.g. video decoders). This is also when you can set the decryption parameters for secure codecs (see MediaCrypto
). Finally, since some codecs can operate in multiple modes, you must specify whether you want it to work as a decoder or an encoder.
Since android.os.Build.VERSION_CODES#LOLLIPOP
, you can query the resulting input and output format in the Configured state. You can use this to verify the resulting configuration, e.g. color formats, before starting the codec.
If you want to process raw input video buffers natively with a video consumer – a codec that processes raw video input, such as a video encoder – create a destination Surface for your input data using #createInputSurface
after configuration. Alternately, set up the codec to use a previously created #createPersistentInputSurface persistent input surface by calling #setInputSurface
.
<h4 id=CSD>"CSD">Codec-specific Data</h4>
Some formats, notably AAC audio and MPEG4, H.264 and H.265 video formats require the actual data to be prefixed by a number of buffers containing setup data, or codec specific data. When processing such compressed formats, this data must be submitted to the codec after #start
and before any frame data. Such data must be marked using the flag #BUFFER_FLAG_CODEC_CONFIG
in a call to #queueInputBuffer queueInputBuffer
.
Codec-specific data can also be included in the format passed to #configure configure
in ByteBuffer entries with keys "csd-0", "csd-1", etc. These keys are always included in the track MediaFormat
obtained from the MediaExtractor#getTrackFormat MediaExtractor
. Codec-specific data in the format is automatically submitted to the codec upon #start
; you <strong>MUST NOT</strong> submit this data explicitly. If the format did not contain codec specific data, you can choose to submit it using the specified number of buffers in the correct order, according to the format requirements. In case of H.264 AVC, you can also concatenate all codec-specific data and submit it as a single codec-config buffer.
Android uses the following codec-specific data buffers. These are also required to be set in the track format for proper MediaMuxer
track configuration. Each parameter set and the codec-specific-data sections marked with (<sup>*</sup>) must start with a start code of "\x00\x00\x00\x01"
.
<style>td.NA { background: #ccc; } .mid > tr > td { vertical-align: middle; }</style> <table> <thead> <th>Format</th> <th>CSD buffer #0</th> <th>CSD buffer #1</th> <th>CSD buffer #2</th> </thead> <tbody class=mid> <tr> <td>AAC</td> <td>Decoder-specific information from ESDS<sup>*</sup></td> <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> <tr> <td>VORBIS</td> <td>Identification header</td> <td>Setup header</td> <td class=NA>Not Used</td> </tr> <tr> <td>OPUS</td> <td>Identification header</td> <td>Pre-skip in nanosecs<br> (unsigned 64-bit ByteOrder#nativeOrder native-order integer.)<br> This overrides the pre-skip value in the identification header.</td> <td>Seek Pre-roll in nanosecs<br> (unsigned 64-bit ByteOrder#nativeOrder native-order integer.)</td> </tr> <tr> <td>FLAC</td> <td>"fLaC", the FLAC stream marker in ASCII,<br> followed by the STREAMINFO block (the mandatory metadata block),<br> optionally followed by any number of other metadata blocks</td> <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> <tr> <td>MPEG-4</td> <td>Decoder-specific information from ESDS<sup>*</sup></td> <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> <tr> <td>H.264 AVC</td> <td>SPS (Sequence Parameter Sets<sup>*</sup>)</td> <td>PPS (Picture Parameter Sets<sup>*</sup>)</td> <td class=NA>Not Used</td> </tr> <tr> <td>H.265 HEVC</td> <td>VPS (Video Parameter Sets<sup>*</sup>) +<br> SPS (Sequence Parameter Sets<sup>*</sup>) +<br> PPS (Picture Parameter Sets<sup>*</sup>)</td> <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> <tr> <td>VP9</td> <td>VP9 CodecPrivate Data (optional)</td> <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> <tr> <td>AV1</td> <td>AV1 AV1CodecConfigurationRecord Data (optional) </td> <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> </tbody> </table>
<p class=note> <strong>Note:</strong> care must be taken if the codec is flushed immediately or shortly after start, before any output buffer or output format change has been returned, as the codec specific data may be lost during the flush. You must resubmit the data using buffers marked with #BUFFER_FLAG_CODEC_CONFIG
after such flush to ensure proper codec operation.
Encoders (or codecs that generate compressed data) will create and return the codec specific data before any valid output buffer in output buffers marked with the #BUFFER_FLAG_CODEC_CONFIG codec-config flag. Buffers containing codec-specific-data have no meaningful timestamps.
<h3>Data Processing</h3>
Each codec maintains a set of input and output buffers that are referred to by a buffer-ID in API calls. After a successful call to #start
the client "owns" neither input nor output buffers. In synchronous mode, call #dequeueInputBuffer dequeueInput
/#dequeueOutputBuffer OutputBuffer(…)
to obtain (get ownership of) an input or output buffer from the codec. In asynchronous mode, you will automatically receive available buffers via the Callback#onInputBufferAvailable MediaCodec.Callback.onInput
/Callback#onOutputBufferAvailable OutputBufferAvailable(…)
callbacks.
Upon obtaining an input buffer, fill it with data and submit it to the codec using #queueInputBuffer queueInputBuffer
– or #queueSecureInputBuffer queueSecureInputBuffer
if using decryption. Do not submit multiple input buffers with the same timestamp (unless it is codec-specific data marked as such).
The codec in turn will return a read-only output buffer via the Callback#onOutputBufferAvailable onOutputBufferAvailable
callback in asynchronous mode, or in response to a #dequeueOutputBuffer dequeueOutputBuffer
call in synchronous mode. After the output buffer has been processed, call one of the #releaseOutputBuffer releaseOutputBuffer
methods to return the buffer to the codec.
While you are not required to resubmit/release buffers immediately to the codec, holding onto input and/or output buffers may stall the codec, and this behavior is device dependent. <strong>Specifically, it is possible that a codec may hold off on generating output buffers until <em>all</em> outstanding buffers have been released/resubmitted.</strong> Therefore, try to hold onto to available buffers as little as possible.
Depending on the API version, you can process data in three ways: <table> <thead> <tr> <th>Processing Mode</th> <th>API version <= 20<br>Jelly Bean/KitKat</th> <th>API version >= 21<br>Lollipop and later</th> </tr> </thead> <tbody> <tr> <td>Synchronous API using buffer arrays</td> <td>Supported</td> <td>Deprecated</td> </tr> <tr> <td>Synchronous API using buffers</td> <td class=NA>Not Available</td> <td>Supported</td> </tr> <tr> <td>Asynchronous API using buffers</td> <td class=NA>Not Available</td> <td>Supported</td> </tr> </tbody> </table>
<h4>Asynchronous Processing using Buffers</h4>
Since android.os.Build.VERSION_CODES#LOLLIPOP
, the preferred method is to process data asynchronously by setting a callback before calling #configure configure
. Asynchronous mode changes the state transitions slightly, because you must call #start
after #flush
to transition the codec to the Running sub-state and start receiving input buffers. Similarly, upon an initial call to start
the codec will move directly to the Running sub-state and start passing available input buffers via the callback.
<center> <img src="../../../images/media/mediacodec_async_states.svg" style="width: 516px; height: 353px" alt="MediaCodec state diagram for asynchronous operation"> </center>
MediaCodec is typically used like this in asynchronous mode:
MediaCodec codec = MediaCodec.createByCodecName(name);
MediaFormat mOutputFormat; // member variable
codec.setCallback(new MediaCodec.Callback() {
{@literal @Override}
void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
// fill inputBuffer with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
{@literal @Override}
void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is equivalent to mOutputFormat
// outputBuffer is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
}
{@literal @Override}
void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
mOutputFormat = format; // option B
}
{@literal @Override}
void onError(…) {
…
}
{@literal @Override}
void onCryptoError(…) {
…
}
});
codec.configure(format, …);
mOutputFormat = codec.getOutputFormat(); // option B
codec.start();
// wait for processing to complete
codec.stop();
codec.release();
<h4>Synchronous Processing using Buffers</h4>
Since android.os.Build.VERSION_CODES#LOLLIPOP
, you should retrieve input and output buffers using #getInputBuffer getInput
/#getOutputBuffer OutputBuffer(int)
and/or #getInputImage getInput
/#getOutputImage OutputImage(int)
even when using the codec in synchronous mode. This allows certain optimizations by the framework, e.g. when processing dynamic content. This optimization is disabled if you call #getInputBuffers getInput
/#getOutputBuffers OutputBuffers()
.
<p class=note> <strong>Note:</strong> do not mix the methods of using buffers and buffer arrays at the same time. Specifically, only call getInput
/OutputBuffers
directly after #start
or after having dequeued an output buffer ID with the value of #INFO_OUTPUT_FORMAT_CHANGED
.
MediaCodec is typically used like this in synchronous mode:
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
MediaFormat outputFormat = codec.getOutputFormat(); // option B
codec.start();
for (;;) {
int inputBufferId = codec.dequeueInputBuffer(timeoutUs);
if (inputBufferId >= 0) {
ByteBuffer inputBuffer = codec.getInputBuffer(…);
// fill inputBuffer with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
// bufferFormat is identical to outputFormat
// outputBuffer is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
// Can ignore if using getOutputFormat(outputBufferId)
outputFormat = codec.getOutputFormat(); // option B
}
}
codec.stop();
codec.release();
<h4>Synchronous Processing using Buffer Arrays (deprecated)</h4>
In versions android.os.Build.VERSION_CODES#KITKAT_WATCH
and before, the set of input and output buffers are represented by the ByteBuffer[]
arrays. After a successful call to #start
, retrieve the buffer arrays using #getInputBuffers getInput
/#getOutputBuffers OutputBuffers()
. Use the buffer ID-s as indices into these arrays (when non-negative), as demonstrated in the sample below. Note that there is no inherent correlation between the size of the arrays and the number of input and output buffers used by the system, although the array size provides an upper bound.
MediaCodec codec = MediaCodec.createByCodecName(name);
codec.configure(format, …);
codec.start();
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
for (;;) {
int inputBufferId = codec.dequeueInputBuffer(…);
if (inputBufferId >= 0) {
// fill inputBuffers[inputBufferId] with valid data
…
codec.queueInputBuffer(inputBufferId, …);
}
int outputBufferId = codec.dequeueOutputBuffer(…);
if (outputBufferId >= 0) {
// outputBuffers[outputBufferId] is ready to be processed or rendered.
…
codec.releaseOutputBuffer(outputBufferId, …);
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
outputBuffers = codec.getOutputBuffers();
} else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
// Subsequent data will conform to new format.
MediaFormat format = codec.getOutputFormat();
}
}
codec.stop();
codec.release();
<h4>End-of-stream Handling</h4>
When you reach the end of the input data, you must signal it to the codec by specifying the #BUFFER_FLAG_END_OF_STREAM
flag in the call to #queueInputBuffer queueInputBuffer
. You can do this on the last valid input buffer, or by submitting an additional empty input buffer with the end-of-stream flag set. If using an empty buffer, the timestamp will be ignored.
The codec will continue to return output buffers until it eventually signals the end of the output stream by specifying the same end-of-stream flag in the BufferInfo
set in #dequeueOutputBuffer dequeueOutputBuffer
or returned via Callback#onOutputBufferAvailable onOutputBufferAvailable
. This can be set on the last valid output buffer, or on an empty buffer after the last valid output buffer. The timestamp of such empty buffer should be ignored.
Do not submit additional input buffers after signaling the end of the input stream, unless the codec has been flushed, or stopped and restarted.
<h4>Using an Output Surface</h4>
The data processing is nearly identical to the ByteBuffer mode when using an output Surface
; however, the output buffers will not be accessible, and are represented as null
values. E.g. #getOutputBuffer getOutputBuffer
/#getOutputImage Image(int)
will return null
and #getOutputBuffers
will return an array containing only null
-s.
When using an output Surface, you can select whether or not to render each output buffer on the surface. You have three choices: <ul> <li><strong>Do not render the buffer:</strong> Call #releaseOutputBuffer(int, boolean) releaseOutputBuffer(bufferId, false)
.</li> <li><strong>Render the buffer with the default timestamp:</strong> Call #releaseOutputBuffer(int, boolean) releaseOutputBuffer(bufferId, true)
.</li> <li><strong>Render the buffer with a specific timestamp:</strong> Call #releaseOutputBuffer(int, long) releaseOutputBuffer(bufferId, timestamp)
.</li> </ul>
Since android.os.Build.VERSION_CODES#M
, the default timestamp is the BufferInfo#presentationTimeUs presentation timestamp of the buffer (converted to nanoseconds). It was not defined prior to that.
Also since android.os.Build.VERSION_CODES#M
, you can change the output Surface dynamically using #setOutputSurface setOutputSurface
.
When rendering output to a Surface, the Surface may be configured to drop excessive frames (that are not consumed by the Surface in a timely manner). Or it may be configured to not drop excessive frames. In the latter mode if the Surface is not consuming output frames fast enough, it will eventually block the decoder. Prior to android.os.Build.VERSION_CODES#Q
the exact behavior was undefined, with the exception that View surfaces (SurfaceView or TextureView) always dropped excessive frames. Since android.os.Build.VERSION_CODES#Q
the default behavior is to drop excessive frames. Applications can opt out of this behavior for non-View surfaces (such as ImageReader or SurfaceTexture) by targeting SDK android.os.Build.VERSION_CODES#Q
and setting the key MediaFormat#KEY_ALLOW_FRAME_DROP
to 0
in their configure format.
<h4>Transformations When Rendering onto Surface</h4>
If the codec is configured into Surface mode, any crop rectangle, MediaFormat#KEY_ROTATION rotation and #setVideoScalingMode video scaling mode will be automatically applied with one exception: <p class=note> Prior to the android.os.Build.VERSION_CODES#M
release, software decoders may not have applied the rotation when being rendered onto a Surface. Unfortunately, there is no standard and simple way to identify software decoders, or if they apply the rotation other than by trying it out.
There are also some caveats. <p class=note> Note that the pixel aspect ratio is not considered when displaying the output onto the Surface. This means that if you are using #VIDEO_SCALING_MODE_SCALE_TO_FIT
mode, you must position the output Surface so that it has the proper final display aspect ratio. Conversely, you can only use #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
mode for content with square pixels (pixel aspect ratio or 1:1). <p class=note> Note also that as of android.os.Build.VERSION_CODES#N
release, #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
mode may not work correctly for videos rotated by 90 or 270 degrees. <p class=note> When setting the video scaling mode, note that it must be reset after each time the output buffers change. Since the #INFO_OUTPUT_BUFFERS_CHANGED
event is deprecated, you can do this after each time the output format changes.
<h4>Using an Input Surface</h4>
When using an input Surface, there are no accessible input buffers, as buffers are automatically passed from the input surface to the codec. Calling #dequeueInputBuffer dequeueInputBuffer
will throw an IllegalStateException
, and #getInputBuffers
returns a bogus ByteBuffer[]
array that <strong>MUST NOT</strong> be written into.
Call #signalEndOfInputStream
to signal end-of-stream. The input surface will stop submitting data to the codec immediately after this call.
<h3>Seeking & Adaptive Playback Support</h3>
Video decoders (and in general codecs that consume compressed video data) behave differently regarding seek and format change whether or not they support and are configured for adaptive playback. You can check if a decoder supports CodecCapabilities#FEATURE_AdaptivePlayback adaptive playback via CodecCapabilities#isFeatureSupported CodecCapabilities.isFeatureSupported(String)
. Adaptive playback support for video decoders is only activated if you configure the codec to decode onto a Surface
.
<h4 id=KeyFrames>"KeyFrames">Stream Boundary and Key Frames</h4>
It is important that the input data after #start
or #flush
starts at a suitable stream boundary: the first frame must be a key frame. A <em>key frame</em> can be decoded completely on its own (for most codecs this means an I-frame), and no frames that are to be displayed after a key frame refer to frames before the key frame.
The following table summarizes suitable key frames for various video formats. <table> <thead> <tr> <th>Format</th> <th>Suitable key frame</th> </tr> </thead> <tbody class=mid> <tr> <td>VP9/VP8</td> <td>a suitable intraframe where no subsequent frames refer to frames prior to this frame.<br> (There is no specific name for such key frame.)</td> </tr> <tr> <td>H.265 HEVC</td> <td>IDR or CRA</td> </tr> <tr> <td>H.264 AVC</td> <td>IDR</td> </tr> <tr> <td>MPEG-4<br>H.263<br>MPEG-2</td> <td>a suitable I-frame where no subsequent frames refer to frames prior to this frame.<br> (There is no specific name for such key frame.)</td> </tr> </tbody> </table>
<h4>For decoders that do not support adaptive playback (including when not decoding onto a Surface)</h4>
In order to start decoding data that is not adjacent to previously submitted data (i.e. after a seek) you <strong>MUST</strong> flush the decoder. Since all output buffers are immediately revoked at the point of the flush, you may want to first signal then wait for the end-of-stream before you call flush
. It is important that the input data after a flush starts at a suitable stream boundary/key frame. <p class=note> <strong>Note:</strong> the format of the data submitted after a flush must not change; #flush
does not support format discontinuities; for that, a full #stop
- #configure configure(…)
- #start
cycle is necessary.
<p class=note> <strong>Also note:</strong> if you flush the codec too soon after #start
– generally, before the first output buffer or output format change is received – you will need to resubmit the codec-specific-data to the codec. See the codec-specific-data section for more info.
<h4>For decoders that support and are configured for adaptive playback</h4>
In order to start decoding data that is not adjacent to previously submitted data (i.e. after a seek) it is <em>not necessary</em> to flush the decoder; however, input data after the discontinuity must start at a suitable stream boundary/key frame.
For some video formats - namely H.264, H.265, VP8 and VP9 - it is also possible to change the picture size or configuration mid-stream. To do this you must package the entire new codec-specific configuration data together with the key frame into a single buffer (including any start codes), and submit it as a <strong>regular</strong> input buffer.
You will receive an #INFO_OUTPUT_FORMAT_CHANGED
return value from #dequeueOutputBuffer dequeueOutputBuffer
or a Callback#onOutputBufferAvailable onOutputFormatChanged
callback just after the picture-size change takes place and before any frames with the new size have been returned. <p class=note> <strong>Note:</strong> just as the case for codec-specific data, be careful when calling #flush
shortly after you have changed the picture size. If you have not received confirmation of the picture size change, you will need to repeat the request for the new picture size.
<h3>Error handling</h3>
The factory methods #createByCodecName createByCodecName
and #createDecoderByType createDecoder
/#createEncoderByType EncoderByType
throw IOException
on failure which you must catch or declare to pass up. MediaCodec methods throw IllegalStateException
when the method is called from a codec state that does not allow it; this is typically due to incorrect application API usage. Methods involving secure buffers may throw CryptoException
, which has further error information obtainable from CryptoException#getErrorCode
.
Internal codec errors result in a CodecException
, which may be due to media content corruption, hardware failure, resource exhaustion, and so forth, even when the application is correctly using the API. The recommended action when receiving a CodecException
can be determined by calling CodecException#isRecoverable
and CodecException#isTransient
: <ul> <li><strong>recoverable errors:</strong> If isRecoverable()
returns true, then call #stop
, #configure configure(…)
, and #start
to recover.</li> <li><strong>transient errors:</strong> If isTransient()
returns true, then resources are temporarily unavailable and the method may be retried at a later time.</li> <li><strong>fatal errors:</strong> If both isRecoverable()
and isTransient()
return false, then the CodecException
is fatal and the codec must be #reset reset or #release released.</li> </ul>
Both isRecoverable()
and isTransient()
do not return true at the same time.
<h2 id=History>"History">Valid API Calls and API History</h2>
This sections summarizes the valid API calls in each state and the API history of the MediaCodec class. For API version numbers, see android.os.Build.VERSION_CODES
.
<style> .api > tr > th, .api > tr > td { text-align: center; padding: 4px 4px; } .api > tr > th { vertical-align: bottom; } .api > tr > td { vertical-align: middle; } .sml > tr > th, .sml > tr > td { text-align: center; padding: 2px 4px; } .fn { text-align: left; } .fn > code > a { font: 14px/19px Roboto Condensed, sans-serif; } .deg45 { white-space: nowrap; background: none; border: none; vertical-align: bottom; width: 30px; height: 83px; } .deg45 > div { transform: skew(-45deg, 0deg) translate(1px, -67px); transform-origin: bottom left 0; width: 30px; height: 20px; } .deg45 > div > div { border: 1px solid #ddd; background: #999; height: 90px; width: 42px; } .deg45 > div > div > div { transform: skew(45deg, 0deg) translate(-55px, 55px) rotate(-45deg); }</style>
<table align="right" style="width: 0%"> <thead> <tr><th>Symbol</th><th>Meaning</th></tr> </thead> <tbody class=sml> <tr><td>●</td><td>Supported</td></tr> <tr><td>⁕</td><td>Semantics changed</td></tr> <tr><td>○</td><td>Experimental support</td></tr> <tr><td>[ ]</td><td>Deprecated</td></tr> <tr><td>⎋</td><td>Restricted to surface input mode</td></tr> <tr><td>⎆</td><td>Restricted to surface output mode</td></tr> <tr><td>▧</td><td>Restricted to ByteBuffer input mode</td></tr> <tr><td>↩</td><td>Restricted to synchronous mode</td></tr> <tr><td>⇄</td><td>Restricted to asynchronous mode</td></tr> <tr><td>( )</td><td>Can be called, but shouldn't</td></tr> </tbody> </table>
<table style="width: 100%;"> <thead class=api> <tr> <th class=deg45><div><div style="background:#4285f4"><div>Uninitialized</div></div></div></th> <th class=deg45><div><div style="background:#f4b400"><div>Configured</div></div></div></th> <th class=deg45><div><div style="background:#e67c73"><div>Flushed</div></div></div></th> <th class=deg45><div><div style="background:#0f9d58"><div>Running</div></div></div></th> <th class=deg45><div><div style="background:#f7cb4d"><div>End of Stream</div></div></div></th> <th class=deg45><div><div style="background:#db4437"><div>Error</div></div></div></th> <th class=deg45><div><div style="background:#666"><div>Released</div></div></div></th> <th></th> <th colspan="8">SDK Version</th> </tr> <tr> <th colspan="7">State</th> <th>Method</th> <th>16</th> <th>17</th> <th>18</th> <th>19</th> <th>20</th> <th>21</th> <th>22</th> <th>23</th> </tr> </thead> <tbody class=api> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class=fn>#createByCodecName createByCodecName
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class=fn>#createDecoderByType createDecoderByType
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class=fn>#createEncoderByType createEncoderByType
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td class=fn>#createPersistentInputSurface createPersistentInputSurface
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>●</td> </tr> <tr> <td>16+</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td class=fn>#configure configure
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>⁕</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>18+</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td class=fn>#createInputSurface createInputSurface
</td> <td></td> <td></td> <td>⎋</td> <td>⎋</td> <td>⎋</td> <td>⎋</td> <td>⎋</td> <td>⎋</td> </tr> <tr> <td>-</td> <td>-</td> <td>16+</td> <td>16+</td> <td>(16+)</td> <td>-</td> <td>-</td> <td class=fn>#dequeueInputBuffer dequeueInputBuffer
</td> <td>●</td> <td>●</td> <td>▧</td> <td>▧</td> <td>▧</td> <td>⁕▧↩</td> <td>▧↩</td> <td>▧↩</td> </tr> <tr> <td>-</td> <td>-</td> <td>16+</td> <td>16+</td> <td>16+</td> <td>-</td> <td>-</td> <td class=fn>#dequeueOutputBuffer dequeueOutputBuffer
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>⁕↩</td> <td>↩</td> <td>↩</td> </tr> <tr> <td>-</td> <td>-</td> <td>16+</td> <td>16+</td> <td>16+</td> <td>-</td> <td>-</td> <td class=fn>#flush flush
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>18+</td> <td>18+</td> <td>18+</td> <td>18+</td> <td>18+</td> <td>18+</td> <td>-</td> <td class=fn>#getCodecInfo getCodecInfo
</td> <td></td> <td></td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>(21+)</td> <td>21+</td> <td>(21+)</td> <td>-</td> <td>-</td> <td class=fn>#getInputBuffer getInputBuffer
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>16+</td> <td>(16+)</td> <td>(16+)</td> <td>-</td> <td>-</td> <td class=fn>#getInputBuffers getInputBuffers
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>[⁕↩]</td> <td>[↩]</td> <td>[↩]</td> </tr> <tr> <td>-</td> <td>21+</td> <td>(21+)</td> <td>(21+)</td> <td>(21+)</td> <td>-</td> <td>-</td> <td class=fn>#getInputFormat getInputFormat
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>(21+)</td> <td>21+</td> <td>(21+)</td> <td>-</td> <td>-</td> <td class=fn>#getInputImage getInputImage
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>○</td> <td>●</td> <td>●</td> </tr> <tr> <td>18+</td> <td>18+</td> <td>18+</td> <td>18+</td> <td>18+</td> <td>18+</td> <td>-</td> <td class=fn>#getName getName
</td> <td></td> <td></td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>(21+)</td> <td>21+</td> <td>21+</td> <td>-</td> <td>-</td> <td class=fn>#getOutputBuffer getOutputBuffer
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>16+</td> <td>16+</td> <td>16+</td> <td>-</td> <td>-</td> <td class=fn>#getOutputBuffers getOutputBuffers
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>[⁕↩]</td> <td>[↩]</td> <td>[↩]</td> </tr> <tr> <td>-</td> <td>21+</td> <td>16+</td> <td>16+</td> <td>16+</td> <td>-</td> <td>-</td> <td class=fn>#getOutputFormat()
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>(21+)</td> <td>21+</td> <td>21+</td> <td>-</td> <td>-</td> <td class=fn>#getOutputFormat(int)
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>(21+)</td> <td>21+</td> <td>21+</td> <td>-</td> <td>-</td> <td class=fn>#getOutputImage getOutputImage
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>○</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>-</td> <td>16+</td> <td>(16+)</td> <td>-</td> <td>-</td> <td class=fn>#queueInputBuffer queueInputBuffer
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>⁕</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>-</td> <td>16+</td> <td>(16+)</td> <td>-</td> <td>-</td> <td class=fn>#queueSecureInputBuffer queueSecureInputBuffer
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>⁕</td> <td>●</td> <td>●</td> </tr> <tr> <td>16+</td> <td>16+</td> <td>16+</td> <td>16+</td> <td>16+</td> <td>16+</td> <td>16+</td> <td class=fn>#release release
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>-</td> <td>16+</td> <td>16+</td> <td>-</td> <td>-</td> <td class=fn>#releaseOutputBuffer(int, boolean)
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>⁕</td> <td>●</td> <td>⁕</td> </tr> <tr> <td>-</td> <td>-</td> <td>-</td> <td>21+</td> <td>21+</td> <td>-</td> <td>-</td> <td class=fn>#releaseOutputBuffer(int, long)
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>⎆</td> <td>⎆</td> <td>⎆</td> </tr> <tr> <td>21+</td> <td>21+</td> <td>21+</td> <td>21+</td> <td>21+</td> <td>21+</td> <td>-</td> <td class=fn>#reset reset
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>21+</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td class=fn>#setCallback(Callback) setCallback
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>●</td> <td>●</td> <td>#setCallback(Callback, Handler) ⁕
</td> </tr> <tr> <td>-</td> <td>23+</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td class=fn>#setInputSurface setInputSurface
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>⎋</td> </tr> <tr> <td>23+</td> <td>23+</td> <td>23+</td> <td>23+</td> <td>23+</td> <td>(23+)</td> <td>(23+)</td> <td class=fn>#setOnFrameRenderedListener setOnFrameRenderedListener
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>○ ⎆</td> </tr> <tr> <td>-</td> <td>23+</td> <td>23+</td> <td>23+</td> <td>23+</td> <td>-</td> <td>-</td> <td class=fn>#setOutputSurface setOutputSurface
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td>⎆</td> </tr> <tr> <td>19+</td> <td>19+</td> <td>19+</td> <td>19+</td> <td>19+</td> <td>(19+)</td> <td>-</td> <td class=fn>#setParameters setParameters
</td> <td></td> <td></td> <td></td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>(16+)</td> <td>(16+)</td> <td>16+</td> <td>(16+)</td> <td>(16+)</td> <td>-</td> <td class=fn>#setVideoScalingMode setVideoScalingMode
</td> <td>⎆</td> <td>⎆</td> <td>⎆</td> <td>⎆</td> <td>⎆</td> <td>⎆</td> <td>⎆</td> <td>⎆</td> </tr> <tr> <td>(29+)</td> <td>29+</td> <td>29+</td> <td>29+</td> <td>(29+)</td> <td>(29+)</td> <td>-</td> <td class=fn>#setAudioPresentation setAudioPresentation
</td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> <tr> <td>-</td> <td>-</td> <td>18+</td> <td>18+</td> <td>-</td> <td>-</td> <td>-</td> <td class=fn>#signalEndOfInputStream signalEndOfInputStream
</td> <td></td> <td></td> <td>⎋</td> <td>⎋</td> <td>⎋</td> <td>⎋</td> <td>⎋</td> <td>⎋</td> </tr> <tr> <td>-</td> <td>16+</td> <td>21+(⇄)</td> <td>-</td> <td>-</td> <td>-</td> <td>-</td> <td class=fn>#start start
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>⁕</td> <td>●</td> <td>●</td> </tr> <tr> <td>-</td> <td>-</td> <td>16+</td> <td>16+</td> <td>16+</td> <td>-</td> <td>-</td> <td class=fn>#stop stop
</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> <td>●</td> </tr> </tbody> </table>
Java documentation for android.media.MediaCodec
.
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.
Fields
BufferFlagCodecConfig |
Obsolete.
This indicated that the buffer marked as such contains codec initialization / codec specific data instead of media data. |
BufferFlagDecodeOnly |
Obsolete.
This indicates that the buffer is decoded and updates the internal state of the decoder, but does not produce any output buffer. |
BufferFlagEndOfStream |
Obsolete.
This signals the end of stream, i. |
BufferFlagKeyFrame |
Obsolete.
This indicates that the (encoded) buffer marked as such contains the data for a key frame. |
BufferFlagPartialFrame |
Obsolete.
This indicates that the buffer only contains part of a frame, and the decoder should batch the data until a buffer without this flag appears before decoding the frame. |
BufferFlagSyncFrame |
Obsolete.
This indicates that the (encoded) buffer marked as such contains the data for a key frame. |
ConfigureFlagEncode |
Obsolete.
If this codec is to be used as an encoder, pass this flag. |
ConfigureFlagUseBlockModel |
Obsolete.
If this codec is to be used with |
ConfigureFlagUseCryptoAsync |
Obsolete.
This flag should be used on a secure decoder only. |
CryptoModeAesCbc | |
CryptoModeAesCtr | |
CryptoModeUnencrypted | |
InfoOutputBuffersChanged |
Obsolete.
The output buffers have changed, the client must refer to the new
set of output buffers returned by |
InfoOutputFormatChanged |
Obsolete.
The output format has changed, subsequent data will follow the new format. |
InfoTryAgainLater |
Obsolete.
If a non-negative timeout had been specified in the call
to |
ParameterKeyHdr10PlusInfo |
Set the HDR10+ metadata on the next queued input frame. |
ParameterKeyLowLatency |
Enable/disable low latency decoding mode. |
ParameterKeyOffsetTime |
Specify an offset (in micro-second) to be added on top of the timestamps onward. |
ParameterKeyRequestSyncFrame |
Request that the encoder produce a sync frame "soon". |
ParameterKeySuspend |
Temporarily suspend/resume encoding of input data. |
ParameterKeySuspendTime |
When |
ParameterKeyTunnelPeek |
Control video peek of the first frame when a codec is configured for tunnel mode with
|
ParameterKeyVideoBitrate |
Change a video encoder's target bitrate on the fly. |
VideoScalingModeScaleToFit |
Obsolete.
The content is scaled to the surface dimensions |
VideoScalingModeScaleToFitWithCropping |
Obsolete.
The content is scaled, maintaining its aspect ratio, the whole surface area is used, content may be cropped. |
Properties
CanonicalName |
Retrieve the underlying codec name. |
Class |
Returns the runtime class of this |
CodecInfo |
Get the codec info. |
Handle |
The handle to the underlying Android instance. (Inherited from Object) |
InputFormat |
Call this after |
JniIdentityHashCode | (Inherited from Object) |
JniPeerMembers | |
Metrics |
Return Metrics data about the current codec instance. |
Name |
Retrieve the codec name. |
OutputFormat |
Call this after dequeueOutputBuffer signals a format change by returning
|
PeerReference | (Inherited from Object) |
SupportedVendorParameters |
Returns a list of vendor parameter names. |
ThresholdClass |
This API supports the Mono for Android infrastructure and is not intended to be used directly from your code. (Inherited from Object) |
ThresholdType |
This API supports the Mono for Android infrastructure and is not intended to be used directly from your code. (Inherited from Object) |
Methods
Clone() |
Creates and returns a copy of this object. (Inherited from Object) |
Configure(MediaFormat, Surface, MediaCodecConfigFlags, MediaDescrambler) |
Configure a component to be used with a descrambler. |
Configure(MediaFormat, Surface, MediaCrypto, MediaCodecConfigFlags) |
Configures a component. |
CreateByCodecName(String) |
If you know the exact name of the component you want to instantiate use this method to instantiate it. |
CreateDecoderByType(String) |
Instantiate the preferred decoder supporting input data of the given mime type. |
CreateEncoderByType(String) |
Instantiate the preferred encoder supporting output data of the given mime type. |
CreateInputSurface() |
Requests a Surface to use as the input to an encoder, in place of input buffers. |
CreatePersistentInputSurface() |
Create a persistent input surface that can be used with codecs that normally have an input surface, such as video encoders. |
DequeueInputBuffer(Int64) |
Returns the index of an input buffer to be filled with valid data or -1 if no such buffer is currently available. |
DequeueOutputBuffer(MediaCodec+BufferInfo, Int64) |
Dequeue an output buffer, block at most "timeoutUs" microseconds. |
Dispose() | (Inherited from Object) |
Dispose(Boolean) | (Inherited from Object) |
Equals(Object) |
Indicates whether some other object is "equal to" this one. (Inherited from Object) |
Flush() |
Flush both input and output ports of the component. |
GetHashCode() |
Returns a hash code value for the object. (Inherited from Object) |
GetInputBuffer(Int32) |
Returns a |
GetInputBuffers() |
Obsolete.
Retrieve the set of input buffers. |
GetInputImage(Int32) |
Returns a writable Image object for a dequeued input buffer index to contain the raw input video frame. |
GetOutputBuffer(Int32) |
Returns a read-only ByteBuffer for a dequeued output buffer index. |
GetOutputBuffers() |
Obsolete.
Retrieve the set of output buffers. |
GetOutputFormat(Int32) |
Returns the output format for a specific output buffer. |
GetOutputFrame(Int32) |
Returns an |
GetOutputImage(Int32) |
Returns a read-only Image object for a dequeued output buffer index that contains the raw video frame. |
GetParameterDescriptor(String) |
Describe a parameter with the name. |
GetQueueRequest(Int32) |
Return a |
JavaFinalize() |
Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. (Inherited from Object) |
MapHardwareBuffer(HardwareBuffer) |
Map a |
Notify() |
Wakes up a single thread that is waiting on this object's monitor. (Inherited from Object) |
NotifyAll() |
Wakes up all threads that are waiting on this object's monitor. (Inherited from Object) |
QueueInputBuffer(Int32, Int32, Int32, Int64, MediaCodecBufferFlags) |
After filling a range of the input buffer at the specified index submit it to the component. |
QueueSecureInputBuffer(Int32, Int32, MediaCodec+CryptoInfo, Int64, MediaCodecBufferFlags) |
Similar to |
Release() |
Free up resources used by the codec instance. |
ReleaseOutputBuffer(Int32, Boolean) |
If you are done with a buffer, use this call to return the buffer to the codec or to render it on the output surface. |
ReleaseOutputBuffer(Int32, Int64) |
If you are done with a buffer, use this call to update its surface timestamp and return it to the codec to render it on the output surface. |
Reset() |
Returns the codec to its initial (Uninitialized) state. |
SetAudioPresentation(AudioPresentation) |
Sets the audio presentation. |
SetCallback(MediaCodec+Callback, Handler) |
Sets an asynchronous callback for actionable MediaCodec events on the default looper. |
SetCallback(MediaCodec+Callback) |
Sets an asynchronous callback for actionable MediaCodec events on the default looper. |
SetHandle(IntPtr, JniHandleOwnership) |
Sets the Handle property. (Inherited from Object) |
SetInputSurface(Surface) |
Configures the codec (e. |
SetOnFirstTunnelFrameReadyListener(Handler, MediaCodec+IOnFirstTunnelFrameReadyListener) |
Registers a callback to be invoked when the first output frame has been decoded
and is ready to be rendered on a codec configured for tunnel mode with |
SetOnFrameRenderedListener(MediaCodec+IOnFrameRenderedListener, Handler) |
Registers a callback to be invoked when an output frame is rendered on the output surface. |
SetOutputSurface(Surface) |
Dynamically sets the output surface of a codec. |
SetParameters(Bundle) |
Communicate additional parameter changes to the component instance. |
SetVideoScalingMode(VideoScalingMode) |
If a surface has been specified in a previous call to |
SignalEndOfInputStream() |
Signals end-of-stream on input. |
Start() |
After successfully configuring the component, call |
Stop() |
Finish the decode/encode session, note that the codec instance
remains active and ready to be |
SubscribeToVendorParameters(IList<String>) |
Subscribe to vendor parameters, so that these parameters will be present in
|
ToArray<T>() | (Inherited from Object) |
ToString() |
Returns a string representation of the object. (Inherited from Object) |
UnregisterFromRuntime() | (Inherited from Object) |
UnsubscribeFromVendorParameters(IList<String>) |
Unsubscribe from vendor parameters, so that these parameters will not be present in
|
Wait() |
Causes the current thread to wait until it is awakened, typically by being <em>notified</em> or <em>interrupted</em>. (Inherited from Object) |
Wait(Int64, Int32) |
Causes the current thread to wait until it is awakened, typically by being <em>notified</em> or <em>interrupted</em>, or until a certain amount of real time has elapsed. (Inherited from Object) |
Wait(Int64) |
Causes the current thread to wait until it is awakened, typically by being <em>notified</em> or <em>interrupted</em>, or until a certain amount of real time has elapsed. (Inherited from Object) |
Explicit Interface Implementations
IJavaPeerable.Disposed() | (Inherited from Object) |
IJavaPeerable.DisposeUnlessReferenced() | (Inherited from Object) |
IJavaPeerable.Finalized() | (Inherited from Object) |
IJavaPeerable.JniManagedPeerState | (Inherited from Object) |
IJavaPeerable.SetJniIdentityHashCode(Int32) | (Inherited from Object) |
IJavaPeerable.SetJniManagedPeerState(JniManagedPeerStates) | (Inherited from Object) |
IJavaPeerable.SetPeerReference(JniObjectReference) | (Inherited from Object) |
Extension Methods
JavaCast<TResult>(IJavaObject) |
Performs an Android runtime-checked type conversion. |
JavaCast<TResult>(IJavaObject) | |
GetJniTypeName(IJavaPeerable) |