简介
日常开发中,我们都习惯于使用第三方播放器进行音视频的播放,如常用的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)
解码流程
- 创建创建视频多媒体提取器并设置视频路径
scss
//创建视频多媒体提取器
val mVideoExtractor = MediaExtractor()
//设置视频多媒体路径
val mVideoExtractor.setDataSource(videoPath)
- 获取视频的编码格式及视频轨道
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
}
}
- 创建 MediaCodec 对象,指定编码器或解码器类型
ini
//创建视频解码器
mVideoDecoder = MediaCodec.createDecoderByType(mVideoKeyType)
- 配置解码器参数,例如SurfaceView,编码格式、分辨率、帧率、码率等
scss
//设置视频轨道
mVideoExtractor.selectTrack(mVideoTrackIndex)
//配置解码器参数
mVideoDecoder.configure(mVideoFormat, MediaCodecVideoActivity.getSurface(), null, 0)
- 开始执行解码任务,调用 start() 方法启动解码器。
scss
//开始执行解码
mVideoDecoder.start()
- 开启线程将解码出来的视频流刷新到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)
}
}
- 释放资源
scss
if (mVideoDecoder!=null){
mVideoDecoder.stop()
mVideoDecoder.release()
}
mVideoExtractor.release()
实现效果
本人Demo实现效果如下:
完整代码已经上传GitHub,需要的小伙伴可以私聊小编。