Android Multimedia框架总结(二十)MediaCodec状态周期及Codec与输入/输出Buffer过程(附实例)

转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiophttp://blog.csdn.net/hejjunlin/article/details/53183718

前言:前面几节都是介绍Camera2相关,对于Camera2预览把图像显示在SurfaceView上,还有录像时,时时刷新当前图像区域。追溯到最早介绍的MediaPlayer播放视频,这些都离不开重要角色MediaCodec,今天介绍MediaCodec,看下Agenda:

  • MediaCodec是什么?
  • codec操作的数据类型
    • 压缩buffer
    • 原始音频buffer
    • 原始视频buffer
  • MediaCodec状态周期图
  • Codec-specific数据
  • Codec数据处理过程
  • MediaCodec案例

MediaCodec是什么?

MediaCodec类可以访问底层媒体编解码器框架(StageFright或openMAX),即编码器/解码器组件。这是Android low-level多媒体支持基础设施的一部分(通常与MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, and AudioTrack.一起使用))。

从广义上讲,codec处理输入数据产生输出数据。异步处理数据,并使用一组输入和输出buffer。在简单的层面,请求(或接收)空输入缓冲区,加满数据并将其发送到codec进行处理。codec使用的数据转换成一个空的输出缓冲区。最后,请求(或接收)输出buffer,消耗其内容并释放它回codec。

codec操作的数据类型

codec操作三种数据:压缩数据,原始音频数据和原始的视频数据。所有这三种数据可以使用ByteBuffers处理,应该对原始视频数据使用一个Surface来提高编码性能。Surface使用native视频的buffers没有映射或复制他们ByteBuffers;因此,它更高效。通常不能访问原始视频数据使用一个Surface时,可以使用ImageReader类访问未固定的解码视频帧(原生)。比起使用ByteBuffers的话,这可能会更有效的,一些native 的buffers可能直接映射到ByteBuffers。当使用ByteBuffer模式中,您可以使用Image类及getInput / OutputImage(int)访问原始视频帧。

压缩buffer

输入缓冲区(解码器)和输出缓冲区(编码器)包含压缩数据格式的类型。对于视频类型,这是一个压缩的视频帧。对于音频数据,这通常是一个单一的访问单元(一个音频段通常包含几毫秒的音频编码格式决定的类型),但这个需求略有放松,缓冲区可能包含多个接入单位的音频编码。在这两种情况下,在任意字byte边界上,buffer不会start和end,直到在(帧/访问)unit边界上。

原始音频buffer

原始音频缓冲区包含整个PCM音频帧数据,下面是一个示例为每个通道的通道顺序。每一个样例都是16位带符号整数在native byte通道中。

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 = formet.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;
 }

原始视频buffer

ByteBuffer模式视频缓冲区根据他们的颜色格式布局。你可以从getCodecInfo().getCapabilitiesForType .colorFormats(…)得到支持的颜色格式数组。视频编解码器支持三种颜色格式:

  • native raw video format: 带有COLOR_FormatSurface标记,并用于输入/输出到Surface.
  • flexible YUV buffers (带有COLOR_FormatYUV420Flexible): 当在 ByteBuffer 模式下,通过getInput/OutputImage(int)方法,也用于输入/输出到Surface.
  • specific formats: 这些都是通常只支持ByteBuffer模式,一些特定于供应商的颜色格式,其他人则MediaCodecInfo.CodecCapabilities中定义,对于color formats ,相当于一个灵活的格式,你依旧可以使用getInput/OutputImage(int)

自LOLLIPOP_MR1开始,所有视频编解码器支持灵活的YUV 4:2:0的buffer

本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/53183718

MediaCodec状态

在codec的生命周期的概念上存在三种状态之一:Stopped, Executing or Released. 。停止所有状态实际上是聚集的三种状态:Uninitialized, Configured and Error,而执行状态概念上的进展通过三个子状态:Flushed, Running and End-of-Stream。

当您使用一个工厂方法创建一个codec时,codec处于未初始化的状态。首先,您需要配置它通过configue(…),这让它处于配置的状态,然后调用start()方法将它移动到Executing状态。在这种状态下你可以通过上述缓冲队列操作过程数据。

执行状态有三个子状态:Flushed, Running and End-of-Stream。在start()方法调用后,codec会立刻刷新子状态,它拥有所有的buffer。一旦第一个输入buffer从列中移除,codec将花费比较长的时间移动到正在运行的子状态上。当你队列的输入buffer带有end-of-stream标记,codec将转换到end-of-stream子状态。在这种状态下的codec不再接受进一步输入buffer,但仍然生成输出缓冲区,直到达到end-of-stream输出。你可以移回到Flushed子状态在任何时候当在Executing 状态时使用flush()方法。

调用stop()方法返回的codec未初始化(Uninitialized)状态,因为它可能是再次被配置了。当你再使用一个编解码器(codec),你必须释放它通过调用release()方法。

在极少数情况下的codec可能会遇到一个错误,Error状态。这是沟通使用无效的返回值从一个队列操作,或者通过一个例外。调用reset()方法让编解码器(codec)能再次可用。你可以调用它 把当前的任何状态转移到Uninitialized状态,否则,调用release()方法,回到终端Released状态。

本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/53183718

Codec-specific数据

一些格式,特别是AAC音频和MPEG4,H.264和H.265视频格式需要通过大量的buffer包含设置数据,或codec特定数据来作为真实数据的开头。在处理这样的压缩格式时,在任意帧数据开始前,调用start方法后,这个数据必须提交给codec。当在调用queueInputBuffer时,这些数据必须使用flag:BUFFER_FLAG_CODEC_CONFIG。

Codec-specific数据也可以包含在ByteBuffer条目的格式传递给配置钥匙“csd-0”、“csd-1”,等等。这些钥匙总是包含在跟踪MediaFormat从MediaExtractor获得。Codec-specific数据格式自动提交给codec在start调用;你不能明确地提交这些数据。如果不包含编码特定的数据格式,你可以选择提交使用指定的缓冲区以正确的顺序,按照格式要求。对于H.264 AVC,您还可以连接所有codec-specific数据并提交它作为单个codec-config缓冲区。

Android使用以下codec-specific数据缓冲区。这些也需要跟踪格式中设置适当的MediaMuxer track 配置。每个参数设置和codec-specific-data部分标注(*)必须从以”\x00\x00\x00\x01”开始。

注意:必须注意codec在start之后马上刷新,在任何output buffer或输出格式被改变被返回之前, codec specific data也许会丢失在刷新过程中。在这种刷新时,要确保合适的codec操作不出问题,必须要重提交data,使用带BUFFER_FLAG_CODEC_CONFIG标志的buffers。

在任意有效的output buffers输出的buffer带有codec-config 标志前,编码器(或codec生成压缩数据)将创建并返回编码特定的数据。buffer包含codec-specific-data没有意义的时间戳。

本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/53183718

Codec数据处理过程

每个codec维护的一组输入和输出buffer被指向在API调用的buffer-ID。成功调用start()方法后,client“拥有”输入和输出buffer。在同步模式下,调用dequeueInput / OutputBuffer(…)来获得(所有权)codec的输入或输出buffer。在异步模式下,您通过MediaCodec.Callback.onInput / OutputBufferAvailable(…)回调方法将自动接收buffer。

在获得一个输入缓冲区,填入数据并提交它的codec使用queueInputBuffer——或者queueSecureInputBuffer如果使用解密。不要使用相同的时间戳提交多个输入buffer(除非codec-specific数据被标记为)。

codec在异步模式下通过onOutputBufferAvailable回调方法将返回一个只读输出buffer,或在同步模式下响应dequeuOutputBuffer调用。在输出buffer被处理后,调用一个releaseOutputBuffer方法返回codec的buffer。

当你不需要重新提交/立即释放codec的buffer,抓住输入和/或输出buffer可能在codec中延后,这种情况和设备相关。具体地说,codec可能暂缓生成输出buffer,直到所有buffer的未发布/重新提交。因此,试图得到可用buffer尽可能少。

根据API版本,您可以在三个方面:处理数据

异步处理使用缓冲区

自LOLLIPOP开始,首选的方法处理异步过程数据是通过设置一个回调在调用配置之前。异步模式改变了状态转换,因为刷新后必须调用start()方法的codec过渡到正在运行的子状态,开始接收输入buffer。同样,在一个初始直接调用开始后,codec将正在运行的子状态,开始通过回调传递可用的输入buffer。

本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/53183718

案例

此前我们曾用MediaPlayer播放网络视频及本地视频,今天不用MediaPlayer,转而用MediaCodec来Codec一个视频到SurfaceView上,下面看代码:

效果图

案例工程已上传到github:https://github.com/hejunlin2013/MultiMediaSample,之前其他多媒体框架相关案例也在此仓库中。

第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。

如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易

时间: 2024-10-26 21:53:08

Android Multimedia框架总结(二十)MediaCodec状态周期及Codec与输入/输出Buffer过程(附实例)的相关文章

Android Multimedia框架总结(十)Stagefright框架之音视频输出过程

转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52560012 前言:上篇文中最后介绍了数据解码放到Buffer过程,今天分析的是stagefright框架中音视频输出过程: 先看下今天的Agenda: 一张图回顾数据处理过程 视频渲染器构建过程 音频数据到Buffer过程 AudioPlayer在AwesomePlayer运行过程 音视频同步 音视频输出 一张图看音视频输出 一

Android Multimedia框架总结(十四)Camera框架初识及自定义相机案例

转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52738492 前言:国庆节告一段落,又是新一月,上月主要是围绕MediaPlayer相关展开,从今天开始,开始分析多媒体框架中的Camera模块,看下今天的Agenda: Camera拍照 Camera录像 新API android.hardware.camera2 新旧API特点对比 Camera自定义相机 新API andro

Android学习路线(二十四)ActionBar Fragment运用最佳实践

通过前面的几篇博客,大家看到了Google是如何解释action bar和fragment以及推荐的用法.俗话说没有demo的博客不是好博客,下面我会介绍一下action bar和fragment在实战中的应用,以及相关demo源码,希望和大家相互交流. 了解过fragment的同学们应该都知道,fragment是android 3.0版本才出现的的,因此如果要在支持android 3.0一下版本的工程中使用fragment的话是需要添加Support Library的.具体如何添加我就不再赘述

Android Multimedia框架总结(九)Stagefright框架之数据处理及到OMXCodec过程

转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52532085 不知不觉到第九篇了,感觉还有好多好多没有写,路漫漫其修远兮 ,吾将上下而求索,上篇主要介绍了Stagefright框架及AwesomePlayer的数据解析器,最后我们说道,涉及parse及decode部分,将在本篇中介绍,看下今天的Agenda: 两张图看数据走向 AwesomePlayer中prepare过程 Awesom

Android Multimedia框架总结(六)C++中MediaPlayer的C/S架构

http://blog.csdn.net/hejjunlin/article/details/52435789 前面几节中,都是通过java层调用到jni中,jni向下到c++层并未介绍 看下Java层一个方法在c++层 MediaPlayer后续过程 frameworks/av/media/libmedia/MediaPlayer.cpp 找一个我们之前熟悉的setDataResource方法看下C/S模式的过程,亦可参考Android Multimedia框架总结(四)MediaPlayer

android产品研发(二十二)--&gt;android实用调试技巧

转载请标明出处:一片枫叶的专栏 上一篇文章中我们讲解了android UI优化方面的知识.我们讲解了android中的include.marge.ViewStub标签,在使用这些标签时可以简化我们的布局文件,优化组件绘制流程:讲解了android中的过度绘制相关知识点,通过优化我们的App过度绘制可以提高App的UI绘制流程与性能:我们还讲解了App中一些UI优化的小tips.更多关于android UI优化方面的知识可以参考我的:android产品研发(二十一)–>android中的UI优化

Android Multimedia框架总结(七)C++中MediaPlayer的C/S架构补充及MediaService介绍

http://blog.csdn.net/hejjunlin/article/details/52465168 前面一篇主要介绍c++中MediaPlayer的C/S架构中和Client相关部分,并中间穿插了mediaplayerservice的部分.但是对于这块C/S部分,没有放大去分析.<Android Multimedia框架总结(四)MediaPlayer中从Java层到C++层类关系及prepare及之后其他过程>是从整体上看的,今天我们把这块C/S模型放大去看下.同样先看下Agen

Android项目实战(二十九):酒店预定日期选择

原文:Android项目实战(二十九):酒店预定日期选择 先看需求效果图:              几个需求点: 1.显示当月以及下个月的日历 (可自行拓展更多月份) 2.首次点击选择"开始日期",再次点击选择"结束日期" (1).如果"开始日期" "结束日期" 相同  (2).如果"开始日期" "结束日期" 不同,且"结束日期" 晚于 "开始日期&quo

Android学习路线(二十)运用Fragment构建动态UI

要在Android系统上创建一个动态或者多面板的用户界面,你需要将UI组件以及activity行为封装成模块,让它能够在你的activity中灵活地切换显示与隐藏.你可以使用Fragment类来创建这些模块,它们能够表现得有些像嵌套的activity,它们定义着自己的布局,管理自己的生命周期. 当一个fragment指定了它自己的布局,它可以在activity中和其他的fragment配置为不同的组合,这样就能够为不同的屏幕尺寸来修改你的布局配置(在小屏幕上一次展现一个fragment,而在大屏