Android音视频-MediaCode解码播放MP4

简介

日常开发中,我们都习惯于使用第三方播放器进行音视频的播放,如常用的ijkPlayer,Gsyvideoplayer等三方库,本篇文章给大家带来Android原生的视频解码器(MediaCode)对MP4进行解码播放,帮助读者更加深入的学习视频播放原理。

MediaCode是Android原生底层多媒体框架,通常与一起使用MediaExtractor , MediaSync , MediaMuxer , MediaCrypto , MediaDrm , Image , Surface ,和AudioTrack。MediaCode使用底层硬件编解码,编解码速度快,效率高。

MediaCode解码流程

先了解MediaCode对视频解码播放的整个流程。具体解码流程如下:

相关API使用

java 复制代码
//获取可用的输入缓冲区的索引
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)

解码流程

  1. 创建创建视频多媒体提取器并设置视频路径
scss 复制代码
//创建视频多媒体提取器
 val mVideoExtractor = MediaExtractor()
 //设置视频多媒体路径
 val mVideoExtractor.setDataSource(videoPath)
 
  1. 获取视频的编码格式及视频轨道
ini 复制代码
for (index in 0 until mVideoExtractor.trackCount) {
            val format = mVideoExtractor.getTrackFormat(index)
            val keyMine = format.getString(MediaFormat.KEY_MIME)
            if (keyMine?.startsWith("video/") == true) {
                mVideoTrackIndex = index
                mVideoKeyType = keyMine
                mVideoFormat = format
                continue
            }
        }
  1. 创建 MediaCodec 对象,指定编码器或解码器类型
ini 复制代码
  //创建视频解码器
     mVideoDecoder = MediaCodec.createDecoderByType(mVideoKeyType)
  1. 配置解码器参数,例如SurfaceView,编码格式、分辨率、帧率、码率等
scss 复制代码
   //设置视频轨道
   mVideoExtractor.selectTrack(mVideoTrackIndex)
  //配置解码器参数
  mVideoDecoder.configure(mVideoFormat, MediaCodecVideoActivity.getSurface(), null, 0)
  1. 开始执行解码任务,调用 start() 方法启动解码器。
scss 复制代码
//开始执行解码
mVideoDecoder.start()
  1. 开启线程将解码出来的视频流刷新到SurfaceView上进行显示
ini 复制代码
 if (!isPlay) {
                        //获取可用的输入缓冲区的索引
                        val inputIndex = mVideoDecoder.dequeueInputBuffer(-1)
                        //缓冲区有数据
                        if (inputIndex > 0) {
                            //读取缓冲区数据
                            val byteBuffer = mVideoDecoder.getInputBuffer(inputIndex)
                            //读取一片或一帧数据
                            byteBuffer?.let {
                                var sampleSize = mVideoExtractor.readSampleData(it, 0)
            
                                if (sampleSize < 0) {
                                    // sampleSize = 0;
                                    // isFinishDecode = true
                                    mVideoExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
                                } else {
                                    //读取时间戳
                                    val time = mVideoExtractor.sampleTime
                                    //读取一帧数据
                                    mVideoDecoder.queueInputBuffer(inputIndex, 0, sampleSize, time, 0)
                                    /**
                                     * 进入下一个样本。如果没有更多的样本数据可用,返回false(流结束)
                                     * 。当提取本地文件时,
                                     * advance和readSampleData的行为在并发写同一个本地文件时是未定义的
                                     * ;更具体地说,流结束的信号可能比预期的要早。
                                     */
                                    mVideoExtractor.advance()
                                }

                            }
                        }
                        val bufferInfo = BufferInfo()
                        val outIndex = mVideoDecoder.dequeueOutputBuffer(bufferInfo, 0)
                        if (outIndex >= 0) {
                            /* if (bufferInfo.flags == BUFFER_FLAG_END_OF_STREAM){
                                 isFinishDecode = true
                             }*/
                            mVideoDecoder.releaseOutputBuffer(outIndex, true)

                        }
                        if (bufferInfo.flags == BUFFER_FLAG_END_OF_STREAM) {
                            mVideoExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)
                        }
                    }
  1. 释放资源
scss 复制代码
 if (mVideoDecoder!=null){
        mVideoDecoder.stop()
        mVideoDecoder.release()
     }
 mVideoExtractor.release()

实现效果

本人Demo实现效果如下:

完整代码已经上传GitHub,需要的小伙伴可以私聊小编。

相关推荐
小比卡丘1 小时前
C语言进阶版第17课—自定义类型:联合和枚举
android·java·c语言
前行的小黑炭2 小时前
一篇搞定Android 实现扫码支付:如何对接海外的第三方支付;项目中的真实经验分享;如何高效对接,高效开发
android
落落落sss3 小时前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
代码敲上天.4 小时前
数据库语句优化
android·数据库·adb
GEEKVIP6 小时前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
model20058 小时前
android + tflite 分类APP开发-2
android·分类·tflite
彭于晏6898 小时前
Android广播
android·java·开发语言
与衫9 小时前
掌握嵌套子查询:复杂 SQL 中 * 列的准确表列关系
android·javascript·sql
500了16 小时前
Kotlin基本知识
android·开发语言·kotlin
人工智能的苟富贵16 小时前
Android Debug Bridge(ADB)完全指南
android·adb