Android MediaCodec 完全解析

article/2025/10/6 20:21:01

MediaCodec是什么?

MediaCodec类为开发者提供了能访问到Android底层媒体Codec(Encoder/Decoder)的能力,它是Android底层多媒体基础架构的一部分(通常和MediaExtractor、MediaSync、MediaMuxer、MediaCrypto、MediaDrm、Image、Surface、AudioTrack一起使用)。
Codec示意图
从广义上来讲,Codec就是处理输入数据产生输出数据。它使用一组输入、输出缓冲区异步的处理数据,简而言之,你请求一个空的input Buffer,给它填充数据,然后将这个buffer送入Codec进行处理,Codec会消耗掉输入的数据,然后将InputBuffer转换成一空的Buffer以供下次请求时使用。最后,你请求一个填充满数据的outputBuffer,消费掉Buffer里的内容,然后释放掉这个Buffer返回给Codec

数据类型

Codec对三种类型类型的数据起作用:编码后的压缩数据,原始视频数据,原始音频数据。这三种类型的数据都可以通过ByteBuffer来传递给Codec,但是对于原始视频数据我们建议使用Surface来传递,这样可以提高Codec的性能,Surface使用的是native video buffer,不用映射或者拷贝成ByteBuffer,因此这样的方式更高效。当你使用Surface来传递原始视频数据时,也就无法获取到了原始视频数据,Android 提供了ImageReader帮助你获取到解码后的原始视频数据。这种方式可能仍然有要比ByteBuffer的方式更加高效,因为某些native video buffer会直接映射成byteBuffer。当然如果你ByteBuffer的模式,你可以使用Image类提供的getInput/OutputImage(int)来获取原始视频数据

压缩数据Buffer

Decoder输入的InputBuffer或者Encoder输出的outputBuffer包含的都是编码后的压缩数据,数据的压缩类型由MediaFormat#KEY_MIME指明。对于视频类型而言,这个数据通常是一个压缩后的视频帧。对于音频数据而言,通常是一个访问单元(一个编码的音频段,通常包含几毫秒的音频数据,数据类型format type 指定),有时候,一个音频单元对于一个buffer而言可能有点宽松,所以一个buffer里可能包含多个编码后的音频数据单元。无论Buffer包含的是视频数据还是音频数据,Buffer都不会再任意字节边界上开始或者结束,而是在帧(视频)或者单元(音频)的边界上开始或者结束。除非它们被BUFFER_FLAG_PARTIAL_FRAME标记。

原始音频Buffer

原始音频Buffer包含PCM音频数据的整个帧,是每一个通道按着通道顺序的采样数据。每一个采样按16Bit量化。

short[] getSamplesForChannel(MediaCodec codec , int bufferId , int channelIx){ByteBuffer outputBuffer = codec.getOutputBuffer(bufferId);MediaFormat format = codec.getOutputFormat(bufferId);ShortBuffer samples = oputBuffer.order(ByteBuffer.nativeOrder()).asShortBuffer();int numChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);if(channelIx <0 || channelIx >= numChannels){return null;}shor[] 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模式下,视频数据的排布由MediaFormat#KEY_COLOR_FORMAT指定,我们可以通过getCodecInfo().MediaCodecInfo#getCapabilitiesForType.CodecCapabilities#colorFormats获取到一个设备支持的color format数组。视频Codec可能支持三种类型的Color Format:

  • native raw video format:由CodecCapabilities#COLOR_FormatSurface标记,可以用做Input/output Surface.
  • flexible YUV buffer(例如:CodecCapabilities#COLOR_FormatYUV420Flexible):这些buffer能用作input/output surface,同时也能用于byte buffer模式,通过getInput/OutputBuffer(int)
  • 其他指定的格式:这些数据通常只支持byteBuffer模式。

Build.VERSION_CODES.LOLLIPOP_MR1开始所有的视频Codec都支持 flexible YUV 4:2:0

在一些老设备上如何获取原始视频数据buffer

对于Build.VERSION_CODES.LOLLIPOP之前并且支持Image类时,我们需要使用MediaFormat#KEY_STRIDEMediaFormat#KEY_SLICE_HEIGHT的值去理解输出的原始视频数据的布局。

注意:在某些设备上slice-height被标记为0,这可能意味着slice-height的值和frame height的值相同,或者和frame height按着2的幂对齐。很遗憾,没有一个标准或者简单的方式告诉我们slice-height的真实值。此外,在planar 格式下U plane的stride也没有明确的指定,虽然通常它是slice height的一半。

键值MediaFormat#KEY_WIDTHMediaFormat#KEY_HEIGHT指明了视频Frame的size。然而,对于大多数用于编码的视频图像,他们只占用了video Frame的一部分。这部分用一个'crop rectangle来表示。

我们需要用下面的一些keys从获取原始视频数据的crop rectangle,如果out format中没有包含这些keys,则表示视频占据了整个video Frame,这个crop rectangle的解释应该立足于应用任何MediaFormat#KEY_ROTATION之前。

Format KeyTypeDescription
“crop-left”IntegerThe left-coordinate (x) of the crop rectangle
“crop-top”IntegerThe top-coordinate (y) of the crop rectangle
“crop-right”IntegerThe right-coordinate (x) MINUS 1 of the crop rectangle
“crop-bottom”IntegerThe bottom-coordinate (y) MINUS 1 of the crop rectangle

下面是在旋转之前计算视频的尺寸的案例:

MediaFormat format = decoder.getOutputFormat(...);
int width = format.getInteger(MediaFormat.KEY_WIDTH);
if(format.containsKey("crop-left") && format.containsKey("crop-right")){width = format.getInteger("crop-right")+1 - format.getInteger("crop-left");
}
int height = format.getInteger(MediaFormat.KEY_WIDTH);
if(format.containsKey("crop-top") && format.containsKey("crop-bottom"){height = format.getInteger("crop-bottom")+1 - format.getInteger("crop-top"); 
}

Codec的状态

从概念上讲Codec的声明周期存在三种状态:StopedExecutingReleasedStoped状态是一个集合状态,它聚合了三种状态:UninitializedConfigured,和Error,同时Executing状态的处理也是通过三个子状态来完成:FlushedRunningEnd-of-Stream

Codec状态转换图
当我们用工厂方法创建出一个Codec后,Codec就处于Uninitialized状态,首先我们需要通过Configure(...)去Configure这个Codec,这将会将Codec转换成Configured状态,然后我们可以调用start()函数,将Codec转换成Executing状态,在这个状态下我们才能通过buffer处理数据。

Executing状态有三个子状态:Flushed,Running,和End-of-Stream,当我们调用玩Start()函数后,Codec就立刻进入Flushed子状态,这个状态下,它持有全部的buffer,只要第一个Input buffer被dequeued,Codec就转变成Running子状态,这个状态占据了Codec的生命周期的绝大部分。当入队一个带有 end-of-stream标志的InputBuffer后,Codec将转换成End of Stream子状态,在这个状态下,Codec将不会再接收任何输入的数据,但是仍然会产生output buffer ,直到end-of-Stream标记的buffer被输出。我们可以在Executing状态的任何时候,使用flush()函数,将Codec切换成Flushed状态。

调用stop()函数会将Codec返回到Uninitialized状态,这样我们就可以对Codec进行重新配置,当你用完了Codec后,你必须要调用release()函数去释放这个Codec

在极少数情况下,Codec可能也会遇到错误,此时Codec将会切换到Error状态,我们可以通过queuing操作获取到一个无效的返回值,或者有时会通过异常来的得知Codec发生了错误。通过调用reset()函数,将Codec进行重置,这样Codec将切换成Uninitalized状态,我们可以在任何状态下调用rest()函数将Codec将切换成Uninitalized`状态。

Codec的创建

使用MediaCodecList创建一个指定MediaFormat的MediaCodec。当我们解码一个文件或者一个流时,我们可以通过MediaExtractor#getTrackFormat获取期望的Fromat,同时我们可以通过MediaFormat#setFeatureEnabledCodec注入任何我们想要的特性。然后调用MediaCodecList#findDecoderForFormat获取能够处理对应format数据Codec的name,最后我们使用createByCodecName(String)创建出这个Codec

注意:在 Build.VERSION_CODES.LOLLIPOP ,MediaCodecList.findDecoder/EncoderForFormat 获取的format不能包含MediaFormat#KEY_FRAME_RATE,用format.setString(MediaFormat.KEY_FRAME_RATE, null)这个函数可以清楚已经在这个format中设置的frame rate。

我们也可以使用createDecoder/EncoderByType(java.lang.String)函数来创建指定的MIME类型的Codec,但是这样我们无法向其中注入一些指定的特性,这样创建的Codec可能不能处理我们期望的媒体类型数据。

创建安全的Decoder


http://chatgpt.dhexx.cn/article/AtDYTGr0.shtml

相关文章

Android MediaCodec解析

Android MediaCodec解析 1 引言 MediaCodec是Android平台提供的一个底层的音视频编解码框架&#xff0c;它是安卓底层多媒体基础框架的重要组成部分。它经常和 MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, AudioTrack一起使用。解码的作…

Android原生编解码接口MediaCodec详解

作者&#xff1a;躬行之 了解了音视频的相关知识&#xff0c;可以先阅读同系列文章&#xff1a; 音视频开发基础知识音频帧、视频帧及其同步Camera2、MediaCodec录制mp4 MediaCodec 是 Android 中的编解码器组件&#xff0c;用来访问底层提供的编解码器&#xff0c;通常与 Me…

MediaCodec视频解码流程详解及参考demo

一、MediaCodec简介 MediaCodec是Android自带的底层多媒体支持架构的一部分&#xff08;通常与 MediaExtractor&#xff0c;MediaSync&#xff0c;MediaMuxer&#xff0c;MediaCrypto&#xff0c;MediaDrm&#xff0c;Image&#xff0c;Surface 和 AudioTrack 一起使用&#xf…

Android MediaCodec简单总结

#.MedaiCodec简介 MediaCodec是Android中提供的音视频编码、解码工具。它主要是完成上层接口的封装&#xff0c;提供给开发者使用&#xff0c;编解码功能实际是在native底层服务中完成的。 #.MediaCodec工作的宏观流程&#xff1a; ##.包换两个缓冲区队列 一个输入缓冲区队列&a…

软件测试面试指导之自我介绍

面试自我介绍虽然人人都准备&#xff0c;但是做到让人印象深刻可不容易啊。 本篇就具体来聊聊人人都要经历的面试&#xff0c;怎么做自我介绍&#xff0c;才能让你在面试官的眼睛里像金子一样闪闪发光&#xff1f; 面试是什么&#xff1f; 它是个机会&#xff0c;让面试官更…

软件测试面试要注意的细节以及处理(自我介绍篇)

面试问题第一问&#xff0c;95%都会是&#xff1a; 请简单的做个自我介绍吧~ 分以下几点说明。 一、个人的基本信息&#xff0c;扬长避短 1、年纪太大与太小&#xff0c;都不需要主动去说明。 比如我年纪只有21岁 例子&#xff1a;面试官您好&#xff0c;我叫***&#xff…

【软件测试】企业测试面试题9道,从自我介绍到项目考察+回答......

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、自我介绍 您好…

软件测试面试怎样介绍自己的项目?会问到什么程度?

最近收到很多粉丝的私信说找不到工作&#xff0c;简历投了百十来份&#xff0c;邀约都没几个&#xff0c;更别说offer了&#xff0c;是不是软件测试要黄了&#xff1f; 说句实话&#xff0c;现在大环境确实不好&#xff0c;互联网大厂裁员这是摆在明面上的原因。时代的一粒沙&…

软件测试面试怎样介绍自己的测试项目?会问到什么程度?

想知道面试时该怎样介绍测试项目&#xff1f;会问到什么程度&#xff1f;那就需要换位思考&#xff0c;思考HR在这个环节想知道什么。 HR在该环节普遍想获得的情报主要是下面这2个方面&#xff1a; 1&#xff09;应聘者的具体经验和技术能力&#xff0c; 2&#xff09;应聘者的…

【软件测试】面试中介绍项目你该这么说!

黑马程序员视频库 播妞微信号&#xff1a;heiniu526 传智播客旗下互联网资讯、学习资源免费分享平台 测试人员在找工作的过程中&#xff0c;通常有一个问题是很难绕开的。就是要如何向别人介绍自己之前做过的项目。下面我们就这个问题简单的做一些分析。 大体上可以分为如下几个…

软件测试面试自我介绍/项目介绍居然还有模板?我要是早点发现就好了

目录 1、自我介绍 2、项目介绍 2.1、最全电商项目介绍 2.2、电商项目介绍 2.3、在线教育项目介绍 2.4、互联网金融项目介绍 总结 1、自我介绍 以XXX简历来举例&#xff08;参照下面的案例&#xff0c;编写你的自我介绍&#xff0c;框架就是&#xff1a;我是谁&#xff0…

软件测试面试,如何自我介绍?如何介绍项目?如何介绍个人技术?(提供面试话术)

前准备不足而导致面试失败那可就亏大了&#xff01;为了提高面试成功率&#xff0c;帮助大家尽快拿到高薪offer&#xff0c;我们盘点了面试环节必问的三类问题&#xff0c;希望对即将参加软件测试面试的小伙伴们有所帮助。 01 如何自我介绍 面试过程中一定要放慢语速&#xf…

软件测试面试,如何自我介绍?

01 如何自我介绍 面试过程中一定要放慢语速&#xff0c;做到条理清晰。特别是做自我介绍时&#xff0c;可以适当多介绍自己会什么&#xff0c;有哪些重要经验。 例如&#xff1a; 面试官&#xff0c;上午/下午好。 我是XXX&#xff0c;今天来面试贵公司的软件测试工程师岗位&a…

软件测试工程师面试如何做好自我介绍?

听了很多提问者和我的学生&#xff0c;在做自我自我介绍的时候&#xff0c;一般存在的问题&#xff1a; 1、表述不太流畅。多练习表述&#xff0c;自己录音&#xff0c;听回放&#xff0c;有问题改正。 2、表述太溜&#xff0c;语速太快。不自信表现&#xff0c;隐含紧张的情绪…

软件测试三分钟自我介绍

目录 一、个人的基本信息&#xff0c;扬长避短 二、突出自己的工作经验 三、突出自己的技能 四、个人兴趣爱好与结尾 自我介绍在面试中常常作为第一个问题而出现&#xff0c;好的自我介绍可以带来良好的第一印象&#xff0c;如何讲好自我介绍呢。我建议从三部分下手 一、个…

软件测试面试指导之自我介绍 (干货)

面试自我介绍虽然人人都准备&#xff0c;但是做到让人印象深刻可不容易啊。 本篇就具体来聊聊人人都要经历的面试&#xff0c;怎么做自我介绍&#xff0c;才能让你在面试官的眼睛里像金子一样闪闪发光&#xff1f; 面试是什么&#xff1f; 它是个机会&#xff0c;让面试官更…

软件测试面试必考题:自我介绍

面试问题第一问&#xff0c;95%都会是&#xff1a; 请简单的做个自我介绍吧~ 分以下几点说明。 一、个人的基本信息&#xff0c;扬长避短 1、年纪太大与太小&#xff0c;都不需要主动去说明。 比如我年纪只有21岁 例子&#xff1a;面试官您好&#xff0c;我叫***&#xff0…

软件测试工程师自我介绍(范本)

一、先介绍自己的基本信息&#xff0c;要注意扬长避短 1、年纪太大与太小&#xff0c;就不要主动去说明。 比如你的年纪只有20岁 例子&#xff1a;我叫***&#xff0c;从事软件测试工作有几年了。 2、不是计算机相关专业毕业的也不要过多的去提 比如你的专业是机械专业 例…

软件测试面试01:自我介绍

2.1 简单的自我介绍下 面试宫&#xff0c;您好&#xff0c;我叫XXX&#xff0c;来自于XXXX&#xff0c;目前从事软件测试工作&#xff0c;已经三年工作经验&#xff0c;个人性格&#xff0c;比较开朗&#xff0c;跟人关系比较好&#xff0c;做事也比较细心三年测试工作经验中&…

IntelliJ IDEA 打jar包后,运行没有反应

问题现象&#xff1a; 双击没有反应&#xff0c;CMD下运行java -jar xxx.jar 后提示:xxx.jar中没有主清单属性 问题分析: 1.可能是没有maven插件 spring-boot-maven-plugin。 2. 没有指定入口&#xff08;main方法&#xff09;&#xff0c;或者指定错误。 解决方法&#xf…