Android多媒体--MediaCodec 中文API文档

本文档详细介绍了Android MediaCodec类,用于处理音频和视频的编解码。MediaCodec支持压缩数据、原始音频和视频,提供异步和同步处理方式。通过数据处理、状态管理、创建和初始化等步骤,实现多媒体数据的灵活操作。同时,文档涵盖了错误处理、流结束处理、使用输入/输出Surface等功能。
摘要由CSDN通过智能技术生成

*由于工作需要,需要利用MediaCodec实现Playback及Transcode等功能,故在学习过程中翻译了Google官方的MediaCodec API文档,由于作者水平限制,文中难免有错误和不恰当之处,望批评指正。

*转载请注明出处:http://www.cnblogs.com/roger-yu/

从API 16开始,Android提供了Mediacodec类以便开发者更加灵活的处理音视频的编解码,与MediaPlayer/VideoView等high-level APIs相比,MediaCodec是low-level APIs,因此它提供了更加完善、灵活、丰富的接口,开发者可以实现更加灵活的功能。废话即止,开始学习之旅~~

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采用异步方式处理数据,并且使用了一组输入输出缓存(input and output buffers)。简单来讲,你请求或接收到一个空的输入缓存(input buffer),向其中填充满数据并将它传递给编解码器处理。编解码器处理完这些数据并将处理结果输出至一个空的输出缓存(output buffer)中。最终,你请求或接收到一个填充了结果数据的输出缓存(output buffer),使用完其中的数据,并将其释放给编解码器再次使用。

 数据类型(Data Types)

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

  压缩缓存(Compressed Buffers)

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

  原始音频缓存(Raw Audio 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模式下,视频缓存(video 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中。这些颜色格式同 flexible format相似,你仍然可以使用 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)。

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

创建(Creation)

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

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

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

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

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

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

初始化(Initialization)

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

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

  如果你想将原始视频数据(raw video data)送视频消费者处理(将原始视频数据作为输入的编解码器,例如视频编码器),你可以在配置好视频消费者编解码器(encoder)后调用createInputSurface方法创建一个目的surface来存放输入数据,如此,调用视频生产者(decoder)的setInputSurface(Surface)方法将前面创建的目的Surface配置给视频生产者作为输出缓存位置。

  Codec-specific数据

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

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

  Android 使用下列的codec-specific data 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)

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

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

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

  当你不需要立即向编解码器重新提交或释放buffers时ÿ

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值