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,需要的小伙伴可以私聊小编。

相关推荐
数据猎手小k3 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小103 小时前
JavaWeb项目-----博客系统
android
风和先行4 小时前
adb 命令查看设备存储占用情况
android·adb
AaVictory.4 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
似霰5 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶5 小时前
Android——网络请求
android
干一行,爱一行5 小时前
android camera data -> surface 显示
android
断墨先生6 小时前
uniapp—android原生插件开发(3Android真机调试)
android·uni-app
无极程序员7 小时前
PHP常量
android·ide·android studio
萌面小侠Plus8 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机