目录
一、Android音视频硬解码篇:
- 1,音视频基础知识
- 2,音视频硬解码流程
- 3,音视频播放:音视频同步
- 4,音视频解封和封装:生成一个MP4
二、使用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
}
/**