MediaCodec文档翻译

前言

之前我们所学习的音视频编解码全都是通过Android已经封装好的MediaRecorder实现的,以后真正在工作中我们很少使用MediaRecorder实现音视频的录制;因为我们接触不了音视频的原始数据,不能对它进行二次处理,比如美颜相机;如今直播的兴起,我们通过直播视频可以看到每一个直播女;我为什么不说直播美女呢!!!原因是我们所看到的视频并不是她们的真是面目;而是经过二次处理后的,我们所看到的视频是经过美白、瘦脸之后的视频,所以我们会发现每一个直播女又白又漂亮;现在就让我们一起来学习如何打好直播技术的基础知识吧!!!

MediaCodec

public final class MediaCodec extends Object

Java.lang.Object

  → android.media.MediaCodec

  MediaCodec类可用于访问Android底层的媒体编解码器,也就是,编码器/解码器组件。它是Android底层多媒体支持基本架构的一部分(通常与MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, 以及AudioTrack一起使用)。
  这里写图片描述
  从广义上讲,一个编解码器通过处理输入数据来产生输出数据。MediaCode采用异步方式处理数据,并且使用了一组输入输出缓存(buffer)。在简单的层面,你请求或接收到一个空的输入缓存(buffer),向其中填充满数据并将它传递给编解码器处理。编解码器处理完这些数据并将处理结果输出至一个空的输出缓存(buffer)中。最终,你请求或接收到一个填充了数据的输出缓存(buffer),使用完其中的数据,并将其释放回编解码器。

数据类型(Data Types)

  编解码器处理三种类型的数据:压缩数据、原始音频数据、原始视频数据。三种类型的数据均可以利用ByteBuffers进行处理,但是对于原始视频数据应提供一个Surface以提高编解码器的性能。Surface直接使用本地视频数据缓存,而没有映射或复制它们到ByteBuffers;因此,这种方式会更加高效。在使用Surface的时候,通常不能直接访问原始视频数据,但是可以使用ImageReader类来访问不可靠的解码后(或原始)的视频帧。这可能仍然比使用ByteBuffers更加高效,因为一些本地缓存可以被映射到 direct ByteBuffers。当使用ByteBuffer模式,你可以利用Image类和getInput/OutputImage(int)方法来访问到原始视频数据帧。

  压缩缓存(Compressed Buffers)

  输入缓存-buffers(用于解码器)和输出缓存-buffers(用于编码器)中包含由媒体格式类型决定的压缩数据。对于视频类型是单个压缩的视频帧。对于音频数据通常是单个可访问单元(一个编码的音频片段,通常包含几毫秒的遵循特定格式类型的音频数据),但这种要求也不是十分严格,一个缓存-buffer可能包含多个可访问的音频单元。在这两种情况下,缓存不会在任意的字节边界上开始或结束,而是在帧或可访问单元的边界上开始或结束。

  原始音频缓存(Raw Audio Buffers)

  输入缓存-buffers(用于解码器)和输出缓存-buffers(用于编码器)中包含由媒体格式类型决定的压缩数据。对于视频类型是单个压缩的视频帧。对于音频数据通常是单个可访问单元(一个编码的音频片段,通常包含几毫秒的遵循特定格式类型的音频数据),但这种要求也不是十分严格,一个缓存-buffer可能包含多个可访问的音频单元。在这两种情况下,缓存不会在任意的字节边界上开始或结束,而是在帧或可访问单元的边界上开始或结束。

  原始音频缓存(Raw Audio Buffers)

  原始的音频数据缓存(buffers)包含整个PCM(脉冲编码调制)音频数据帧,这是每一个通道按照通道顺序的一个样本。每一个样本是一个按照本机字节顺序的16位带符号整数(16-bit signed integer in native byte order)。
  

 1    short[] getSamplesForChannel(MediaCodec codec, int bufferId, int channelIx) {
 2   ByteBuffer outputBuffer = codec.getOutputBuffer(bufferId);
 3   MediaFormat format = codec.getOutputFormat(bufferId);
 4   ShortBuffer samples = outputBuffer.order(ByteOrder.nativeOrder()).asShortBuffer();
 5   int numChannels = formet.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
 6   if (channelIx < 0 || channelIx >= numChannels) {
 7     return null;
 8   }
 9   short[] res = new short[samples.remaining() / numChannels];
10   for (int i = 0; i < res.length; ++i) {
11     res[i] = samples.get(i * numChannels + channelIx);
12   }
13   return res;
14 }

  原始视频缓存(Raw Video Buffers)

  在ByteBuffer模式下,视频缓存-buffers根据它们的颜色格式(color format)进行展现。你可以通过调用getCodecInfo().getCapabilitiesForType(…).colorFormats方法获得其支持的颜色格式数组。视频编解码器可以支持三种类型的颜色格式:
  本地原始视频格式(native raw video format):这种格式被COLOR_FormatSurface标记,并可以与输入或输出Surface一起使用。
灵活的YUV缓存(flexible YUV buffers)(例如:COLOR_FormatYUV420Flexible):这些可以与输入/输出Surface一起使用,并且在ByteBuffer模式下,可以通过调用getInput/OutputImage(int)方法进行使用。
其他,特定的格式(other, specific formats):通常只在ByteBuffer模式下被支持。有些颜色格式是特定供应商指定的。其他的一些被定义在 MediaCodecInfo.CodecCapabilities中。颜色格式是一个很灵活的格式,你仍然可以使用 getInput/OutputImage(int)方法。
  从Android 5.1(LOLLIPOP_MR1)开始,所有的视频编解码器都支持灵活的YUV4:2:0缓存(flexible YUV 4:2:0 buffers)。

状态(States)

  在编解码器的生命周期内有三种理论状态:停止态-Stopped、执行态-Executing、释放态-Released,停止状态(Stopped)包括了三种子状态:未初始化(Uninitialized)、配置(Configured)、错误(Error)。执行状态(Executing)在概念上会经历三种子状态:刷新(Flushed)、运行(Running)、流结束(End-of-Stream)。
  这里写图片描述

  1、当你使用一种工厂方法创建了一个编解码器,此时编解码器处于未初始化状态(Uninitialized)。首先,你需要使用configure(…)方法对编解码器进行配置,这将使编解码器转为配置状态(Configured)。然后调用start()方法使其转入执行状态(Executing)。在这种状态下你可以通过上述的缓存队列操作处理数据。
  2、执行状态(Executing)包含三个子状态: 刷新(Flushed)、运行( Running) 以及流结束(End-of-Stream)。在调用start()方法后编解码器立即进入刷新子状态(Flushed),此时编解码器会拥有所有的缓存。一旦第一个输入缓存(input buffer)被移出队列,编解码器就转入运行子状态(Running),这种状态占据了编解码器的大部分生命周期。当你将一个带有end-of-stream marker标记的输入缓存入队列时,编解码器将转入流结束子状态(End-of-Stream)。在这种状态下,编解码器不再接收之后的输入缓存,但它仍然产生输出缓存(output buffers)直到输出到达end-of- stream标记输出。你可以在执行状态(Executing)的任何时候通过调用flush()方法返回到刷新子状态(Flushed)。
  3、通过调用stop()方法使编解码器返回到未初始化状态(Uninitialized),因此这个编解码器可以再次重新配置 。当你使用完编解码器后,你必须调用release()方法释放其资源。
  4、在极少情况下编解码器会遇到错误并进入错误状态(Error)。这个错误可能是在队列操作时返回一个错误的值或者有时候产生了一个异常导致的。通过调用 reset()方法使编解码器再次可用。你可以在任何状态调用reset()方法使编解码器返回到未初始化状态(Uninitialized)。否则,调用 release()方法进入最终的Released状态。
 

创建(Creation)

  使用MediaCodecList创建一个指定MediaFormat的MediaCodec实例。在解码文件或流时,你可以通过调用MediaExtractor.getTrackFormat方法获得所期望的格式。并调用MediaFormat.setFeatureEnabled方法注入任意你想要添加的特定特性,然后调用MediaCodecList.findDecoderForFormat方法获得可以处理指定的媒体格式的编解码器的名字。最后,通过调用createByCodecName(String)方法创建一个编解码器。

  注意:在Android 5.0 (LOLLIPOP)上,传递给MediaCodecList.findDecoder/EncoderForFormat的格式不能包含帧率-frame rate。通过调用format.setString(MediaFormat.KEY_FRAME_RATE, null)方法清除任何存在于当前格式中的帧率。

  你也可以利用createDecoder/EncoderByType(String)方法创建一个指定MIME类型的首选编解码器。然而,这种方式不能够给编解码器加入指定特性,而且创建的编解码器有可能不能处理具体期望的媒体格式

  创建安全的解码器(Creating secure decoders)

  在Android 4.4(KITKAT_WATCH)及之前版本,安全的编解码器没有被列在MediaCodecList中,但是仍然可以在系统中使用。存在的安全编解码器只能够通过名字进行实例化,其名字是在常规编解码器的名字后附加.secure标识(所有安全编解码器的名字都必须以.secure结尾),调用createByCodecName(String)方法创建安全编解码器时,如果系统中不存在指定名字的编解码器就会抛出IOException异常。

  从Android 5.0(LOLLIPOP)及之后版本,你可以在媒体格式中使用FEATURE_SecurePlayback属性来创建一个安全编解码器。

初始化(Initialization)

  在创建了编解码器后,如果你想异步地处理数据,可以通过调用setCallback方法设置一个回调方法。然后,使用指定的媒体格式配置编解码器。这段时间你可以为视频原始数据产生者(例如视频解码器)指定输出Surface。此时你也可以为secure 编解码器设置解码参数(详见MediaCrypto) 。最后,因为有些编解码器可以操作于多种模式,你必须指定是想让他作为一个解码器或编码器运行。

  从API LOLLIPOP起,你可以在Configured 状态查询输入和输出格式的结果。在开始编解码前你可以通过这个结果来验证配置的结果,例如,颜色格式。

  如果你想通过视频处理者处理原始输入视频buffers,一个处理原始视频输入的编解码器,例如视频编码器,在配置完成后通过调用createInputSurface()方法为你的输入数据创建一个目标Surface。通过先前创建的persistent input surface调用setInputSurface(Surface)配置这个编解码器。

  Codec-specific数据

  有些格式,特别是ACC音频和MPEG4、H.264和H.265视频格式要求实际数据以若干个包含配置数据或编解码器指定数据的缓存为前缀。当处理这种压缩格式时,这些数据必须在调用start()方法后及任何帧数据之前提交给编解码器。这些数据必须在调用queueInputBuffer方法时使用BUFFER_FLAG_CODEC_CONFIG进行标记。

  Codec-specific数据也可以被包含在传递给configure方法的格式format中,在ByteBuffer条目中以”csd-0”, “csd-1”等key标记。这些keys通常包含在通过MediaExtractor获得的轨道MediaFormat中。一旦调用start()方法格式中的Codec-specific数据自动提交给编解码器;你不能显示的提交这些数据。如果这个格式不包含编解码器指定的数据,你可以根据格式要求,按照正确的顺序使用指定数目的缓存来提交数据。还有,你也可以连接所有的codec-specific数据并作为一个单独的codec-config buffer提交。

  Android 使用下列的codec-specific数据buffers。对于适当的MediaMuxer轨道配置,这些也要在轨道格式中进行设置。每一个参数集以及被标记为(*)的codec-specific-data段必须以”\x00\x00\x00\x01”字符开头。
  这里写图片描述
   注意:当编解码器被立即刷新或start之后不久刷新,并且在任何输出buffer或输出格式变化被返回前需要特别地小心,因为编解码器的codec specific data可能会在flush过程中丢失。为保证编解码器的正常运行,你必须在刷新后使用标记为BUFFER_FLAG_CODEC_CONFIGbuffers的buffers再次提交这些数据。

   编码器(或者产生压缩数据的编解码器)会产生和返回编解码器指定的数据,它会在以codec-config flag标记的输出缓存中的有效输出缓存之前。包含codec-specific-data的Buffers没有有意义的时间戳。

数据处理(Data Processing)

  每一个编解码器包含一组输入和输出缓存,这些缓存在API调用中通过buffer-ID进行引用。当成功调用start()方法后客户端将不会“拥有”输入或输出buffers。在同步模式下,通过调用dequeueInput/OutputBuffer(…) 方法从编解码器获得(取得所有权)一个输入或输出buffer。在异步模式下,你可以通过MediaCodec.Callback.onInput/OutputBufferAvailable(…)的回调方法自动地获得可用的buffers。

  在获得一个输入buffe后,填充数据,利用queueInputBuffer方法将其提交给编解码器,若使用解密模式,则利用queueSecureInputBuffer方法提交。不要提交多个具有相同时间戳的输入bufers(除非它是也被同样标记的codec-specific data)。

  在异步模式下通过onOutputBufferAvailable方法的回调或者在同步模式下响应dequeuOutputBuffer的调用,编解码器返回一个只读的output buffer。在这个output buffer被处理后,调用一个releaseOutputBuffer方法将这个buffer返回给编解码器。

  当你不需要立即向编解码器重新提交或释放buffers时,保持对输入或输出buffers的所有权可使编解码器停止工作,当然这些行为依赖于设备情况。特别地,编解码器可能延迟产生输出buffers直到输出的buffers被释放或重新提交。因此,尽可能少地保存可用的buffers。

  根据API版本情况,你有三种处理相关数据的方式:
  这里写图片描述

  使用缓存的异步处理方式(Asynchronous Processing using Buffers)

   从Android 5.0(LOLLIPOP)开始,首选的方法是调用configure之前通过设置回调异步地处理数据。异步模式稍微改变了状态转换方式,因为你必须在调用flush()方法后再调用start()方法才能使编解码器的状态转换为Running子状态并开始接收输入buffers。同样,初始调用start方法将编解码器的状态直接变化为Running 子状态并通过回调方法开始传递可用的输入buufers。
   这里写图片描述
  异步模式下,编解码器典型的使用方法如下:
  

1  MediaCodec codec = MediaCodec.createByCodecName(name);
 2  MediaFormat mOutputFormat; // member variable
 3  codec.setCallback(new MediaCodec.Callback() {
 4    @Override
 5    void onInputBufferAvailable(MediaCodec mc, int inputBufferId) {
 6      ByteBuffer inputBuffer = codec.getInputBuffer(inputBufferId);
 7      // fill inputBuffer with valid data
 89      codec.queueInputBuffer(inputBufferId, …);
10    }
11 
12    @Override
13    void onOutputBufferAvailable(MediaCodec mc, int outputBufferId, …) {
14      ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
15      MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
16      // bufferFormat is equivalent to mOutputFormat
17      // outputBuffer is ready to be processed or rendered.
1819      codec.releaseOutputBuffer(outputBufferId, …);
20    }
21 
22    @Override
23    void onOutputFormatChanged(MediaCodec mc, MediaFormat format) {
24      // Subsequent data will conform to new format.
25      // Can ignore if using getOutputFormat(outputBufferId)
26      mOutputFormat = format; // option B
27    }
28 
29    @Override
30    void onError(…) {
3132    }
33  });
34  codec.configure(format, …);
35  mOutputFormat = codec.getOutputFormat(); // option B
36  codec.start();
37  // wait for processing to complete
38  codec.stop();
39  codec.release();

使用缓存的同步处理方式(Synchronous Processing using Buffers)

  从Android5.0(LOLLIPOP)开始,即使在同步模式下使用编解码器你应该通过getInput/OutputBuffer(int) 和/或 getInput/OutputImage(int) 方法检索输入和输出buffers。这允许通过框架进行某些优化,例如,在处理动态内容过程中。如果你调用getInput/OutputBuffers()方法这种优化是不可用的。

  注意,不要同时混淆使用缓存和缓存数组的方法。特别地,仅仅在调用start()方法后或取出一个值为INFO_OUTPUT_FORMAT_CHANGED的输出buffer ID后你才可以直接调用getInput/OutputBuffers方法。

  同步模式下MediaCodec的典型应用如下:

MediaCodec codec = MediaCodec.createByCodecName(name);
 2  codec.configure(format, …);
 3  MediaFormat outputFormat = codec.getOutputFormat(); // option B
 4  codec.start();
 5  for (;;) {
 6    int inputBufferId = codec.dequeueInputBuffer(timeoutUs);
 7    if (inputBufferId >= 0) {
 8      ByteBuffer inputBuffer = codec.getInputBuffer(…);
 9      // fill inputBuffer with valid data
1011      codec.queueInputBuffer(inputBufferId, …);
12    }
13    int outputBufferId = codec.dequeueOutputBuffer(…);
14    if (outputBufferId >= 0) {
15      ByteBuffer outputBuffer = codec.getOutputBuffer(outputBufferId);
16      MediaFormat bufferFormat = codec.getOutputFormat(outputBufferId); // option A
17      // bufferFormat is identical to outputFormat
18      // outputBuffer is ready to be processed or rendered.
1920      codec.releaseOutputBuffer(outputBufferId, …);
21    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
22      // Subsequent data will conform to new format.
23      // Can ignore if using getOutputFormat(outputBufferId)
24      outputFormat = codec.getOutputFormat(); // option B
25    }
26  }
27  codec.stop();
28  codec.release();

使用缓存数组的同步处理方式(Synchronous Processing using Buffer Arrays)– (deprecated)

  在Android 4.4(KITKAT_WATCH)及之前版本,一组输入或输出buffers使用ByteBuffer[]数组表示。在成功调用了start()方法后,通过调用getInput/OutputBuffers()方法检索buffer数组。在这些数组中使用buffer的ID-s(非负数)作为索引,如下面的演示示例中,注意数组大小和系统使用的输入和输出buffers的数量之间并没有固定的关系,尽管这个数组提供了上限边界。

 1  MediaCodec codec = MediaCodec.createByCodecName(name);
 2  codec.configure(format, …);
 3  codec.start();
 4  ByteBuffer[] inputBuffers = codec.getInputBuffers();
 5  ByteBuffer[] outputBuffers = codec.getOutputBuffers();
 6  for (;;) {
 7    int inputBufferId = codec.dequeueInputBuffer(…);
 8    if (inputBufferId >= 0) {
 9      // fill inputBuffers[inputBufferId] with valid data
1011      codec.queueInputBuffer(inputBufferId, …);
12    }
13    int outputBufferId = codec.dequeueOutputBuffer(…);
14    if (outputBufferId >= 0) {
15      // outputBuffers[outputBufferId] is ready to be processed or rendered.
1617      codec.releaseOutputBuffer(outputBufferId, …);
18    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
19      outputBuffers = codec.getOutputBuffers();
20    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
21      // Subsequent data will conform to new format.
22      MediaFormat format = codec.getOutputFormat();
23    }
24  }
25  codec.stop();
26  codec.release();

  流结束处理(End-of-stream Handling)

  当到达输入数据结尾时,你必须在调用queueInputBuffer方法中通过指定BUFFER_FLAG_END_OF_STREAM标记来通知编解码器。你可以在最后一个有效的输入buffer上做这些操作,或者提交一个额外的以end-of-stream标记的空的输入buffer。如果使用一个空的buffer,它的时间戳将被忽略。

  编解码器将会继续返回输出buffers,直到它发出输出流结束的信号,这是通过指定dequeueOutputBuffer方法中MediaCodec.BufferInfo的end-of-stream标记来实现的,或者是通过回调方法onOutputBufferAvailable来返回end-of-stream标记。可以在最后一个有效的输出buffer中设置或者在最后一个有效的输出buffer后添加一个空的buffer来设置,这种空的buffer的时间戳应该被忽略。

  当通知输入流结束后不要再提交额外的输入buffers,除非编解码器被刷新或停止或重启。

  使用一个输出表面(Using an Output Surface)

  使用一个输出Surface进行数据处理的方法与ByteBuffer模式几乎是相同的,然而,输出buffers不再可访问,而且被表示为null值。E.g.方法getOutputBuffer/Image(int)将返回null,方法getOutputBuffers()将返回仅包含null值的数组。

  当使用一个输出Surface时,你能够选择是否渲染surface上的每一个输出buffer,你有三种选择:

  不要渲染这个buffer(Do not render the buffer):通过调用releaseOutputBuffer(bufferId, false)。
  使用默认的时间戳渲染这个buffer(Render the buffer with the default timestamp):调用releaseOutputBuffer(bufferId, true)。
  使用指定的时间戳渲染这个buffer(Render the buffer with a specific timestamp):调用 releaseOutputBuffer(bufferId, timestamp)。
  从Android6.0(M)开始,默认的时间戳是buffer的presentation timestamp(转换为纳秒)。在此前的版本中这是没有被定义的。

  而且,从Android6.0(M)开始,你可以通过使用setOutputSurface方法动态地改变输出Surface。

  使用一个输入表面(Using an Input Surface)

  当使用输入Surface时,将没有可访问的输入buffers,因为这些buffers将会从输入surface自动地向编解码器传输。调用dequeueInputBuffer时将抛出一个IllegalStateException异常,调用getInputBuffers()将要返回一个不能写入的伪ByteBuffer[]数组。

  调用signalEndOfInputStream()方法发送end-of-stream信号。调用这个方法后,输入surface将会立即停止向编解码器提交数据。

查询&自适应播放支持(Seeking & Adaptive Playback Support)

  视频解码器(通常指处理压缩视频数据的编解码器)关于搜索-seek和格式转换(不管它们是否支持)表现不同,且被配置为adaptive playback。你可以通过调用CodecCapabilities.isFeatureSupported(String)方法来检查解码器是否支持adaptive playback 。支持Adaptive playback的解码器只有在编解码器被配置在Surface上解码时才被激活。

  流域界与关键帧(Stream Boundary and Key Frames)

  在调用start()或flush()方法后,输入数据在合适的流边界开始是非常重要的:其第一帧必须是关键帧。一个关键帧能够独立地完全解码(对于大多数编解码器它意味着帧内编码),关键帧之后显示的帧不会引用关键帧之前的帧。

  下面的表格针对不同的视频格式总结了合适的关键帧。
  这里写图片描述

对于不支持adaptive playback的解码器(包括解码到Surface上解码器)

  为了开始解码与先前提交的数据(也就是seek后)不相邻的数据你必须刷新解码器。由于所有输出buffers会在flush的一刻立即撤销,你可能希望在调用flush方法前等待这些buffers首先被标记为end-of-stream。在调用flush方法后输入数据在一个合适的流边界或关键帧开始是非常重要的。

  注意:flush后提交的数据的格式不能改变;flush()方法不支持格式的不连续性;为此,一个完整的stop()-configure(…)-start()的过程是必要的。

  同时注意:如果你调用start()方法后过快地刷新编解码器,通常,在收到第一个输出buffer或输出format变化前,你需要向这个编解码器再次提交codec-specific-data。具体查看codec-specific-data部分以获得更多信息。

对于支持及被配置为adaptive playback的几码器

  为了开始解码与先前提交的数据(也就是seek后)不相邻的数据,你没有必要刷新解码器;然而,在间断后传入的数据必须开始于一个合适的流边界或关键帧。

  针对一些视频格式-也就是H.264、H.265、VP8和VP9,也可以修改图片大小或者配置mid-stream。为了做到这些你必须将整个新codec-specific配置数据与关键帧一起打包到一个单独的buffer中(包括所有的开始数据),并将它作为一个常规的输入数据提交。

  在picture-size被改变后以及任意具有新大小的帧返回之前,你可以从dequeueOutputBuffer方法或onOutputFormatChanged回调中得到 INFO_OUTPUT_FORMAT_CHANGED的返回值。

  注意:就像使用codec-specific data时的情况,在你修改图片大小后立即调用fush()方法时需要非常小心。如果你没有接收到图片大小改变的确认信息,你需要重试修改图片大小的请求。
  

错误处理(Error handling)

  工厂方法createByCodecName以及createDecoder/EncoderByType会在创建codec失败时抛出一个IOException,你必须捕获异常或声明向上传递异常。在编解码器不允许使用该方法的状态下调用时,MediaCodec方法将会抛出IllegalStateException异常;这种情况一般是由于API接口的不正确调用引起的。涉及secure buffers的方法可能会抛出一个MediaCodec.CryptoException异常,可以调用getErrorCode()方法获得更多的异常信息。

  内部的编解码器错误将导致MediaCodec.CodecException,这可能是由于media内容错误、硬件错误、资源枯竭等原因所致,即使你已经正确的使用了API。当接收到一个CodecException时,可以调用isRecoverable()和isTransient()两个方法来决定建议的行为。

  1、可恢复错误(recoverable errors):如果isRecoverable() 方法返回true,然后就可以调用stop(),configure(…),以及start()方法进行修复。
  2、短暂错误(transient errors):如果isTransient()方法返回true,资源短时间内不可用,这个方法可能会在一段时间之后重试。
 3、致命错误(fatal errors):如果isRecoverable()和isTransient()方法均返回fase,CodecException错误是致命的,此时就必须reset这个编解码器或调用released方法释放资源。
  isRecoverable()和isTransient()方法不可能同时都返回true。

合法的API调用和API历史(Valid API Calls and API History)

  下面的表格总结了MediaCodec中合法的API以及API历史版本。更多的API版本号详见Build.VERSION_CODES。
  这里写图片描述
  这里写图片描述

嵌套类(Nested classes)

这里写图片描述

常量(Constants)

这里写图片描述
这里写图片描述

公有方法(Public methods)

configure

void configure (MediaFormat format, Surface surface, MediaCrypto crypto, int flags)

配置一个组件。
这里写图片描述

Start

void start()

成功地配置组件后,调用start方法。

如果codec在异步模式下被配置且处于flushed状态,为处理要求的的输入buffer,也需调用start方法。
这里写图片描述

dequeueInputBuffer

int dequeueInputBuffer(long timeoutUs)

返回一个填充了有效数据的input buffer的索引,如果没有可用的buffer则返回-1.当timeoutUs==0时,该方法立即返回;当timeoutUs<0时,无限期地等待一个可用的input buffer;当timeoutUs>0时,至多等待timeoutUs微妙。
这里写图片描述

queueInputBuffer(没完全理解)

void queueInputbuffer(int index, int offset, int size, long presentationtimeUs, int flags)

给指定索引的input buffer填充数据后,将其提交给codec组件。一旦一个input buffer在codec中排队,它就不再可用直到通过getInputBuffer(int)重新获取,getInputBuffer(int)是对dequeueInputbuffer(long)的返回值或onInputBufferAvailable(MediaCodec, int)回调的响应。

许多解码器要求实际压缩数据流以“codec specific data”为先导,也就是用于初始化codec的设置数据,例如AVC视频情况时的PPS/SPS,或vorbis音频情况时的code tables。MediaExtractor类提供codec specific data作为返回的track format的一部分,在命名为csd-0,csd-1的条目中。

通过指定BUFFER_FLAG_CODEC_CONFIG,这些buffers可以在start()或flush()后直接提交。然而,如果你使用包含这些keys的MediaFormat配置codec,他们将在start后自动地提交。因此,不鼓励使用BUFFER_FLAG_CODEC_CONFIG,仅推荐高级用户使用。

为了指示输入数据的最后一块(或除非decoder flush否则不再有输入数据)需指定DUFFER_FLAG_END_OF_STREAM。

  注意:android6.0(M)之前,presentationTimeUs不会传递到Surface output buffer 的帧的时间戳,这会导致帧的时间戳没有定义。使用releaseOutputBuffer(int, long)方法确保一个指定的时间戳被设置。同样地,尽管frame timestamp可以被destination surface用于同步渲染,必须注意presentationTimeUs正常化,以便不被误认为是一个系统时间。
  这里写图片描述

release

void release()

释放codec实例使用的资源。释放任何开放的组件实例时调用该方法,而不是依赖于垃圾回收机制在以后某个时间完成资源的释放。

reset

void reset()

使codec返回到初始(未初始化)状态。如果发生了不可恢复的错误,调用该方法使codec复位到创建时的初始状态。
这里写图片描述

stop

void stop()

完成解码/编码任务后,需注意的是codec任然处于活跃状态且准备重新start。为了确保其他client可以调用release()方法,且不仅仅只依赖于garbage collection为你完成这些工作。
这里写图片描述

flush

void flush()

冲洗组件的输入和输出端口。

返回时,所有之前通过调用dequeueInputBuffer方法和dequeueOutputBuffer方法或通过onInputBufferAvailable和onOutputBufferAvailable回调获得的索引会失效。所有的buffers都属于codec。

如果codec被配置为异步模式,flush后调用start()重新开始codec操作。编解码器不会请求输入缓冲区,直到这已经发生了。

如果codec配置为同步模式,配置时使用了一个input buffer时codec会自动重新开始,否则,当调用dequeueInputBuffer时重新开始。
这里写图片描述

该博客转载于Hi,出发了

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值