【声 明】
首先,这一系列文章均基于自己的理解和实践,可能有不对的地方,欢迎大家指正。
其次,这是一个入门系列,涉及的知识也仅限于够用,深入的知识网上也有许许多多的博文供大家学习了。
最后,写文章过程中,会借鉴参考其他人分享的文章,会在文章最后列出,感谢这些作者的分享。
码字不易,转载请注明出处!
教程代码:【Github传送门】 |
---|
目录
一、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
}
/**
* 停止读取数据
*/
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