由于解码属于耗时操作,以下流程需要在线程进行
- 构造读取视频数据的对象并设置视频
path
mMediaExtractor = new MediaExtractor();
mMediaExtractor.setDataSource(path);
- 遍历所有轨道
track
,找到并选中video/
开头的轨道
int videoTrackIndex = -1;
MediaFormat videoFormat = null;
int trackCount = mMediaExtractor.getTrackCount();
for (int i = 0; i < trackCount; i++) {
MediaFormat trackFormat = mMediaExtractor.getTrackFormat(i);
String mime = trackFormat.getString(MediaFormat.KEY_MIME);
if (!TextUtils.isEmpty(mime) && mime.startsWith("video/")) {
videoFormat = trackFormat;
videoTrackIndex = i;
}
mMediaExtractor.unselectTrack(i);
}
mMediaExtractor.selectTrack(videoTrackIndex);
- 根据选中轨道
track
的MediaFormat
,生成MediaCodec
解码器
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
String decoderName = mediaCodecList.findDecoderForFormat(videoFormat);
if (decoderName != null) {
mMediaDecoder = MediaCodec.createByCodecName(decoderName);
}
- 配置
MediaCodec
,例如指定类型format
、载体surface
if(mMediaDecoder != null) {
mMediaDecoder.configure(videoFormat, surface, null, 0);
mMediaDecoder.start(); // 启动解码器,并未开始解码
}
-
while(true)
读取数据-渲染数据,直至MediaCodec.BUFFER_FLAG_END_OF_STREAM
,过程是生产(读取)-消费者(渲染)模式
MediaCodec.BufferInfo outputBufferInfo = new MediaCodec.BufferInfo();
long firstFrameTime = 0;
final int TIME_OUT_ESC = 10000;
boolean inputDone = false;
boolean outputDone = false;
// 伪代码
while (!outputDone) {
if (!inputDone) {
// 读取数据放入队列
...
}
if (!outputDone) {
// 队列取出数据,交给 surface 渲染
// 如果 MediaCodec.BUFFER_FLAG_END_OF_STREAM 跳出循环
...
}
}
- 生产者流程
int inputBufferIndex = decoder.dequeueInputBuffer(TIME_OUT_ESC);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = decoder.getInputBuffer(inputBufferIndex);
if (inputBuffer != null) {
int sampleDataSize = extractor.readSampleData(inputBuffer, 0);
if (sampleDataSize > 0) {
long presentationTimeUs = extractor.getSampleTime();
decoder.queueInputBuffer(inputBufferIndex, 0, sampleDataSize, presentationTimeUs, 0);
extractor.advance();
} else {
decoder.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
inputDone = true;
}
}
}
- 消费者流程
int outputBufferIndex = decoder.dequeueOutputBuffer(outputBufferInfo, TIME_OUT_ESC);
if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
Log.d(TAG, "no output from decoder available");
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat outputFormat = decoder.getOutputFormat();
Log.d(TAG, "output format changed, new format is == " + outputFormat.toString());
} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
Log.d(TAG, "decoder output buffers changed");
} else if (outputBufferIndex < 0) {
Log.d(TAG, "decoder output buffers < 0");
} else {
if (firstFrameTime == 0) {
firstFrameTime = System.nanoTime();
}
// 计算帧率,如果没有,播放速度将取决于硬件解码速度
long dNs = outputBufferInfo.presentationTimeUs * 1000L - (System.nanoTime() - firstFrameTime);
long millis = dNs / 1000000L;
int nanos = (int) (dNs & 1000L);
if (millis >= 0 && nanos >= 0) {
Thread.sleep(millis, nanos);
}
// 通过这个值,通知 surface 是否需要渲染
boolean render = outputBufferInfo.size != 0;
decoder.releaseOutputBuffer(outputBufferIndex, render);
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
outputDone = true;
}
}
网友评论