【Android 音视频开发打怪升级:音视频硬解码篇】三、音视频播放:音视频同步

本文详细介绍了Android音视频硬解码的过程,包括数据流分离提取、视频播放、音频播放和音视频同步。通过封装Android原生提取器,实现音视频数据的提取,使用MediaCodec进行解码,配置Surface进行视频渲染,初始化AudioTrack播放音频。重点讲解了音视频同步的实现,以系统时间为同步信号源,确保音视频播放的同步性。文章还提供了简单的页面调用和播放示例。
摘要由CSDN通过智能技术生成

【声 明】

首先,这一系列文章均基于自己的理解和实践,可能有不对的地方,欢迎大家指正。
其次,这是一个入门系列,涉及的知识也仅限于够用,深入的知识网上也有许许多多的博文供大家学习了。
最后,写文章过程中,会借鉴参考其他人分享的文章,会在文章最后列出,感谢这些作者的分享。

码字不易,转载请注明出处!

教程代码:【Github传送门
目录
一、Android音视频硬解码篇:
二、使用OpenGL渲染视频画面篇
  • 1,初步了解OpenGL ES
  • 2,使用OpenGL渲染视频画面
  • 3,OpenGL渲染多视屏,实现画中画
  • 4,深入了解OpenGL之EGL
  • 5,OpenGL FBO数据缓冲区
  • 6,Android音视频硬编码:生成一个MP4
三、Android FFmpeg音视频解码篇
  • 1,FFmpeg so库编译
  • 2,Android 引入FFmpeg
  • 3,Android FFmpeg视频解码播放
  • 4,Android FFmpeg+OpenSL ES音频解码播放
  • 5,Android FFmpeg+OpenGL ES播放视频
  • 6,Android FFmpeg简单合成MP4:视屏解封与重新封装
  • 7,Android FFmpeg视频编码

本文你可以了解到

上一篇文章,主要讲了Android MediaCodec实现音视频硬解码的流程,搭建了基础解码框架。本文将讲解具体的音视频渲染,包括MediaCodec初始化、Surface初始化,AudioTrack初始化、音视频数据流分离提取等,以及非常重要的音视频同步。

在上一篇文章定义的解码流程框架基类中,预留了几个虚函数,留给子类初始化自己的东西,本篇,就来看看如何实现。

一、音视频数据流分离提取器

上篇文章,多次提到音视频数据分离提取器,在实现音视频解码器子类之前,先把这个实现了。

封装Android原生提取器

之前提过,Android原生自带有一个MediaExtractor,用于音视频数据分离和提取,接来下就基于这个,做一个支持音视频提取的工具类MMExtractor:

class MMExtractor(path: String?) {
   

    /**音视频分离器*/
    private var mExtractor: MediaExtractor? = null
    
    /**音频通道索引*/
    private var mAudioTrack = -1
    
    /**视频通道索引*/
    private var mVideoTrack = -1
    
    /**当前帧时间戳*/
    private var mCurSampleTime: Long = 0
    
    /**开始解码时间点*/
    private var mStartPos: Long = 0

    init {
   
        //【1,初始化】
        mExtractor = MediaExtractor()
        mExtractor?.setDataSource(path)
    }

    /**
     * 获取视频格式参数
     */
    fun getVideoFormat(): MediaFormat? {
   
        //【2.1,获取视频多媒体格式】
        for (i in 0 until mExtractor!!.trackCount) {
   
            val mediaFormat = mExtractor!!.getTrackFormat(i)
            val mime = mediaFormat.getString(MediaFormat.KEY_MIME)
            if (mime.startsWith("video/")) {
   
                mVideoTrack = i
                break
            }
        }
        return if (mVideoTrack >= 0)
            mExtractor!!.getTrackFormat(mVideoTrack)
        else null
    }

    /**
     * 获取音频格式参数
     */
    fun getAudioFormat(): MediaFormat? {
   
        //【2.2,获取音频频多媒体格式】
        for (i in 0 until mExtractor!!.trackCount) {
   
            val mediaFormat = mExtractor!!.getTrackFormat(i)
            val mime = mediaFormat.getString(MediaFormat.KEY_MIME)
            if (mime.startsWith("audio/")) {
   
                mAudioTrack = i
                break
            }
        }
        return if (mAudioTrack >= 0) {
   
            mExtractor!!.getTrackFormat(mAudioTrack)
        } else null
    }

    /**
     * 读取视频数据
     */
    fun readBuffer(byteBuffer: ByteBuffer): Int {
   
        //【3,提取数据】
        byteBuffer.clear()
        selectSourceTrack()
        var readSampleCount = mExtractor!!.readSampleData(byteBuffer, 0)
        if (readSampleCount < 0) {
   
            return -1
        }
        mCurSampleTime = mExtractor!!.sampleTime
        mExtractor!!.advance()
        return readSampleCount
    }

    /**
     * 选择通道
     */
    private fun selectSourceTrack() {
   
        if (mVideoTrack >= 0) {
   
            mExtractor!!.selectTrack(mVideoTrack)
        } else if (mAudioTrack >= 0) {
   
            mExtractor!!.selectTrack(mAudioTrack)
        }
    }

    /**
     * Seek到指定位置,并返回实际帧的时间戳
     */
    fun seek(pos: Long): Long {
   
        mExtractor!!.seekTo(pos, MediaExtractor.SEEK_TO_PREVIOUS_SYNC)
        return mExtractor!!.sampleTime
    }

    /**
     * 停止读取数据
     */
    fun stop() {
   
        //【4,释放提取器】
        mExtractor?.release()
        mExtractor = null
    }

    fun getVideoTrack(): Int {
   
        return mVideoTrack
    }

    fun getAudioTrack(): Int {
   
        return mAudioTrack
    }

    fun setStartPos(pos: Long) {
   
        mStartPos = pos
    }

    /**
     * 获取当前帧时间
     */
    fun getCurrentTimestamp(): Long {
   
        return mCurSampleTime
    }
}

比较简单,直接把代码贴出来了。

关键部分有5个,做一下简单讲解:

  • 【1,初始化】

很简单,两句代码:新建,然后设置音视频文件路径

mExtractor = MediaExtractor()
mExtractor?.setDataSource(path)
  • 【2.1/2.2,获取音视频多媒体格式】

音频和视频是一样的:

1)遍历视频文件中所有的通道,一般是音频和视频两个通道;

2) 然后获取对应通道的编码格式,判断是否包含"video/"或者"audio/"开头的编码格式;

3)最后通过获取的索引,返回对应的音视频多媒体格式信息。

  • 【3,提取数据】

重点看看如何提取数据:

1)readBuffer(byteBuffer: ByteBuf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开发的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值