MediaCodec基本原理及使用「建议收藏」

MediaCodec基本原理及使用「建议收藏」MediaCodec类Android提供的用于访问低层多媒体编/解码器接口,它是Android低层多媒体架构的一部分,通常与MediaExtractor、MediaMuxer、AudioTrack结合使用,能够编解码诸如H.264、H.265、AAC、3gp等常见的音视频格式。广义而言,MediaCodec的工作原理就是处理输入数据以产生输出数据。具体来说,MediaCodec在编解码的过程中使用了一组输入/输出缓存区来同步或异步处理数据:首先,客户端向获取到的编解码器输入缓存区写入要编解码的数据并将其提交

大家好,又见面了,我是你们的朋友全栈君。

MediaCodec工作原理

MediaCodec类Android提供的用于访问低层多媒体编/解码器接口,它是Android低层多媒体架构的一部分,通常与MediaExtractor、MediaMuxer、AudioTrack结合使用,能够编解码诸如H.264、H.265、AAC、3gp等常见的音视频格式。广义而言,MediaCodec的工作原理就是处理输入数据以产生输出数据。具体来说,MediaCodec在编解码的过程中使用了一组输入/输出缓存区来同步或异步处理数据:首先,客户端向获取到的编解码器输入缓存区写入要编解码的数据并将其提交给编解码器,待编解码器处理完毕后将其转存到编码器的输出缓存区,同时收回客户端对输入缓存区的所有权;然后,客户端从获取到编解码输出缓存区读取编码好的数据进行处理,待处理完毕后编解码器收回客户端对输出缓存区的所有权。不断重复整个过程,直至编码器停止工作或者异常退出。

MediaCodec基本原理及使用「建议收藏」

通过上图可以看出,mediacodec的作用是处理输入的数据生成输出数据。首先生成一个输入数据缓冲区,将数据填入缓冲区提供给codec,codec会采用异步的方式处理这些输入的数据,然后将填满输出缓冲区提供给消费者,消费者消费完后将缓冲区返还给codec。

MediaCodec编码过程

在整个编解码过程中,MediaCodec的使用会经历配置、启动、数据处理、停止、释放几个过程,相应的状态可归纳为停止(Stopped),执行(Executing)以及释放(Released)三个状态,而Stopped状态又可细分为未初始化(Uninitialized)、配置(Configured)、异常( Error),Executing状态也可细分为读写数据(Flushed)、运行(Running)和流结束(End-of-Stream)。MediaCodec整个状态结构图如下:

MediaCodec基本原理及使用「建议收藏」

从上图可知,当MediaCodec被创建后会进入未初始化状态,待设置好配置信息并调用start()启动后,MediaCodec会进入运行状态,并且可进行数据读写操作。如果在这个过程中出现了错误,MediaCodec会进入Stopped状态,我们就是要使用reset方法来重置编解码器,否则MediaCodec所持有的资源最终会被释放。当然,如果MediaCodec正常使用完毕,我们也可以向编解码器发送EOS指令,同时调用stop和release方法终止编解码器的使用。

MediaCodec API 说明

MediaCodec可以处理具体的视频流,主要有这几个方法:

getInputBuffers:获取需要编码数据的输入流队列,返回的是一个ByteBuffer数组
queueInputBuffer:输入流入队列
dequeueInputBuffer:从输入流队列中取数据进行编码操作
getOutputBuffers:获取编解码之后的数据输出流队列,返回的是一个ByteBuffer数组
dequeueOutputBuffer:从输出队列中取出编码操作之后的数据
releaseOutputBuffer:处理完成,释放ByteBuffer数据

基本使用

所有的同步模式的 MediaCodec API都遵循一个模式:

创建并配置一个 MediaCodec 对象
循环直到完成:
如果输入缓冲区就绪,读取一个输入块,并复制到输入缓冲区中
如果输出缓冲区就绪,复制输出缓冲区的数据
释放 MediaCodec 对象

(1) 创建编/解码器

MediaCodec主要提供了createEncoderByType(String type)、createDecoderByType(String type)两个方法来创建编解码器,它们均需要传入一个MIME类型多媒体格式。常见的MIME类型多媒体格式如下:
● “video/x-vnd.on2.vp8” – VP8 video (i.e. video in .webm)
● “video/x-vnd.on2.vp9” – VP9 video (i.e. video in .webm)
● “video/avc” – H.264/AVC video
● “video/mp4v-es” – MPEG4 video
● “video/3gpp” – H.263 video
● “audio/3gpp” – AMR narrowband audio
● “audio/amr-wb” – AMR wideband audio
● “audio/mpeg” – MPEG1/2 audio layer III
● “audio/mp4a-latm” – AAC audio (note, this is raw AAC packets, not packaged in LATM!)
● “audio/vorbis” – vorbis audio
● “audio/g711-alaw” – G.711 alaw audio
● “audio/g711-mlaw” – G.711 ulaw audio
当然,MediaCodec还提供了一个createByCodecName (String name)方法,支持使用组件的具体名称来创建编解码器。但是该方法使用起来有些麻烦,且官方是建议最好是配合MediaCodecList使用,因为MediaCodecList记录了所有可用的编解码器。当然,我们也可以使用该类对传入的minmeType参数进行判断,以匹配出MediaCodec对该mineType类型的编解码器是否支持。以指定MIME类型为“video/avc”为例,代码如下:

private static MediaCodecInfo selectCodec(String mimeType) {
     // 获取所有支持编解码器数量
     int numCodecs = MediaCodecList.getCodecCount();
     for (int i = 0; i < numCodecs; i++) {
        // 编解码器相关性信息存储在MediaCodecInfo中
         MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
         // 判断是否为编码器
         if (!codecInfo.isEncoder()) {
             continue;
         }
        // 获取编码器支持的MIME类型,并进行匹配
         String[] types = codecInfo.getSupportedTypes();
         for (int j = 0; j < types.length; j++) {
             if (types[j].equalsIgnoreCase(mimeType)) {
                 return codecInfo;
             }
         }
     }
     return null;
 }

(2) 配置、启动编/解码器

编解码器配置使用的是MediaCodec的configure方法,该方法首先对MediaFormat存储的数据map进行提取,然后调用本地方法native-configure实现对编解码器的配置工作。在配置时,configure方法需要传入format、surface、crypto、flags参数,其中format为MediaFormat的实例,它使用”key-value”键值对的形式存储多媒体数据格式信息;surface用于指明解码器的数据源来自于该surface;crypto用于指定一个MediaCrypto对象,以便对媒体数据进行安全解密;flags指明配置的是编码器(CONFIGURE_FLAG_ENCODE)。

MediaFormat mFormat = MediaFormat.createVideoFormat("video/avc", 640 ,480); // 创建MediaFormat
mFormat.setInteger(MediaFormat.KEY_BIT_RATE,600); // 指定比特率
mFormat.setInteger(MediaFormat.KEY_FRAME_RATE,30); // 指定帧率
mFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,mColorFormat); // 指定编码器颜色格式 
mFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,10); // 指定关键帧时间间隔
mVideoEncodec.configure(mFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE); 

以上代码是在编码H.264时的配置方法,createVideoFormat(“video/avc”, 640 ,480)为”video/avc”类型(即H.264)编码器的MediaFormat对象,需要指定视频数据的宽高,如果编解码音频数据,则调用MediaFormat的createAudioFormat(String mime, int sampleRate,int channelCount)的方法。除了一些诸如视频帧率、音频采样率等配置参数,这里需要着重讲解一下MediaFormat.KEY_COLOR_FORMAT配置属性,该属性用于指明video编码器的颜色格式,具体选择哪种颜色格式与输入的视频数据源颜色格式有关。比如,我们都知道Camera预览采集的图像流通常为NV21或YV12,那么编码器需要指定相应的颜色格式,否则编码得到的数据可能会出现花屏、叠影、颜色失真等现象。MediaCodecInfo.CodecCapabilities.存储了编码器所有支持的颜色格式,常见颜色格式映射如下:

原始数据 编码器 
NV12(YUV420sp) ———> COLOR_FormatYUV420PackedSemiPlanar 
NV21 ———-> COLOR_FormatYUV420SemiPlanar 
YV12(I420) ———-> COLOR_FormatYUV420Planar 

当编解码器配置完毕后,就可以调用MediaCodec的start()方法,该方法会调用低层native_start()方法来启动编码器,并调用低层方法ByteBuffer[] getBuffers(input)来开辟一系列输入、输出缓存区。start()方法源码如下:

public final void start() {
        native_start();
        synchronized(mBufferLock) {
            cacheBuffers(true /* input */);
            cacheBuffers(false /* input */);
        }
 }

(3) 数据处理

MediaCodec支持两种模式编解码器,即同步synchronous、异步asynchronous,所谓同步模式是指编解码器数据的输入和输出是同步的,编解码器只有处理输出完毕才会再次接收输入数据;而异步编解码器数据的输入和输出是异步的,编解码器不会等待输出数据处理完毕才再次接收输入数据。这里,我们主要介绍下同步编解码,因为这种方式我们用得比较多。我们知道当编解码器被启动后,每个编解码器都会拥有一组输入和输出缓存区,但是这些缓存区暂时无法被使用,只有通过MediaCodec的dequeueInputBuffer/dequeueOutputBuffer方法获取输入输出缓存区授权,通过返回的ID来操作这些缓存区。下面我们通过一段官方提供的代码,进行扩展分析:

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();

从上面代码可知,当编解码器start后,会进入一个for(;;)循环,该循环是一个死循环,以实现不断地去从编解码器的输入缓存池中获取包含数据的一个缓存区,然后再从输出缓存池中获取编解码好的输出数据。

参考资料

Android MediaCodec实现多段音视频的截取与拼接
http://qkxue.net/info/170238/Android-MediaCodec

安卓解码器MediaCodec解析
https://rangaofei.github.io/2018/03/09/%E5%AE%89%E5%8D%93%E8%A7%A3%E7%A0%81%E5%99%A8MediaCodec%E8%A7%A3%E6%9E%90/

Android直播开发之旅(8):Android硬编解码接口MediaCodec原理剖析
https://blog.csdn.net/andrexpert/article/details/79578149

Android MediaCodec编解码详解及demo
https://www.jianshu.com/p/e6c683d6dbbe

Android 音视频开发(六): MediaCodec API 详解
https://www.cnblogs.com/renhui/p/7478527.html

Android 音视频开发(五):使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件
https://www.cnblogs.com/renhui/p/7474096.html

Android硬编解码接口MediaCodec使用完全解析(一)
https://blog.csdn.net/u013028621/article/details/62417181/

android中MediaCodec类解析
https://www.jianshu.com/p/14da1baaf08f

Android音视频开发-入门(四):使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件
https://www.jianshu.com/p/26c5305da513

Android分离合成音视频(用MediaExtractor和MediaMuxer)
https://blog.csdn.net/k_bb_666/article/details/79175510

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/125018.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 【mysql系列】细谈explain执行计划之“谜”

    【mysql系列】细谈explain执行计划之“谜”目录理论Part概念实践Partidselect_typetabletypepossible_keyskeykey_lenrefrowsfilteredExtra总结聊到mysql数据库的优化,大家基本都会谈论explain关键字,确认sql是否使用数据库表中建立的索引,然后讨论sql语句或者索引优化方案等等~,那本篇文章主要谈论一下explain的理解。主要分为理论和实践相结合。理论Part概念我们先了解一下explain语法和相关理论知识。语法:EXPLAINSELECTselect_o

    2025年6月16日
    3
  • ios-anchorPoint、position理解[通俗易懂]

    ios-anchorPoint、position理解[通俗易懂]anchorPoint(锚点)是layer的一个属性,下面我们来看看其对view的影响,本文主要通过图片方式展现:锚点用单位坐标来描述也就是图层的相对坐标。在苹果文档中说明左下是(0,0),右上是(1,1),经过试验这个是错的,实际上:图层左上角是{0,0},右下角是{1,1},取值范围不限。单位坐标的意思是指以视图长款为单位来计算,坐标系展示如下:默认一个视图的锚

    2022年10月8日
    3
  • Django(3)pycharm创建项目[通俗易懂]

    Django(3)pycharm创建项目[通俗易懂]创建项目我们创建django项目有两种方式,命令行方式和使用pycharm工具创建,本文就介绍常用的pycharm工具创建首先点击django,输入项目的名称,选择创建好的虚拟环境,最后点击cre

    2022年7月28日
    4
  • CentOS7下安装mysql5.7[通俗易懂]

    CentOS7下安装mysql5.7[通俗易懂]更多精彩技术分享请浏览本人博客:https://blog.csdn.net/wohiusdashi一、安装YUMRepo1、由于CentOS的yum源中没有mysql,需要到mysql的官网下载yumrepo配置文件。下载命令:wgethttps://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm…

    2022年4月19日
    130
  • django项目配置使用elasticsearch搜索引擎

    django项目配置使用elasticsearch搜索引擎Elasticsearc 简称 ES 是一个基于 Lucene 实现的开源 分布式 Restful 的全文本搜索引擎 此外 它还是一个分布式实时文档存储 其中每个文档的每个 field 均是被索引的数据 且可被搜索 也是一个带实时分析功能的分布式搜索引擎 能够扩展至数以百计的节点实时处理 PB 级的数据 基本组件索引 index 文档容器 换句话说 索引是具有类似属性的文档的集合 类似新华字典的索引检索页 里面包含了关键词与词条的对应关系 并记录词条的位置 索引名必须使用小写字母 搜索

    2025年8月4日
    3
  • table文字水平垂直居中

    table文字水平垂直居中lt table gt nbsp nbsp lt tr gt nbsp nbsp nbsp nbsp lt tdstyle text align center vertical align middle gt nbsp nbsp nbsp nbsp nbsp nbsp 文字内容 nbsp nbsp nbsp nbsp nbsp nbsp text align center 设置水平居中 nbsp nbsp nbsp nbsp nbsp nbsp vertical align middle 设置垂直居中 nbsp nbsp nbsp nbsp

    2025年12月14日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号