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

相关推荐
万能的小裴同学8 小时前
Android M3U8视频播放器
android·音视频
音视频牛哥8 小时前
轻量级RTSP服务的工程化设计与应用:从移动端到边缘设备的实时媒体架构
人工智能·计算机视觉·音视频·音视频开发·rtsp播放器·安卓rtsp服务器·安卓实现ipc功能
q***57748 小时前
MySql的慢查询(慢日志)
android·mysql·adb
JavaNoober8 小时前
Android 前台服务 "Bad Notification" 崩溃机制分析文档
android
城东米粉儿9 小时前
关于ObjectAnimator
android
zhangphil10 小时前
Android渲染线程Render Thread的RenderNode与DisplayList,引用Bitmap及Open GL纹理上传GPU
android
火柴就是我11 小时前
从头写一个自己的app
android·前端·flutter
lichong95112 小时前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端
用户693717500138412 小时前
14.Kotlin 类:类的形态(一):抽象类 (Abstract Class)
android·后端·kotlin
火柴就是我13 小时前
NekoBoxForAndroid 编译libcore.aar
android