/**
- 获取音视频对应的格式参数
*/
fun getMediaFormat(): MediaFormat?
/**
- 获取音视频对应的媒体轨道
*/
fun getTrack(): Int
/**
- 获取解码的文件路径
*/
fun getFilePath(): String
}
定义了解码器的一些基础操作,如暂停/继续/停止解码,获取视频的时长,视频的宽高,解码状态等等
为什么继承Runnable?
这里使用的是同步模式解码,需要不断循环压入和拉取数据,是一个耗时操作,因此,我们将解码器定义为一个Runnable,最后放到线程池中执行。
接着,继承IDecoder,定义基础解码器BaseDecoder。
首先来看下基础参数:
abstract class BaseDecoder: IDecoder {
//-------------线程相关------------------------
/**
- 解码器是否在运行
*/
private var mIsRunning = true
/**
- 线程等待锁
*/
private val mLock = Object()
/**
- 是否可以进入解码
*/
private var mReadyForDecode = false
//---------------解码相关-----------------------
/**
- 音视频解码器
*/
protected var mCodec: MediaCodec? = null
/**
- 音视频数据读取器
*/
protected var mExtractor: IExtractor? = null
/**
- 解码输入缓存区
*/
protected var mInputBuffers: Array? = null
/**
- 解码输出缓存区
*/
protected var mOutputBuffers: Array? = null
/**
- 解码数据信息
*/
private var mBufferInfo = MediaCodec.BufferInfo()
private var mState = DecodeState.STOP
private var mStateListener: IDecoderStateListener? = null
/**
- 流数据是否结束
*/
private var mIsEOS = false
protected var mVideoWidth = 0
protected var mVideoHeight = 0
//省略后面的方法
…
}
-
首先,我们定义了线程相关的资源,用于判断是否持续解码的mIsRunning,挂起线程的mLock等。
-
然后,就是解码相关的资源了,比如MdeiaCodec本身,输入输出缓冲,解码状态等等。
-
其中,有一个解码状态DecodeState和音视频数据读取器IExtractor。
定义解码状态
为了方便记录解码状态,这里使用一个枚举类表示
enum class DecodeState {
/*开始状态/
START,
/*解码中/
DECODING,
/*解码暂停/
PAUSE,
/*正在快进/
SEEKING,
/*解码完成/
FINISH,
/*解码器释放/
STOP
}
定义音视频数据分离器
前面说过,MediaCodec需要我们不断地喂数据给输入缓冲,那么数据从哪里来呢?肯定是音视频文件了,这里的IExtractor就是用来提取音视频文件中数据流。
Android自带有一个音视频数据读取器MediaExtractor,同样为了方便维护和拓展性,我们依然先定一个读取器IExtractor。
interface IExtractor {
/**
- 获取音视频格式参数
*/
fun getFormat(): MediaFormat?
/**
- 读取音视频数据
*/
fun readBuffer(byteBuffer: ByteBuffer): Int
/**
- 获取当前帧时间
*/
fun getCurrentTim