MediaCodec 类

定义

MediaCodec 类可用于访问低级别媒体编解码器(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
继承
MediaCodec
属性

注解

MediaCodec 类可用于访问低级别媒体编解码器,即编码器/解码器组件。 它是 Android 低级多媒体支持基础结构的一部分(通常与 、、MediaSyncMediaMuxerImageMediaDrmMediaCryptoSurfaceAudioTrack.) 一MediaExtractor起使用。

<center><img src=“../../../images/media/mediacodec_buffers.svg“ style=”width: 540px;height: 205px“ alt=”MediaCodec 缓冲区流图“></center>

从广义上讲,编解码器处理输入数据以生成输出数据。 它以异步方式处理数据,并使用一组输入和输出缓冲区。 在简单级别,你请求(或接收)空输入缓冲区,填充数据并将其发送到编解码器进行处理。 编解码器使用数据并将其转换为其空输出缓冲区之一。 最后,请求(或接收)填充的输出缓冲区,使用其内容并将其释放回编解码器。

<h3 id=qualityFloor“qualityFloor>”>Video Encoding</h3 的最低质量地板>

android.os.Build.VERSION_CODES#S从 Android 的视频 MediaCodecs 开始,强制实施最低质量楼层。 目的是消除质量不佳的视频编码。 当编解码器处于可变比特率 (VBR) 模式时,将应用此质量下限;当编解码器处于常量比特率 (CBR) 模式时,它不适用。 质量楼层执法也仅限于特定大小范围:此大小范围目前用于大于 320x240 到 1920x1080 的视频分辨率。

当此质量楼层生效时,编解码器和支持框架代码将起作用,以确保生成的视频至少具有“公平”或“良好”质量。 用于选择这些目标的指标是 VMAF(视频多方法评估函数),针对所选测试序列的目标分数为 70。

典型的效果是,某些视频将生成比最初配置的比特率更高的比特率。 对于配置了非常低比特率的视频,这最值得注意;编解码器将使用比特率,该比特率确定更有可能生成“公平”或“良好”质量的视频。 另一种情况是视频包括非常复杂的内容(大量的动作和细节):在此类配置中,编解码器将根据需要使用额外的比特率,以避免丢失所有内容的更精细的详细信息。

此质量楼层不会影响以高比特率捕获的内容(高比特率应已为编解码器提供足够的容量来编码所有详细信息)。 质量楼层不对 CBR 编码进行操作。 质量楼层目前不以分辨率为 320x240 或更低,也不对分辨率高于 1920x1080 的视频进行操作。

<h3>数据类型</h3>

编解码器对三种类型的数据进行操作:压缩数据、原始音频数据和原始视频数据。 可以使用这三种类型的数据进行处理 ByteBuffer ByteBuffers,但应使用 Surface 原始视频数据来提高编解码器性能。 Surface 使用本机视频缓冲区,无需映射或将其复制到 ByteBuffers;因此,它的效率要高得多。 使用 Surface 时,通常无法访问原始视频数据,但你可以使用 ImageReader 该类访问不安全的解码(原始)视频帧。 这可能比使用 ByteBuffers 更有效,因为某些本机缓冲区可以映射到 ByteBuffer#isDirect direct ByteBuffers。 使用 ByteBuffer 模式时,可以使用类和 Image/#getInputImage getInput#getOutputImage OutputImage(int).

<h4>压缩缓冲区</h4>

输入缓冲区(对于解码器)和输出缓冲区(对于编码器)根据 MediaFormat#KEY_MIME 格式的类型包含压缩的数据。 对于视频类型,这通常是单个压缩的视频帧。 对于音频数据,这通常是一个访问单元(编码的音频段通常包含几毫秒的音频,如格式类型规定),但此要求稍有放宽,因为缓冲区可能包含多个编码的音频访问单元。 无论哪种情况,缓冲区都不会以任意字节边界开头或结束,而是在帧/访问单元边界上,除非它们被 #BUFFER_FLAG_PARTIAL_FRAME标记。

<h4>原始音频缓冲区</h4>

原始音频缓冲区包含 PCM 音频数据的整个帧,这是每个通道的一个示例,按通道顺序排列。 每个 PCM 音频示例都是 16 位有符号整数或浮点数,按本机字节顺序排列。 仅当 MediaFormat 的 MediaFormat#KEY_PCM_ENCODING 在 MediaCodec #configure configure(&hellip;) 期间设置为 AudioFormat#ENCODING_PCM_FLOAT,并为解码器或#getInputFormat编码器确认#getOutputFormat时,浮点 PCM 编码中的原始音频缓冲区才可能。 在 MediaFormat 中检查浮点 PCM 的示例方法如下所示:

static boolean isPcmFloat(MediaFormat format) {
               return format.getInteger(MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_16BIT)
                   == AudioFormat.ENCODING_PCM_FLOAT;
             }

为了在短数组中提取包含 16 位带符号整数音频数据的缓冲区的一个通道,可以使用以下代码:

// 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 &lt; 0 || channelIx &gt;= numChannels) {
                 return null;
               }
               short[] res = new short[samples.remaining() / numChannels];
               for (int i = 0; i &lt; res.length; ++i) {
                 res[i] = samples.get(i * numChannels + channelIx);
               }
               return res;
             }

<h4>原始视频缓冲区</h4>

在 ByteBuffer 模式下,视频缓冲区根据其 MediaFormat#KEY_COLOR_FORMAT 颜色格式布局。 可以从中获取支持的彩色格式作为数组#getCodecInfoCodecCapabilities#colorFormats colorFormats.MediaCodecInfo#getCapabilitiesForType getCapabilitiesForType(&hellip;).。 视频编解码器可能支持三种颜色格式:<ul<>li><强>本机原始视频格式:</strong> 这是标记的CodecCapabilities#COLOR_FormatSurface,它可用于输入或输出 Surface。</li li><<>强>灵活 YUV 缓冲区</强>(例如CodecCapabilities#COLOR_FormatYUV420Flexible):这些缓冲区可与输入/输出 Surface 以及 ByteBuffer 模式#getOutputImage OutputImage(int)#getInputImage getInput/结合使用。</li li>><<strong>other, specific formats:</strong> These are normally only supported in ByteBuffer mode. 某些颜色格式特定于供应商。 其他定义在 CodecCapabilities. 对于等效于灵活格式的颜色格式,仍 #getInputImage getInput/#getOutputImage OutputImage(int)可使用。</li></ul>

自此 android.os.Build.VERSION_CODES#LOLLIPOP_MR1,所有视频编解码器都支持灵活的 YUV 4:2:0 缓冲区。

<h4>在较旧设备上<访问原始视频字节缓冲区/h4>

android.os.Build.VERSION_CODES#LOLLIPOPImage在支持之前,需要使用MediaFormat#KEY_STRIDEMediaFormat#KEY_SLICE_HEIGHT输出格式值来了解原始输出缓冲区的布局。 <p class=注意> ,在某些设备上,切片高度播发为 0。 这可能意味着切片高度与帧高度相同,或者切片高度是与某些值(通常是 2 的幂)对齐的帧高度。 遗憾的是,在这种情况下,没有用于告知实际切片高度的标准和简单方法。 此外,平面的 U 垂直步幅也未指定或定义,但通常它是切片高度的一半。

MediaFormat#KEY_WIDTHMediaFormat#KEY_HEIGHT键指定视频帧的大小;但是,对于大多数连接,视频(图片)只占用视频帧的一部分。 这由“裁剪矩形”表示。

你需要使用以下键从 #getOutputFormat 输出格式获取原始输出图像的裁剪矩形。 如果这些键不存在,视频将占用整个视频帧。裁剪矩形在应用任何 MediaFormat#KEY_ROTATION旋转前<输出>帧 <em>的上下文中理解。 <table style=“width: 0%”>thead><tr<>>th Format Key</>><th th Type</<>>th description</th></tr></thead><tbody><tr><td><MediaFormat#KEY_CROP_LEFT/td><td td><<>> The left-coordinate (x) of the crop rectangle</td></tr<>><td/td td>>>MediaFormat#KEY_CROP_TOP<< Integer/<<td><td>裁剪矩形</td/tr<>><td<>/td td<>MediaFormat#KEY_CROP_RIGHT 整数</td><td>>><右坐标(x) <强减 1/强>>裁剪矩形</td><><<>/tr td td>><<MediaFormat#KEY_CROP_BOTTOM/td<<>><> 1<</strong>>裁剪矩形</td/tr><tr<>td>< colspan=3> 右坐标和底部坐标可以理解为裁剪输出图像最右有效列/最底部有效行的坐标。 </td></tr></tbody></table>

视频帧的大小(在旋转之前)可以计算如下:

MediaFormat format = decoder.getOutputFormat(&hellip;);
             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> 另请注意,不同设备的含义 BufferInfo#offset BufferInfo.offset 不一致。 在某些设备上,偏移量指向裁剪矩形的左上角像素,而在大多数设备上,它指向整个帧的左上角像素。

<h3>状态</h3>

在其生命周期中,编解码器在概念上存在于以下三种状态之一:已停止、执行或释放。 已停止的集体状态实际上是三种状态的分组:未初始化、已配置和错误,而执行状态在概念上通过三个子状态进行:Flushed、Running 和 End-of-Stream。

<center><img src=“../../../images/media/mediacodec_states.svg“ style=”width: 519px;height: 356px“ alt=”MediaCodec 状态图“></center>

使用工厂方法之一创建编解码器时,编解码器处于未初始化状态。 首先,需要通过 #configure configure(&hellip;)它进行配置,这会将它引入“已配置”状态,然后调用 #start 将其移动到“正在执行”状态。 在此状态下,可以通过上述缓冲区队列操作处理数据。

执行状态有三个子状态:Flushed、Running 和 End-of-Stream。 在编解码器处于 Flushed 子状态后 #start ,该编解码器紧邻其保存所有缓冲区的位置。 一旦取消排队第一个输入缓冲区,编解码器就会移动到“正在运行”子状态,在该状态下,它花费了大部分时间。 使用 #BUFFER_FLAG_END_OF_STREAM 流结束标记对输入缓冲区进行排队时,编解码器将转换为 Stream 结束子状态。 在此状态下,编解码器不再接受进一步的输入缓冲区,但仍会生成输出缓冲区,直到输出到达流结束为止。 对于解码器,可以在执行状态下使用 #flush时随时返回到 Flushed 子状态。 <p class=note><strong Note:</strong>> Back to Flushed state is only supported for decoders, may not work for encoders(行为未定义)。

调用 #stop 以将编解码器返回到未初始化状态,其中可能会再次配置该编解码器。 使用编解码器后,必须通过调用 #release释放它。

在极少数情况下,编解码器可能会遇到错误并移动到“错误”状态。 这是使用队列操作的无效返回值(有时通过异常)进行通信的。 调用 #reset 以使编解码器再次可用。 可以从任何状态调用它,以将编解码器移回未初始化状态。 否则,调用 #release 移动到终端“已发布”状态。

<h3>创建</h3>

用于 MediaCodecList 为特定 MediaFormat对象创建 MediaCodec。 解码文件或流时,可以从中获取所需的格式 MediaExtractor#getTrackFormat MediaExtractor.getTrackFormat。 注入要添加 MediaFormat#setFeatureEnabled MediaFormat.setFeatureEnabled的任何特定功能,然后调用 MediaCodecList#findDecoderForFormat MediaCodecList.findDecoderForFormat 以获取可处理该特定媒体格式的编解码器的名称。 最后,使用 #createByCodecName. 创建编解码器。 <p class=note><strong Note:</strong>> Onandroid.os.Build.VERSION_CODES#LOLLIPOP, the format to MediaCodecList.findDecoder/EncoderForFormat must not contain a MediaFormat#KEY_FRAME_RATE frame rate. 用于 format.setString(MediaFormat.KEY_FRAME_RATE, null) 清除格式中的任何现有帧速率设置。

还可以使用 #createDecoderByType createDecoder/#createEncoderByType EncoderByType(String) 为特定 MIME 类型创建首选编解码器。 但是,这不能用于注入功能,并且可能会创建无法处理特定所需媒体格式的编解码器。

<h4>创建安全解码器</h4>

在版本和更早版本 android.os.Build.VERSION_CODES#KITKAT_WATCH 上,安全编解码器可能不会列在系统中 MediaCodecList,但仍可在系统上使用。 只有名称才能实例化存在的安全编解码器,通过将常规编解码器的名称(所有安全编解码器的名称必须以 .结尾".secure"#createByCodecName追加".secure",如果系统上不存在编解码器,将引发IOException安全编解码器。

从此 android.os.Build.VERSION_CODES#LOLLIPOP 起,应使用 CodecCapabilities#FEATURE_SecurePlayback 媒体格式的功能来创建安全解码器。

<h3>初始化</h3>

创建编解码器后,如果需要异步处理数据,可以使用该回调 #setCallback setCallback 。 然后,#configure 使用特定媒体格式配置编解码器。 这是你可以为生成原始视频数据的视频制作者 – 编解码器(例如视频解码器)指定输出 Surface 时。 此外,还可以为安全编解码器设置解密参数(请参阅 MediaCrypto)。 最后,由于某些编解码器可以在多个模式下运行,因此必须指定是希望它作为解码器还是编码器工作。

因此 android.os.Build.VERSION_CODES#LOLLIPOP,可以查询结果的输入和输出格式(处于“已配置”状态)。 在启动编解码器之前,可以使用此方法来验证生成的配置,例如颜色格式。

如果要使用视频使用者和 ndash 以本机方式处理原始输入视频缓冲区;处理原始视频输入的编解码器,例如视频编码器 –使用 #createInputSurface 配置后为输入数据创建目标 Surface。 或者,通过调用 #setInputSurface将编解码器设置为使用以前创建的 #createPersistentInputSurface 持久输入图面。

<h4 id=CSD“CSD>”>编解码器特定的数据</h4>

某些格式(尤其是 AAC 音频和 MPEG4、H.264 和 H.265 视频格式)要求实际数据以包含设置数据或编解码器特定数据的缓冲区为前缀。 处理此类压缩格式时,必须在任何帧数据之后 #start 和之前将此数据提交到编解码器。 必须在调用#queueInputBuffer queueInputBuffer中使用标志#BUFFER_FLAG_CODEC_CONFIG标记此类数据。

编解码器特定的数据也可以包含在传递给 #configure configure ByteBuffer 条目的格式中,其键为“csd-0”、“csd-1”等。这些键始终包含在从中获取的MediaExtractor#getTrackFormat MediaExtractor轨道MediaFormat中。 格式的编解码器特定数据会自动提交到编解码器; #start您 <强>不得</强> 显式提交此数据。 如果格式不包含编解码器特定数据,则可以根据格式要求,按正确的顺序选择使用指定数量的缓冲区进行提交。 对于 H.264 AVC,还可以连接所有特定于编解码器的数据,并将其作为单个编解码器配置缓冲区提交。

Android 使用以下特定于编解码器的数据缓冲区。 还需要以曲目格式设置这些设置,以便进行正确的 MediaMuxer 跟踪配置。 每个参数集和标记为 (<sup>*</sup>) 的编解码器特定数据节必须以起始代码 "\x00\x00\x00\x01"开头。

<样式>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 td>>Decoder-specific information from ESDS<sup*</sup>></td><td td class=NA>Not Used</td td><class=NA>Not Used</td></tr><tr><td>VORBIS</td<>td Td>Identification header</td><td>Setup header/td<>td td class=NA>Not Used</td<>/tr><<>td>OPUS</td td td Identification header<</td<<>>td>>Pre-skip in nanosecs<br> (unsigned 64-bit ByteOrder#nativeOrder native-order integer.<br> 这会替代标识标头中的预跳过值。</td td><td>Seek Pre-roll in nanosecs<br> (unsigned 64 位 ByteOrder#nativeOrder native-order integer.</td></tr><td><>FLAC</td<>td>“fLaC”,ASCII 中的 FLAC 流标记,<br> 后跟 STREAMINFO 块(强制元数据块),<br>(可选)后跟任意数量的其他元数据块</td<>td td 类=NA Not Used/td<>td td class=NA>>Not Used<</td></tr><tr><td>MPEG-4</td><td>从 ESDS sup*/sup>></td<>td td 类=NA>Not Used/td<>td td class=NA>Not Used<</td<>/tr><<>tr td>H.264 AVC</<>td td>SPS (序列参数集<sup*</sup>)</td<>td PPS (图片参数集<sup>*</sup>>)</td>td<><<class=NA>Not Used</td></tr><><td>H.265 HEVC</td td><>VPS (视频参数集 sup>*</sup>) +br SPS (序列参数集<sup*/sup>) +<<br>> PPS (图片参数集<<sup>*<</sup>>)</td><td td class=NA>Not Used</td td<>td class=NA>Not Used/<td></tr><<>td>VP9/td><td>VP9 <CodecPrivate Data (可选)</td<>td td class=NA>Not Used/td td td class=NA>Not Used<</td<><>/tr><<>tr td>AV1/td><AV1< AV1 AV1CodecConfigurationRecord Data (可选) </td><>class=NA>Not Used/<td><td 类=NA>Not Used</td></tr></tbody></table>

<p class=note><strong Note:</strong>> care be be flushed if the codec is immediateed or shortly after start, before any output buffer or output format change has returned, as the codec specific data may be lost the flush. 必须使用在此类刷新后标记 #BUFFER_FLAG_CODEC_CONFIG 的缓冲区重新提交数据,以确保正确的编解码器操作。

编码器(或生成压缩数据的编解码器)将在输出缓冲区中用 #BUFFER_FLAG_CODEC_CONFIG 编解码器配置标志标记的任何有效输出缓冲区之前创建并返回编解码器特定的数据。 包含编解码器特定数据的缓冲区没有有意义的时间戳。

<h3>数据处理</h3>

每个编解码器都维护一组输入和输出缓冲区,这些缓冲区由 API 调用中的缓冲区 ID 引用。 成功调用客户端后,既不具有 #start 输入和输出缓冲区。 在同步模式下,调用 #dequeueInputBuffer dequeueInput/#dequeueOutputBuffer OutputBuffer(&hellip;) 从编解码器获取输入或输出缓冲区(获取其所有权)。 在异步模式下,将通过回调自动接收可用缓冲区 Callback#onInputBufferAvailable MediaCodec.Callback.onInput/Callback#onOutputBufferAvailable OutputBufferAvailable(&hellip;)

获取输入缓冲区后,使用数据填充该缓冲区,并使用 &ndash 将其提交到 #queueInputBuffer queueInputBuffer 编解码器;或使用 #queueSecureInputBuffer queueSecureInputBuffer 解密。 不要提交具有相同时间戳的多个输入缓冲区(除非它是标记为此类的编解码器特定的数据)。

编解码器反过来将通过异步模式下的 Callback#onOutputBufferAvailable onOutputBufferAvailable 回调或响应同步模式下的 #dequeueOutputBuffer dequeueOutputBuffer 调用返回只读输出缓冲区。 处理输出缓冲区后,调用其中 #releaseOutputBuffer releaseOutputBuffer 一种方法将缓冲区返回到编解码器。

虽然无需立即将/释放缓冲区重新提交到编解码器,但保留输入和输出缓冲区可能会停止编解码器,并且此行为依赖于设备。 <具体而言>,在释放>/重新提交所有</em> 未完成的缓冲区之前<,编解码器可能会阻止生成输出缓冲区。</strong> 因此,请尝试尽可能少地保留到可用缓冲区。

根据 API 版本,可以通过三种方式处理数据:<使用缓冲区数组/>><td td 支持</td 的缓冲区阵列</td td>><td 的表><tad><tr><th<> API 版本 <= 20<br>Jelly Bean/KitKat</th th>><API 版本 >= 21<br>Lollipop 和更高版本</th></tr<>/thead<>tbody<>><td>同步 API><td>Deprecated</td></tr><><td Sync API using buffers</td><td>class=NA>Not Available</td td>Supported</td><></tr><<>tr td>异步 API using buffers</td><class=NA>Not Available</><td td>Supported</td<>/tr<>/tbody></table>

<使用 Buffers/h4 进行< h4>异步处理>

因此 android.os.Build.VERSION_CODES#LOLLIPOP,首选方法是通过在调用 #configure configure之前设置回调来异步处理数据。 异步模式会稍微更改状态转换,因为必须调用 #start 之后 #flush 才能将编解码器转换为“正在运行”子状态并开始接收输入缓冲区。 同样,在对编解码器的初始调用 start 后,将直接移动到“正在运行”的子状态,并开始通过回调传递可用的输入缓冲区。

<center><img src=“../../../images/media/mediacodec_async_states.svg“ style=”width: 516px;height: 353px“ alt=”MediaCodec 状态图,用于异步操作“></center>

MediaCodec 通常用于异步模式中如下所示:

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
                 &hellip;
                 codec.queueInputBuffer(inputBufferId, &hellip;);
               }

               {@literal @Override}
               void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, &hellip;) {
                 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.
                 &hellip;
                 codec.releaseOutputBuffer(outputBufferId, &hellip;);
               }

               {@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(&hellip;) {
                 &hellip;
               }
               {@literal @Override}
               void onCryptoError(&hellip;) {
                 &hellip;
               }
             });
             codec.configure(format, &hellip;);
             mOutputFormat = codec.getOutputFormat(); // option B
             codec.start();
             // wait for processing to complete
             codec.stop();
             codec.release();

<使用 Buffers</h4 进行 h4>同步处理>

因此 android.os.Build.VERSION_CODES#LOLLIPOP,应在同步模式下使用编解码器时使用 #getInputBuffer getInput/#getOutputBuffer OutputBuffer(int) 和/或 #getInputImage getInput/#getOutputImage OutputImage(int) 甚至检索输入和输出缓冲区。 这允许框架进行某些优化,例如处理动态内容时。 如果调用 #getInputBuffers getInput/#getOutputBuffers OutputBuffers()此优化,则禁用此优化。

<p class=note><strong Note:</strong>> do not mix the methods of using buffers and buffer arrays at the same. 具体而言,仅在取消排队输出缓冲区 ID 的值之后或之后直接调用getInput。/OutputBuffers#start#INFO_OUTPUT_FORMAT_CHANGED

MediaCodec 通常用于同步模式中如下所示:

MediaCodec codec = MediaCodec.createByCodecName(name);
             codec.configure(format, &hellip;);
             MediaFormat outputFormat = codec.getOutputFormat(); // option B
             codec.start();
             for (;;) {
               int inputBufferId = codec.dequeueInputBuffer(timeoutUs);
               if (inputBufferId &gt;= 0) {
                 ByteBuffer inputBuffer = codec.getInputBuffer(&hellip;);
                 // fill inputBuffer with valid data
                 &hellip;
                 codec.queueInputBuffer(inputBufferId, &hellip;);
               }
               int outputBufferId = codec.dequeueOutputBuffer(&hellip;);
               if (outputBufferId &gt;= 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.
                 &hellip;
                 codec.releaseOutputBuffer(outputBufferId, &hellip;);
               } 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>同步处理(已弃用)</h4>

在版本 android.os.Build.VERSION_CODES#KITKAT_WATCH 和之前版本中,输入和输出缓冲区集由 ByteBuffer[] 数组表示。 成功调用 #start后,使用 #getInputBuffers getInput/#getOutputBuffers OutputBuffers() 检索缓冲区数组。 将缓冲区 ID-s 用作这些数组(非负数组时)中的索引,如以下示例所示。 请注意,数组大小与系统使用的输入和输出缓冲区数之间没有固有的关联,尽管数组大小提供上限。

MediaCodec codec = MediaCodec.createByCodecName(name);
             codec.configure(format, &hellip;);
             codec.start();
             ByteBuffer[] inputBuffers = codec.getInputBuffers();
             ByteBuffer[] outputBuffers = codec.getOutputBuffers();
             for (;;) {
               int inputBufferId = codec.dequeueInputBuffer(&hellip;);
               if (inputBufferId &gt;= 0) {
                 // fill inputBuffers[inputBufferId] with valid data
                 &hellip;
                 codec.queueInputBuffer(inputBufferId, &hellip;);
               }
               int outputBufferId = codec.dequeueOutputBuffer(&hellip;);
               if (outputBufferId &gt;= 0) {
                 // outputBuffers[outputBufferId] is ready to be processed or rendered.
                 &hellip;
                 codec.releaseOutputBuffer(outputBufferId, &hellip;);
               } 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>流结束处理</h4>

到达输入数据末尾时,必须通过在调用#queueInputBuffer queueInputBuffer中指定#BUFFER_FLAG_END_OF_STREAM标志来向编解码器发出信号。 可以在最后一个有效的输入缓冲区上执行此操作,或者通过设置流结束标志提交其他空输入缓冲区。 如果使用空缓冲区,将忽略时间戳。

编解码器将继续返回输出缓冲区,直到最终通过指定集中#dequeueOutputBuffer dequeueOutputBuffer的相同流结束标志BufferInfoCallback#onOutputBufferAvailable onOutputBufferAvailable向输出流的末尾发出信号。 这可以在最后一个有效输出缓冲区上设置,也可以在最后一个有效输出缓冲区之后的空缓冲区上设置。 应忽略此类空缓冲区的时间戳。

除非编解码器已刷新或停止并重新启动,否则在发出输入流的结束信号后,不要提交其他输入缓冲区。

<h4>使用输出 Surface</h4>

使用输出 Surface时,数据处理几乎与 ByteBuffer 模式完全相同;但是,输出缓冲区将无法访问,并且表示为 null 值。 例如 #getOutputBuffer getOutputBuffer/#getOutputImage Image(int) ,将返回 null#getOutputBuffers 返回仅 null包含 -s 的数组。

使用输出 Surface 时,可以选择是否在图面上呈现每个输出缓冲区。 你有三个选项:<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>

由于 android.os.Build.VERSION_CODES#M,默认时间戳是缓冲区的 BufferInfo#presentationTimeUs 表示时间戳(转换为 nanoseconds)。 它之前未定义。

android.os.Build.VERSION_CODES#M此外,还可以使用#setOutputSurface setOutputSurface动态更改输出 Surface。

将输出呈现到 Surface 时,Surface 可以配置为丢弃过多的帧(Surface 不会及时使用)。 或者,它可以配置为不丢弃过多的帧。 在后一模式下,如果 Surface 不够快地消耗输出帧,它最终将阻止解码器。 android.os.Build.VERSION_CODES#Q在未定义确切行为之前,视图图面(SurfaceView 或 TextureView)始终丢弃过多的帧例外。 由于 android.os.Build.VERSION_CODES#Q 默认行为是丢弃过多的帧。 应用程序可以通过面向 SDK android.os.Build.VERSION_CODES#Q 并将密钥 MediaFormat#KEY_ALLOW_FRAME_DROP 设置为 0 其配置格式来选择退出非视图图面(如 ImageReader 或 SurfaceTexture)的此行为。

<呈现到 Surface</h4 时的 h4>转换>

如果将编解码器配置为 Surface 模式,则任何裁剪矩形、MediaFormat#KEY_ROTATION 旋转和 #setVideoScalingMode 视频缩放模式都将自动应用一个例外:<发布之前 android.os.Build.VERSION_CODES#M p class=note>,在呈现到 Surface 上时,软件解码器可能尚未应用旋转。 遗憾的是,没有用于识别软件解码器的标准和简单方法,或者它们是否应用了轮换,而不是尝试它。

也有一些警告。 <p 类=注意> :在 Surface 上显示输出时,不考虑像素纵横比。 这意味着,如果使用 #VIDEO_SCALING_MODE_SCALE_TO_FIT 模式,则必须定位输出 Surface,使其具有正确的最终显示纵横比。 相反,只能对具有平方像素的内容使用 #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING 模式(像素纵横比或 1:1)。 <p class=note 另请注意> ,从发布开始 android.os.Build.VERSION_CODES#N#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING 模式可能无法正常用于旋转 90 度或 270 度的视频。 <p class=note> 设置视频缩放模式时,请注意,每次输出缓冲区更改后都必须重置它。 #INFO_OUTPUT_BUFFERS_CHANGED由于该事件已弃用,因此每次输出格式更改后,都可以执行此操作。

<h4>使用输入图面</h4>

使用输入 Surface 时,没有可访问的输入缓冲区,因为缓冲区会自动从输入图面传递到编解码器。 调用#dequeueInputBuffer dequeueInputBuffer将引发一个IllegalStateException,并#getInputBuffers返回强 MUST NOT</strong> 写入的虚假ByteBuffer[]数组<>。

调用 #signalEndOfInputStream 以发出流结束信号。 输入图面将在调用后立即停止向编解码器提交数据。

<h3>查找 &自适应播放支持</h3>

视频解码器(以及使用压缩视频数据的一般编解码器)在查找和格式更改方面的行为不同,无论它们是否支持并配置为自适应播放。 可以通过以下方式检查解码器是否支持 CodecCapabilities#FEATURE_AdaptivePlayback自适应播放 CodecCapabilities#isFeatureSupported CodecCapabilities.isFeatureSupported(String)。 仅当将编解码器配置为解码为 Surfacea 时,才会激活对视频解码器的自适应播放支持。

<h4 id=KeyFrames>“KeyFrames”>Stream Boundary and Key Frames</h4>

输入数据在合适的流边界后 #start#flush 从其开始非常重要:第一帧必须是关键帧。 <可以完全自行解码 em>关键帧</em>(对于大多数编解码器,这意味着 I 帧),关键帧后不会显示任何帧引用关键帧之前的帧。

下表汇总了适用于各种视频格式的关键帧。 <table>thead><tr><th>Format</><>th th Suitable key frame</th></tr></thead><tbody class=mid><tr><td>VP9/VP8</td td td><>a suitable intraframe, where no subsequent frames refer to frames prior the frame.<<br>(没有此类关键帧的特定名称。</td></tr><tr><td>H.265 HEVC</td td><>MN 或 CRA</td></tr><tr<>td>H.264 AVC</td Td TdMPEG</td>><</tr<<>>td>>MPEG-4<br H.263<br>>MPEG-2</td td td<>>a suitable I-frame,其中没有后续帧引用此帧之前的帧。<br>(没有此类关键帧的特定名称。</td></tr></tbody></table>

<h4>对于不支持自适应播放的解码器(包括未解码到 Surface 时)</h4>

为了开始解码之前提交的数据(即在查找后),强 <>MUST</strong> 刷新解码器。 由于所有输出缓冲区在刷新时立即撤销,因此可能需要先发出信号,然后在调用 flush之前等待流结束。 刷新后输入数据必须在合适的流边界/关键帧开始。 <p class=note>strong Note:</strong>> the format of the format of the data submitted after a flush must not change; not support format discontinuities; #flush for that, a full - #start#stop#configure configure(&hellip;) - cycle is must.<

<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 receivedash; you will need to resub the codec-specific-data to the codec. 有关详细信息,请参阅编解码器特定的数据部分。

<h4>对于支持和配置为自适应播放</h4 的解码器>

为了开始解码之前提交的数据(即在查找后),<>不需要</em> 刷新解码器;但是,不连续之后的输入数据必须从合适的流边界/关键帧开始。

对于某些视频格式(即 H.264、H.265、VP8 和 VP9),还可以更改图片大小或配置中流。 为此,必须将整个特定于编解码器的配置数据打包到单个缓冲区(包括任何起始代码)中,并将其作为 <强>常规</强> 输入缓冲区提交。

在图片大小更改发生之后以及返回具有新大小的任何帧之前,你将收到#INFO_OUTPUT_FORMAT_CHANGED来自或Callback#onOutputBufferAvailable onOutputFormatChanged回调的返回值#dequeueOutputBuffer dequeueOutputBuffer。 <p class=note><strong Note:</strong>> as the case for codec specific data, be be careful when callingly #flush after you have changed the picture size. 如果尚未收到图片大小更改的确认,则需要对新图片大小重复请求。

<h3>错误处理</h3>

工厂方法#createByCodecName createByCodecName#createEncoderByType EncoderByType#createDecoderByType createDecoder/引发IOException失败,必须捕获或声明要传递。 当从不允许它的编解码器状态调用该方法时,MediaCodec 方法将引发 IllegalStateException ;这通常是由于应用程序 API 用法不正确导致的。 涉及安全缓冲区的方法可能会引发 CryptoException,这具有可从中 CryptoException#getErrorCode获取的进一步错误信息。

内部编解码器错误会导致 CodecException媒体内容损坏、硬件故障、资源耗尽等,即使应用程序正确使用 API 也是如此。 接收 a CodecException 时的建议操作可以通过调用CodecException#isRecoverableCodecException#isTransient和 :<ul<>li><强>可恢复错误:</strong> 如果isRecoverable()返回 true,则调用#stop#configure configure(&hellip;)#start恢复。</li li>><<强>暂时性错误:</strong> 如果isTransient()返回 true,则资源暂时不可用,稍后可能会重试该方法。</li li>><<强>致命错误:</strong> 如果同时isRecoverable()isTransient()返回 false,则CodecException为致命错误,并且编解码器必须 #reset 重置或 #release 释放。</li></ul>

同时isRecoverable()isTransient()不返回 true。

<h2 id=History“History>”>Valid API Calls and API History</h2>

本部分总结了 MediaCodec 类的每个状态和 API 历史记录中的有效 API 调用。 有关 API 版本号,请参阅 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-aligned: 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; 高度: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 div > div { transform: skew(45deg, 0deg) translate(-55px, 55px) rotate(-45deg); }</风格>

<table align=“right” style=“width: 0%”><thead><tr><th>Symbol</th><>Meaning</th></tr/thead><tbody class=sml<>tr<>>< td>●</td><td>支持</td></tr><tr><td>⁕</td><td>语义已更改</td></tr><tr><td>○</td><td>实验支持</td></tr><><td>[ ]</td><td>Deprecated</td></tr tr><><td>⎋</td><td>限制为表面输入模式</td></tr tr><><td>⎆</td><td>Restricted to surface output mode</td/tr tr><><td><>▧</td><td>Restricted to ByteBuffer input mode</td/tr<>><td><>↩</td><td>限制为同步模式</td></tr<>tr><td>⇄</td><td>限制为异步模式</td></tr<>tr><td>()</td><td>可以调用,但不应</td></tr></tbody></table>

<table style=“width: 100%;”><thead class=api><tr><th class=deg45><div div><style=“background:#4285f4”>><div Uninitialized</><div/div></><th<>th class=deg45><div><div style=“background:#f4b400”><div>Configured</><div/><><div/th><class=deg45><div><div style=“background:#e67c73”><div>Flushed</><div/div><></th class><=deg45><div><div style=“background:#0f9d58”>><div Running</div></div></><th<>th class=deg45><><div div style=“background:#f7cb4d”><div>End of Stream</><div/><><div/th><class=deg45><div><div style=“background:#db4437”>><div Error</><div/><><div/th><th class=deg45><div div><style=“background:#666”div>><Released</div></div><></><><><th th colspan=“8”>SDK Version</th/tr tr><<>th>< colspan=“7”>State</th th method</>><><>th th 16</>><th 17/th 18<>></th 19<</<><>>>th 20</th 21<<>/th><>>22</th 23</><>th></tr/thead<>tbody class=api<>tr<><> 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 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><td class=fn><#createDecoderByType createDecoderByType/td td<>td>●</td><td td>●</td><td 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><td class=fn><#createEncoderByType createEncoderByType/td td<>td>●</td><td td>●</td><td 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 class=fn#createPersistentInputSurface createPersistentInputSurface<>/<>><<><>><><>><><><>><><<><<td td td/><td>●</td></tr><tr><td>16+</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 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 class=fn>#createInputSurface createInputSurface</><><td td td/><td td td><<>>⎋</td><td td>⎋</td><td td>⎋</td><td td>⎋</td><td td>⎋</td><td td>⎋</td></tr><<>td>-</td<>td-</td><td>> 16+</td td>16+</td<>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 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 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 td>●</td><td td>●</td><td td>●</td></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 td>●</td><td td>●</td></tr><><td>-</td><td-</td td><>>(21+)</td>><td 21+/td td<>td>(21+<)</<>td 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><<>td>-</<>td td-</td<>td>> 16+</td<>td>(16+)</td td td>(16+)</<>td><td>>-<<<>/><td class=fn><#getInputBuffers getInputBuffers/td<>td td>●</td><td 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-/<>td td class=fn><#getInputFormat getInputFormat/<><>><><td td td/td td/td><><><><><td></td><td>●</td><td td>●</td><td td>●</td></tr><<>td>-</td><td-</td td>><>(21+)</td>><td 21+</td td<>td>(21+)</<>td td>-><<</>><td class=fn#getInputImage getInputImage<>/><><td td td/<><>><td><>< tdtd></td><td></td td>><○</td><td td>●</td><td td>●</td></tr><<>td>18+</td<>td>18+</td<>td>18+</td<>td>18+</td td>18+/td><td 18+/td 18+<</>><<>>><<td class=fn<#getName getName>/><<>td td td/td><td>< 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 td>21+<</td<>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>●</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>[&⁕↩]</td><td>[&↩]</td><td>[&↩]</td></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 td>●</td><td td>●</td><td td>●</td></tr><tr><td>-</td><td-</td><td>>(21+)</td><td>21+/td td td>21+<</td<>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>●</td></tr><tr><td>-</td><td-</td><td>>(21+)</td><td>21+/td td td>21+<</td<>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>●</td></tr><tr><td>-</td><td-</td>>< td><<>> 16+</td><td td>(16+)</td 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 td>⁕</td><td td>●</td><td td>●</td></tr><tr><td>-</td><td-</td>>< td><<>> 16+</td><td td>(16+)</td 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 td>⁕</td><td td>●</td><td td>●</td></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 td>●</td><td td>●</td><td td>●</td></tr><tr><td>-</td><td>-</td><td><<>> 16+</td td>16+</td<>><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 td>⁕</td><td td>●</td><td td>⁕</td></tr>><<td>-</td><td-</td<>> td><<>> 21+</td td>21+/td><td td> 21+</td><td 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>⎆</td></tr>><<td>21+</td><td>21+</td><td>21+</td<>td>21+</td td>21+/td<>21+/td 21+<</>><<>>><<td class=fn<>#reset reset/<><>td 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 class=fn<#setCallback(Callback) setCallback>/><><td td td/<>td td/td td>><<><><td></td td><></td><td>●</td><td td>●</td><td/td><><#setCallback(Callback, Handler) &#8277;/tr><<>td-</td><td>> 23+</td<>td>-</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>><⎋</td></tr><<>td>23+</td><td>23+</td<>td>23+</td td>23+</td<>td>23+</><td<>td td>(23+)</>><td td td(23+)</td<>class=fn><#setOnFrameRenderedListener setOnFrameRenderedListener/><td td td/td><><><><td></td><td></<>td td></<>td<>td></td td td><><>○ ⎆</td></tr><><td>-</td><td>23+</td><td>23+</td td>23+/td<>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>><<td>19+</td><td>19+</td<>td>19+</td<>td>19+</td td>19+/>><td<>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><td td>●</td></tr><<>td>-</td><td>(16+)</td<>td>(16+)</td><td>16+</td td>(16+)</td<<>>> td(16+)</<>td 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 td>⎆</td><td td>⎆</td><td td>⎆</td></tr><><td>(29+)</td><td>29+</td><td>29+</td td>29+</td><<>td>(29+)</>><td td td(29+)</><td td td<>-/><td td class=fn#setAudioPresentation setAudioPresentation></><td td td/td>< td><><><td></td><td></td><td><><></td td/>><<td<>td></><td/tr><tr<>td>-/td td>-<</>><td<>td 18+/td 18+<</><>td td td-</><td td>-/td td><><><td>-</td><td 类=fn><#signalEndOfInputStream signalEndOfInputStream/td><td/td><<> td/td td><><>⎋</td><td td>⎋</td><td td>⎋</td><td td>⎋</td><td td>⎋</td><td td>⎋</td></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 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 td>●</td><td td>●</td><td td>●</td></tr></tbody></table>

适用于 . 的 android.media.MediaCodecJava 文档

本页的某些部分是根据 Android 开放源代码项目创建和共享的工作进行的修改,并根据 Creative Commons 2.5 属性许可证中所述的术语使用。

字段

BufferFlagCodecConfig
已过时.

这表示标记为此类的缓冲区包含编解码器初始化/编解码器特定数据,而不是媒体数据。

BufferFlagDecodeOnly
已过时.

这表示缓冲区已解码并更新解码器的内部状态,但不生成任何输出缓冲区。

BufferFlagEndOfStream
已过时.

这表示流结束,即 i.

BufferFlagKeyFrame
已过时.

这表示标记为的(编码)缓冲区包含关键帧的数据。

BufferFlagPartialFrame
已过时.

这表示缓冲区仅包含帧的一部分,解码器应对数据进行批处理,直到缓冲区未显示此标志,然后才对帧进行解码。

BufferFlagSyncFrame
已过时.

这表示标记为的(编码)缓冲区包含关键帧的数据。

ConfigureFlagEncode
已过时.

如果要将此编解码器用作编码器,请传递此标志。

ConfigureFlagUseBlockModel
已过时.

如果要将此编解码器用于 LinearBlock 和/或 HardwareBuffer,请传递此标志。

ConfigureFlagUseCryptoAsync
已过时.

此标志应仅用于安全解码器。

CryptoModeAesCbc
已过时.

MediaCodec 类可用于访问低级别媒体编解码器(i)。

CryptoModeAesCtr
CryptoModeUnencrypted
InfoOutputBuffersChanged
已过时.

输出缓冲区已更改,客户端必须引用从此点返回 #getOutputBuffers 的新输出缓冲区集。

InfoOutputFormatChanged
已过时.

输出格式已更改,后续数据将遵循新格式。

InfoTryAgainLater
已过时.

如果在调用 #dequeueOutputBuffer中指定了非负超时,则表示调用超时。

ParameterKeyHdr10PlusInfo

在下一个排队输入帧上设置 HDR10+ 元数据。

ParameterKeyLowLatency

启用/禁用低延迟解码模式。

ParameterKeyOffsetTime

指定要在时间戳之后添加的偏移量(以微秒为单位)。

ParameterKeyRequestSyncFrame

请求编码器“很快”生成同步帧。

ParameterKeySuspend

暂时暂停/恢复输入数据的编码。

ParameterKeySuspendTime

如果 #PARAMETER_KEY_SUSPEND 存在,客户端还可以选择使用此密钥来指定暂停/恢复操作生效的时间戳(以微秒为单位)。

ParameterKeyTunnelPeek

当编解码器配置为隧道模式时,控制第一帧的视频速览,同时MediaFormat#KEY_AUDIO_SESSION_IDAudioTrack暂停。

ParameterKeyVideoBitrate

动态更改视频编码器的目标比特率。

VideoScalingModeScaleToFit
已过时.

内容缩放到图面尺寸

VideoScalingModeScaleToFitWithCropping
已过时.

内容缩放,保持其纵横比,整个外围应用,内容可能会被裁剪。

属性

CanonicalName

检索基础编解码器名称。

Class

返回此 Object的运行时类。

(继承自 Object)
CodecInfo

获取编解码器信息。

Handle

基础 Android 实例的句柄。

(继承自 Object)
InputFormat

成功返回后 #configure 调用此代码,以获取编解码器接受的输入格式。

JniIdentityHashCode

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
JniPeerMembers

MediaCodec 类可用于访问低级别媒体编解码器(i)。

Metrics

返回有关当前编解码器实例的指标数据。

Name

检索编解码器名称。

OutputFormat

在 dequeueOutputBuffer 返回 #INFO_OUTPUT_FORMAT_CHANGED格式更改后调用此方法。

PeerReference

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
SupportedVendorParameters

返回供应商参数名称的列表。

ThresholdClass

此 API 支持 Mono for Android 基础结构,不打算直接从代码使用。

(继承自 Object)
ThresholdType

此 API 支持 Mono for Android 基础结构,不打算直接从代码使用。

(继承自 Object)

方法

Clone()

创建并返回此对象的副本。

(继承自 Object)
Configure(MediaFormat, Surface, MediaCodecConfigFlags, MediaDescrambler)

将组件配置为与 descrambler 一起使用。

Configure(MediaFormat, Surface, MediaCrypto, MediaCodecConfigFlags)

配置组件。

CreateByCodecName(String)

如果知道要实例化的组件的确切名称,请使用此方法对其进行实例化。

CreateDecoderByType(String)

实例化支持给定 mime 类型的输入数据的首选解码器。

CreateEncoderByType(String)

实例化支持给定 mime 类型的输出数据的首选编码器。

CreateInputSurface()

请求 Surface 用作编码器的输入,以代替输入缓冲区。

CreatePersistentInputSurface()

创建一个持久输入图面,该图面可用于通常具有输入图面的编解码器,例如视频编码器。

DequeueInputBuffer(Int64)

返回要用有效数据填充的输入缓冲区的索引;如果没有此类缓冲区当前可用,则返回 -1。

DequeueOutputBuffer(MediaCodec+BufferInfo, Int64)

取消输出缓冲区的排队,最多阻止“timeoutUs”微秒。

Dispose()

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
Dispose(Boolean)

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
Equals(Object)

指示其他对象是否“等于”此对象。

(继承自 Object)
Flush()

刷新组件的输入和输出端口。

GetHashCode()

返回对象的哈希代码值。

(继承自 Object)
GetInputBuffer(Int32)

返回一个 java.nio.Buffer#clear cleared可写的 ByteBuffer 对象,该对象用于取消排队的输入缓冲区索引以包含输入数据。

GetInputBuffers()
已过时.

检索输入缓冲区集。

GetInputImage(Int32)

返回已取消排队的输入缓冲区索引的可写图像对象,以包含原始输入视频帧。

GetOutputBuffer(Int32)

返回已取消排队输出缓冲区索引的只读 ByteBuffer。

GetOutputBuffers()
已过时.

检索输出缓冲区集。

GetOutputFormat(Int32)

返回特定输出缓冲区的输出格式。

GetOutputFrame(Int32)

返回一个 OutputFrame 对象。

GetOutputImage(Int32)

返回包含原始视频帧的已取消排队输出缓冲区索引的只读 Image 对象。

GetParameterDescriptor(String)

描述具有名称的参数。

GetQueueRequest(Int32)

返回输入 QueueRequest 槽索引的对象。

JavaFinalize()

当垃圾回收确定不再引用该对象时,由对象上的垃圾回收器调用。

(继承自 Object)
MapHardwareBuffer(HardwareBuffer)

HardwareBuffer 对象 Image映射到其中,以便可访问缓冲区的内容。

Notify()

唤醒正在等待此对象的监视器的单个线程。

(继承自 Object)
NotifyAll()

唤醒正在等待此对象的监视器的所有线程。

(继承自 Object)
QueueInputBuffer(Int32, Int32, Int32, Int64, MediaCodecBufferFlags)

在指定索引处填充输入缓冲区的范围后,将其提交到组件。

QueueSecureInputBuffer(Int32, Int32, MediaCodec+CryptoInfo, Int64, MediaCodecBufferFlags)

类似于 #queueInputBuffer queueInputBuffer 但提交可能加密的缓冲区。

Release()

释放编解码器实例使用的资源。

ReleaseOutputBuffer(Int32, Boolean)

如果使用缓冲区完成此操作,请使用此调用将缓冲区返回到编解码器或将其呈现在输出图面上。

ReleaseOutputBuffer(Int32, Int64)

如果使用缓冲区完成此操作,请使用此调用更新其图面时间戳,并将其返回到编解码器以在输出图面上呈现它。

Reset()

将编解码器返回到其初始(未初始化)状态。

SetAudioPresentation(AudioPresentation)

设置音频演示文稿。

SetCallback(MediaCodec+Callback)

为默认循环器上可操作的 MediaCodec 事件设置异步回调。

SetCallback(MediaCodec+Callback, Handler)

为默认循环器上可操作的 MediaCodec 事件设置异步回调。

SetHandle(IntPtr, JniHandleOwnership)

设置 Handle 属性。

(继承自 Object)
SetInputSurface(Surface)

配置编解码器(e.

SetOnFirstTunnelFrameReadyListener(Handler, MediaCodec+IOnFirstTunnelFrameReadyListener)

注册在解码第一个输出帧后要调用的回调,并准备好在配置为隧道模式的 KEY_AUDIO_SESSION_ID编解码器上呈现回调。

SetOnFrameRenderedListener(MediaCodec+IOnFrameRenderedListener, Handler)

注册在输出图面上呈现输出帧时要调用的回调。

SetOutputSurface(Surface)

动态设置编解码器的输出图面。

SetParameters(Bundle)

将其他参数更改传达给组件实例。

SetVideoScalingMode(VideoScalingMode)

如果在上一次调用中指定了图面,以 #configure 指定要使用的缩放模式。

SignalEndOfInputStream()

在输入时发出流结束信号。

Start()

成功配置组件后,调用 start

Stop()

完成解码/编码会话,请注意编解码器实例保持活动状态,并准备 #start再次运行。

SubscribeToVendorParameters(IList<String>)

订阅供应商参数,以便这些参数将出现在这些参数中 #getOutputFormat 并更改,从而生成输出格式更改事件。

ToArray<T>()

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
ToString()

返回对象的字符串表示形式。

(继承自 Object)
UnregisterFromRuntime()

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
UnsubscribeFromVendorParameters(IList<String>)

取消订阅供应商参数,使这些参数不存在, #getOutputFormat 并且这些参数的更改不再生成输出格式更改事件。

Wait()

使当前线程等待,直到唤醒它,通常是通过 em 通知/em> 或 <em>interrupted</em>。<><

(继承自 Object)
Wait(Int64)

使当前线程等待直到唤醒,通常是通过 <em>通知</em> 或 <em interrupted</em>>,或直到经过一定数量的实时。

(继承自 Object)
Wait(Int64, Int32)

使当前线程等待直到唤醒,通常是通过 <em>通知</em> 或 <em interrupted</em>>,或直到经过一定数量的实时。

(继承自 Object)

显式接口实现

IJavaPeerable.Disposed()

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
IJavaPeerable.DisposeUnlessReferenced()

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
IJavaPeerable.Finalized()

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
IJavaPeerable.JniManagedPeerState

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
IJavaPeerable.SetJniIdentityHashCode(Int32)

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
IJavaPeerable.SetJniManagedPeerState(JniManagedPeerStates)

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)
IJavaPeerable.SetPeerReference(JniObjectReference)

MediaCodec 类可用于访问低级别媒体编解码器(i)。

(继承自 Object)

扩展方法

JavaCast<TResult>(IJavaObject)

执行 Android 运行时检查的类型转换。

JavaCast<TResult>(IJavaObject)

MediaCodec 类可用于访问低级别媒体编解码器(i)。

GetJniTypeName(IJavaPeerable)

MediaCodec 类可用于访问低级别媒体编解码器(i)。

适用于