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

Android媒体解码MediaCodec,MediaExtractor相关推荐

  1. android jni 硬解码,Android 硬解码 MediaCodec 遇到的(部分手机绿屏)API21

    MediaCodec 是android用来做音视频编解码 下面是遇到其它的问题(解码几分钟后一直dequeueInputBuffer-1) http://blog.csdn.net/qq3773663 ...

  2. Android 音视频编解码 MediaCodec

    MediaCodec 简介 Android中的MediaCodec是一个用于音视频编解码功能的API,使用它可以实现对音视频数据进行压缩.解压缩.编辑和转换.以下是MediaCodec的主要功能: 支 ...

  3. 【AVD】FFmpeg + MediaCodec 实现 Android 硬件解码,中间有个大坑

    最近在做移动端音视频编解码,首先要实现的是移动端视频的解码功能.纯的 FFmpeg 方法在移动端也能实现,但是效率上的确要慢一些,1080p 的视频还好,但是上到 2k.4k,那个解码速度(以肉眼可见 ...

  4. Android中使用MediaCodec视频编码异步实现

    Android中使用MediaCodec进行视频编解码异步实现 简单的介绍一下MediaCodec:本文主要讲述的是博主自己在用MediaCodec进行编解码过程中分别用同步和异步两种方式实现了硬编解 ...

  5. Android音视频-MediaCodec

    原文:https://mp.weixin.qq.com/s?__biz=MzU3NTA3MDU1OQ==&mid=2247484865&idx=1&sn=174b8ca7024 ...

  6. android m4a播放器,如何在android上解码m4a音频

    我试图在android上解码音频并获取原始数据以应用过滤器. 所以我首先用 extractor.getTrackFormat(0); MediaExtractor extractor = new Me ...

  7. Android 媒体 IV-支持的媒体格式

    概述: 该文档介绍了Android平台可以支持的媒体编解码, 容器和网络协议. 作为一个APP开发者, 我们可以自由的使用任何Android设备上的媒体编解码器, 包括那些Android提供的和设备指 ...

  8. android 播放器 wav 无法播放,对于Android媒体播放器mp3与wav(For android media player mp3 vs. wav)...

    对于Android媒体播放器mp3与wav(For android media player mp3 vs. wav) 我想知道在Android媒体播放器上加载和播放小wav是否比较快的小文件更快. ...

  9. 全新的基于 Windows NT (2000/XP/2003) 的媒体解码包.

    Zoom Player Platinum Stream Codec ( 绚彩魅影) 2.1 Build 104 全新的基于 Windows NT (2000/XP/2003) 的媒体解码包.它包含了支 ...

最新文章

  1. 简单介绍Lua中三种循环语句的使用
  2. server sql 分组 去重 字符串拼接_SQL | 数据分析面试必备SQL语句+语法
  3. python怎么限制输出精度_谈谈关于Python里面小数点精度控制的问题
  4. keyshot手机渲染教程_keyshot灯光渲染基础入门教程【英】
  5. (转)百度文库浏览器分析及实现(续)
  6. 五款常用邮件管理系统评测
  7. sklearn中常用的数据预处理方法
  8. 软件测试之python面试题_常见Python面试题整理带答案
  9. L323 英语有必要学语法吗
  10. 基于Java生鲜蔬菜食品商城系统详细设计和实现
  11. 52个比付费软件更好的免费软件
  12. pymol怎么做底物口袋表面_高质量PyMOL作图教程
  13. fabs () 函数
  14. NTP客户端配置-Windows时间同步设置
  15. 数学建模竞赛常用软件培训2
  16. Java自学之异常处理——自学笔记
  17. 2014智联卓聘积分获取新攻略
  18. python下载油管、B站视频的方法
  19. Android 机型适配之百分比适配 ConstraintLayout
  20. 医疗供应链-药品SPD管理

热门文章

  1. c语言gcc汇编文件,[汇编]gcc生成汇编.s文件 $ gcc -Og -S sum.c /$ gcc -Og -S -masm=intel sum.c 与 反汇编 objdump...
  2. Promise学习笔记
  3. LeetCode 1973. Count Nodes Equal to Sum of Descendants(DFS)
  4. LeetCode 1819. 序列中不同最大公约数的数目
  5. ACwing 2. 01背包问题(DP)
  6. LeetCode 319. 灯泡开关(找规律)
  7. LeetCode 26. 删除排序数组中的重复项
  8. Android8.1怎么装谷歌,谷歌PixelXL安卓9.0/8.1/8.0/7.X安装面具ROOT方案
  9. .net pdf转图片_pdf2image类库实现批量pdf转图片
  10. python求超级素数代码_C语言求超级素数