Android媒体解码MediaCodec,MediaExtractor

Android提供了MediaPlayer播放器播放媒体文件,其实MediaPlyer只是对Android Media包下的MediaCodec和MediaExtractor进行了包装,方便使用。但是最好理解下Android媒体文件的解码,编码和渲染流程。

Shape Of My Heart.mp4

<source src="http://7xoquj.com1.z0.glb.clouddn.com/shape_of_my_heart.mp4" type="video/mp4">

使用android.media包下的MediaCodec和MediaExtractor实现一个简单的视频解码渲染。

使用到了:

  • MediaCodec:负责媒体文件的编码和解码工作,内部方法均为native
  • MediaExtractor:负责将指定类型的媒体文件从文件中找到轨道,并填充到MediaCodec的缓冲区中
  • AudioTrack:负责将解码之后的音频播放
  • SurfaceView:展示解码之后的视频

视频被播放主要分为以下步骤:

  1. 将资源加载到extractor
  2. 获取视频所在轨道
  3. 设置extractor选中视频所在轨道
  4. 创将解码视频的MediaCodec,decoder
  5. 开始循环,直到视频资源的末尾
  6. 将extractor中资源以一个单位填充进decoder的输入缓冲区
  7. decoder将解码之后的视频填充到输出缓冲区
  8. decoder释放输出缓冲区的同时,将缓冲区中数据渲染到surface

音频的播放类似,只多了AudioTrack部分,少了渲染到surface部分。

MediaCodec.releaseOutputBuffer(int outputBufferIndex, boolean render);

  • render为true就会渲染到surface

播放的控制,视频和音频各自拥有一个Thread。

    public void play() {isPlaying = true;if (videoThread == null) {videoThread = new VideoThread();videoThread.start();}if (audioThread == null) {audioThread = new AudioThread();audioThread.start();}}public void stop() {isPlaying = false;}

VideoThread

private class VideoThread extends Thread {@Overridepublic void run() {MediaExtractor videoExtractor = new MediaExtractor();MediaCodec videoCodec = null;try {videoExtractor.setDataSource(filePath);} catch (IOException e) {e.printStackTrace();}int videoTrackIndex;//获取视频所在轨道videoTrackIndex = getMediaTrackIndex(videoExtractor, "video/");if (videoTrackIndex >= 0) {MediaFormat mediaFormat = videoExtractor.getTrackFormat(videoTrackIndex);int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);//视频长度:秒float time = mediaFormat.getLong(MediaFormat.KEY_DURATION) / 1000000;callBack.videoAspect(width, height, time);videoExtractor.selectTrack(videoTrackIndex);try {videoCodec = MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME));videoCodec.configure(mediaFormat, surface, null, 0);} catch (IOException e) {e.printStackTrace();}}if (videoCodec == null) {Log.v(TAG, "MediaCodec null");return;}videoCodec.start();MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();ByteBuffer[] inputBuffers = videoCodec.getInputBuffers();
//            ByteBuffer[] outputBuffers = videoCodec.getOutputBuffers();boolean isVideoEOS = false;long startMs = System.currentTimeMillis();while (!Thread.interrupted()) {if (!isPlaying) {continue;}//将资源传递到解码器if (!isVideoEOS) {isVideoEOS = putBufferToCoder(videoExtractor, videoCodec, inputBuffers);}int outputBufferIndex = videoCodec.dequeueOutputBuffer(videoBufferInfo, TIMEOUT_US);switch (outputBufferIndex) {case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:Log.v(TAG, "format changed");break;case MediaCodec.INFO_TRY_AGAIN_LATER:Log.v(TAG, "解码当前帧超时");break;case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED://outputBuffers = videoCodec.getOutputBuffers();Log.v(TAG, "output buffers changed");break;default://直接渲染到Surface时使用不到outputBuffer//ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];//延时操作//如果缓冲区里的可展示时间>当前视频播放的进度,就休眠一下sleepRender(videoBufferInfo, startMs);//渲染videoCodec.releaseOutputBuffer(outputBufferIndex, true);break;}if ((videoBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {Log.v(TAG, "buffer stream end");break;}}//end whilevideoCodec.stop();videoCodec.release();videoExtractor.release();}}

获取指定类型媒体文件所在轨道

    //获取指定类型媒体文件所在轨道private int getMediaTrackIndex(MediaExtractor videoExtractor, String MEDIA_TYPE) {int trackIndex = -1;for (int i = 0; i < videoExtractor.getTrackCount(); i++) {//获取视频所在轨道MediaFormat mediaFormat = videoExtractor.getTrackFormat(i);String mime = mediaFormat.getString(MediaFormat.KEY_MIME);if (mime.startsWith(MEDIA_TYPE)) {trackIndex = i;break;}}return trackIndex;}

将缓冲区传递至解码器

    //将缓冲区传递至解码器private boolean putBufferToCoder(MediaExtractor extractor, MediaCodec decoder, ByteBuffer[] inputBuffers) {boolean isMediaEOS = false;int inputBufferIndex = decoder.dequeueInputBuffer(TIMEOUT_US);if (inputBufferIndex >= 0) {ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];int sampleSize = extractor.readSampleData(inputBuffer, 0);if (sampleSize < 0) {decoder.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);isMediaEOS = true;Log.v(TAG, "media eos");} else {decoder.queueInputBuffer(inputBufferIndex, 0, sampleSize, extractor.getSampleTime(), 0);extractor.advance();}}return isMediaEOS;}

音频的部分类似,完整源码请移步jiyangg/MediaPlaySimpleDemo

转载于:https://www.cnblogs.com/jiy-for-you/p/7282033.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/487860.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2020五大技术趋势一览!超自动化、人类增强技术、无人驾驶发展、机器视觉崛起、区块链实用化...

文章原载于 RichardLiu自动驾驶技术的发展近年来&#xff0c;自动驾驶技术一直在发展&#xff0c;特斯拉、英特尔等大公司在这一领域取得了长足的进展。虽然我们还没有达到L4级或L5级自动驾驶汽车的水平&#xff0c;但我们已经很接近了。为了解释每个级别的含义&#xff0c;下…

ansible执行mysql命令,Ansible常用命令(ad-hoc 命令)

Ansible提供两种方式去完成任务&#xff1a;1、ad-hoc命令2、写Ansible playbook脚本前者可以解决一些简单的任务&#xff0c;后者解决较复杂的任务(ad-hoc命令和ansible playbook的关系类似于在命令行敲入shell命令和写shell scripts两者之间的关系)。ansible命令应用基础语法…

Facebook 开源聊天机器人Blender,经94 亿个参数强化训练,更具“人情味”

来源&#xff1a;AI前线作者 | Kyle Wiggers编译 | Sambodhi策划 & 编辑 | 刘燕不久前&#xff0c;Facebook 开源了号称是全球最强大的聊天机器人 Blender&#xff0c;它标志着 Facebook 在 AI 领域的新进展&#xff1a;新的聊天机器人不仅解决了此前聊天机器人的固有缺点&…

微服务go还是java,Java微服务 vs Go微服务,究竟谁更强!?

前言Java微服务能像Go微服务一样快吗&#xff1f;这是我最近一直在思索地一个问题。去年8月份的the Oracle Groundbreakers Tour 2020 LATAM大会上&#xff0c;Mark Nelson和Peter Nagy就对此做过一系列基础的的测试用以比较。接下来就给大家介绍下。在程序员圈子里&#xff0c…

php类中双冒号和-的区别

就是为了区分对象的方法和属性&#xff0c;和是访问类的静态方法和静态变量&#xff0c;类的静态方法和静态变量是类公用的&#xff0c;不需要实例化也能访问&#xff0c;而对象的方法和属性是每个对象特有的&#xff0c;因此必须先实例化。其他语言如C,JAVA等也是一样的&#…

java用循环语法在窗体中显示文字,如果子窗体在窗体视图中,则访问2003循环当前页面不起作用...

我有一个包含多个子表单的表单 .我想有以下行为&#xff1a;按主窗体的最后一个字段中的tab键或子窗体的最后一个字段中焦点移动到下一个子窗体或根据定义的Tab顺序返回到主窗体 .为此&#xff0c;所有子表单和主表单都将Cycle属性设置为Current页面 .除了表单视图中显示的子表…

Defi安全-Mono攻击事件分析--etherscan+phalcon

MonoX攻击事件相关信息 在Ethereum和Polygon网络都发生了&#xff0c;攻击手段相同&#xff0c;以Ethereum为例进行分析&#xff1a; 攻击者地址&#xff1a;MonoX Finance Exploiter | Address 0xecbe385f78041895c311070f344b55bfaa953258 | Etherscan 攻击合约&#xff1a…

特斯拉自动驾驶靠自研芯片,国产智能汽车怎么办?

文章来源&#xff1a;量子位从这个月开始&#xff0c;特斯拉终于跌破30万&#xff0c;进入补贴行列&#xff0c;与国产汽车展开正面竞争。在国产汽车和特斯拉之间该如何选择&#xff0c;除了续航里程外&#xff0c;最重要的可能就是智能驾驶系统了。智能驾驶系统该怎么比较&…

Codeforces Round #263 (Div. 2) D. Appleman and Tree 树形dp

链接&#xff1a; http://codeforces.com/contest/462/problem/D 题意&#xff1a; 给定n个点的树&#xff0c; 0为根&#xff0c;下面n-1行表示每个点的父节点 最后一行n个数 表示每个点的颜色&#xff0c;0为白色&#xff0c;1为黑色。 把树分成若干个联通块使得每个联通块有…

【星·企业】竞逐卫星互联网“新基建”,「九天微星」完成2.7亿元B 轮融资

图为九天微星一箭七星“瓢虫系列”主星示意图来源&#xff1a;中科创星据了解&#xff0c;「九天微星」将用此轮融资在河北唐山和四川宜宾分别建设互联网卫星平台、载荷自动化产线&#xff0c;同时强化宽带通信系统研发能力&#xff0c;加速地面终端产品投产。九天微星卫星工厂…

java kafka搭建,Apache Kafka 安装步骤

概览安装过程总共分为 3 大块&#xff0c;第一 Java 环境不必多说&#xff0c;第二 Zookeeper 安装&#xff0c;第三 Kafka 安装。概念了解Kafka 有几个重要的概念需要先了解一下名词解释broker可以理解为 Kafka 所在的服务器ZooKeeper分布式服务框架在 Kafka 中的作用主要负责…

Java的for-each循环

for (循环变量类型 循环变量名称 : 要被遍历的对象) 例&#xff1a; for (String string:str) for(int i0;i<str.size();i){   String string str[i];} 上面代码简单点来描述就是把str里的内容依次遍历给string转载于:https://www.cnblogs.com/CYG7/p/7289497.html

3D原子映射有助于研究生命的起源

Chi Ma/Royal Ontario Museum来源&#xff1a;IEEE电气电子工程师一种相对较新的研究原子的技术可能会对地球上生命的起源提供更多的线索。科学家利用三维原子图&#xff08;特别是原子探针层析成像&#xff09;发现&#xff0c;太阳系中最古老的分子流体本可以支持氨基酸的发展…

java7 uri,细数Java8中那些让人纵享丝滑的文件操作

在丑陋的 Java I/O 编程方式诞生多年以后&#xff0c;Java终于简化了文件读写的基本操作。打开并读取文件对于大多数编程语言来是非常常用的&#xff0c;由于 I/O 糟糕的设计以至于很少有人能够在不依赖其他参考代码的情况下完成打开文件的操作。在 Java7 中对此引入了巨大的改…

混合现实未来的八大应用场景

来源&#xff1a;诚迈科技近日&#xff0c;福布斯发布了一篇关于混合现实未来的八个值得关注的领域和应用场景。文中提到&#xff1a;随着时间的推移&#xff0c;技术永远在不断进展&#xff0c;而未来技术中很可能是AR和VR的结合形式。有报告显示&#xff1a;2024年&#xff0…

也来分析为什么支付宝要做社交

也来分析为什么支付宝要做社交 近期。身边的同事和朋友似乎对使用支付宝有点“走火入魔”了。常常有人在支付宝里面请求加我为好友。大家在食堂吃饭的时候也在讨论今天加了谁谁谁为好友。一问才知道。原来假设加入好友达到一定的数量&#xff0c;将有机会在春节期间抢支付宝发出…

4个重要的量子理论实验综述

文章原载于&#xff1a;大数据01 量子理论量子理论是一个容易让人迷惑的理论&#xff0c;因此&#xff0c;如果我们想准确地了解其概况&#xff0c;就需要非常谨慎。与大多数自牛顿著作和其他17世纪晚期科学家著作问世以来出现的物理学成果一样&#xff0c;量子理论是一个以数学…

php中intval函数用法,php intval函数用法总结

语法&#xff1a;int intval ( $var, $base )参数&#xff1a;此函数接受两个参数&#xff0c;其中一个是必需的&#xff0c;而另一个是可选的。参数如下所述&#xff1a;$var&#xff1a;这是一个必需参数&#xff0c;用作需要转换为整数值的变量。$base&#xff1a;它是一个可…

转:mysql group by 用法解析(详细)

group by 用法解析 group by语法可以根据给定数据列的每个成员对查询结果进行分组统计&#xff0c;最终得到一个分组汇总表。 SELECT子句中的列名必须为分组列或列函数。列函数对于GROUP BY子句定义的每个组各返回一个结果。 某个员工信息表结构和数据如下&#xff1a; id name…

前沿|《细胞》:绕过眼睛植入幻觉,科学家成功在盲人脑海中呈现指定图像!...

该文章内容转载自学术头条对于全球 5000 多万盲人来说&#xff0c;重见光明是一个遥不可及的梦想。而为了与盲人朋友进行交互&#xff0c;我们发明了盲文&#xff0c;用各种凸起的字符集合来表达各种意思。但这种通过手指触摸来识别的方式太过低效。随着脑科学研究的深入&#…