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

article/2025/10/6 20:16:17

一、MediaCodec简介

        MediaCodec是Android自带的底层多媒体支持架构的一部分(通常与 MediaExtractor,MediaSync,MediaMuxer,MediaCrypto,MediaDrm,Image,Surface 和 AudioTrack 一起使用)。可以用来访问底层媒体编解码器,即编码器/解码器的组件。

        使用MediaCodec编解码即我们常说的硬编解码,它的优势是使用底层硬件编解码,效率比软编解码FFmpeg要高出不少。但劣势是由于硬编解码依赖于手机厂商的硬件设计,导致不同机型的效果可能千差万别,不好管控和统一。

        因此在实际开发中,使用软编解码或硬编解码还是要看具体的实际场景。

二、MediaCodec生命周期

        MediaCodec 有三种状态,分别是执行(Executing)、停止(Stopped)和释放(Released),其中执行和停止分别有三个子状态,执行的三个字状态分别是 Flushed、Running 和 Stream-of-Stream,停止的三个子状态分别是 Uninitialized、Configured 和 Error,MediaCodec 生命周期示意图如下:

        同步模式下:

         异步模式下:

         当创建了 MediaCodec 之后,是处于未初始化的 Uninitialized 状态,调用 configure 方法之后就处于 Configured 状态,调用了 start 方法之后,就处于 Executing 状态。

        在 Executing 状态下开始处理数据,它又有三个子状态,分别是:Flushed、RunningEnd of Stream。当一调用 start 方法之后,就进入了 Flushed 状态,从输入缓冲区队列中取出一个缓冲区就进入了 Running 状态,当入队的缓冲区带有 EOS 标志时, 就会切换到 End of Stream 状态, MediaCodec 不再接受入队的缓冲区,但是仍然会对已入队的且没有进行编解码操作的缓冲区进行操作、输出,直到输出的缓冲区带有 EOS 标志,表示编解码操作完成了。

        在 Executing 状态下可以调用 flush 方法,使 MediaCodec 切换到 Flushed 状态。

        在 Executing 状态下可以调用 stop 方法,使 MediaCodec 切换到 Uninitialized 状态,然后再次调用 configure 方法进入 Configured 状态。另外,当调用 reset 方法也会进入到 Uninitialized 状态。

        当不再需要 MediaCodec 时,调用 release 方法将它释放掉,进入 Released 状态。

        当 MediaCodec 工作发生异常时,会进入到 Error 状态,此时还是可以通过 reset 方法恢复过来,进入 Uninitialized 状态。

三、MediaCodec解码流程

        下面将讲解使用mediacodec对mp4的视频文件进行解码的API使用流程。本文主要是采用同步的解码方式。

        解码流程工作模型:

 

1、创建mediacodec并初始化

        可通过createDecoderByType来创建mediacodec:

mediaCodec = MediaCodec.createDecoderByType("video/avc");

        上面表示创建了一个解码器,并指定了解码类型为avc的视频解码器。

        初始化主要是为了给这个解码器设置一些格式,配置等,如下完整的创建和初始代码:

    private void initMediaCodecSys() {try {//创建mediaCodec = MediaCodec.createDecoderByType("video/avc");//格式mediaFormat = MediaFormat.createVideoFormat("video/avc", 1280, 720);mediaExtractor = new MediaExtractor();//MP4 文件存放位置mediaExtractor.setDataSource(MainActivity.MP4_PLAY_PATH);Log.d(TAG, "getTrackCount: " + mediaExtractor.getTrackCount());for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {MediaFormat format = mediaExtractor.getTrackFormat(i);String mime = format.getString(MediaFormat.KEY_MIME);Log.d(TAG, "mime: " + mime);if (mime.startsWith("video")) {mediaFormat = format;mediaExtractor.selectTrack(i);}}} catch (IOException e) {e.printStackTrace();}Surface surface = MainActivity.getSurface();//配置mediaCodec.configure(mediaFormat, surface, null, 0);mediaCodec.start();}

2、视频解码线程

        视频解码线程主要内容就是进行解码的流程控制。这个阶段的相应API有如下:

 // 获取可用的输入缓冲区的索引public int dequeueInputBuffer (long timeoutUs)// 获取输入缓冲区public ByteBuffer getInputBuffer(int index)// 将填满数据的inputBuffer提交到编码队列public final void queueInputBuffer(int index,int offset, int size, long presentationTimeUs, int flags)// 获取已成功编解码的输出缓冲区的索引public final int dequeueOutputBuffer(BufferInfo info, long timeoutUs)// 获取输出缓冲区public ByteBuffer getOutputBuffer(int index)// 释放输出缓冲区public final void releaseOutputBuffer(int index, boolean render) 

        获取可用的输入缓冲区的索引:

int inputIndex = mediaCodec.dequeueInputBuffer(-1);

        获取输入缓冲区:

ByteBuffer byteBuffer = mediaCodec.getInputBuffer(inputIndex);

        读取相关数据:

//读取一片或者一帧数据
int sampSize = mediaExtractor.readSampleData(byteBuffer, 0);
//读取时间戳
long time = mediaExtractor.getSampleTime();

        将填满数据的inputBuffer提交到编码队列:

if (sampSize > 0 && time > 0) {mediaCodec.queueInputBuffer(inputIndex, 0, sampSize, time, 0);//读取一帧后必须调用,提取下一帧//控制帧率在30帧左右mSpeedController.preRender(time);mediaExtractor.advance();
}

        获取已成功编解码的输出缓冲区的索引:

BufferInfo bufferInfo = new BufferInfo();
int outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);

        输出缓冲区:

if (outIndex >= 0) {mediaCodec.releaseOutputBuffer(outIndex, true);
}

        完整的视频解码线程代码如下:

/*** Play the MP4 file Thread* 解码主流程*/
private class DecoderMP4Thread extends Thread {long pts = 0;@Overridepublic void run() {super.run();while (!isDecodeFinish) {int inputIndex = mediaCodec.dequeueInputBuffer(-1);Log.d(TAG, "inputIndex: " + inputIndex);if (inputIndex >= 0) {ByteBuffer byteBuffer = mediaCodec.getInputBuffer(inputIndex);//读取一片或者一帧数据int sampSize = mediaExtractor.readSampleData(byteBuffer, 0);//读取时间戳long time = mediaExtractor.getSampleTime();if (sampSize > 0 && time > 0) {mediaCodec.queueInputBuffer(inputIndex, 0, sampSize, time, 0);//读取一帧后必须调用,提取下一帧//控制帧率在30帧左右mSpeedController.preRender(time);mediaExtractor.advance();}}BufferInfo bufferInfo = new BufferInfo();int outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);if (outIndex >= 0) {mediaCodec.releaseOutputBuffer(outIndex, true);}}}
}

3、解码结束关闭回收

        完成解码播放后,对相关内存回收和关闭处理:

public void close() {try {Log.d(TAG, "close start");if (mediaCodec != null) {isDecodeFinish = true;try {if (mDecodeMp4Thread != null) {mDecodeMp4Thread.join(2000);}} catch (InterruptedException e) {Log.e(TAG, "InterruptedException " + e);}boolean isAlive = mDecodeMp4Thread.isAlive();Log.d(TAG, "close end isAlive :" + isAlive);mediaCodec.stop();mediaCodec.release();mediaCodec = null;mSpeedController.reset();}} catch (IllegalStateException e) {e.printStackTrace();}instance = null;
}

        至此,整个视频解码流程就讲解完毕。

四、demo运行

        本人写了一个最简单的demo,使用mediacodec对mp4文件进行纯视频的解码播放,采用同步方式。

        该demo的解码对象mp4文件是内置在了raw/video.mp4,如果需要替换视频文件,可以替换这里:

         demo运行效果如下:

         点击START进行播放:

         播放过程中点击STOP可以停止播放。

        完整例子已经放到github上,如下:

        https://github.com/weekend-y/mediacodec_demo/tree/master/MediaCodec_DecodeMP4

        


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

相关文章

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…

IDEA中如何导入jar包

IDEA中如何导入jar包&#xff0c;有两种方法。 此处的Directory eclipse中的folder 方法一&#xff1a; 方法二&#xff1a;

idea手动导入jar包并配置

标题&#xff1a;idea手动导入jar包并配置 1.右击项目&#xff0c;直接新建文件夹lib&#xff0c;然后将需要导入的jar&#xff0c;粘贴到lib中。2.file > project Structure > libraries > > java, 然后找到该项目所在的lib的我们粘贴进去的jar&#xff0c;点击…

IDEA中导入jar包失败如何解决

Project Structure 中的Libraries中出现报错。下面给出我的一点建议&#xff0c;希望可以帮助大家&#xff01; 首先点击左上角的&#xff0b;&#xff0c;点击java 接着它会导入你目录中的某个位置&#xff0c;选中ok。 然后我将我已有的都导入进去了&#xff0c;按住Ctrl&…