本文着重介绍 MediaCodec 解码视频的异步方式。异步方式在 Android 5.0 后提供的解码能力。相较同步方式,异步方式更加灵活,无需维护独立线程,无需人为控制入队出队逻辑,使用更加方便等优势。
代码实现
private void codecAsync() {
String fileName = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath() +
"/VideoDemo/80s_TestVideo.mp4";
Log.e(TAG, "fileName: " + fileName);
try {
mediaExtractor = new MediaExtractor();
mediaExtractor.setDataSource(fileName);
int selectTrackIndex = 0;
String mime = "";
MediaFormat format = null;
int videoWidth = 0;
int videoHeight = 0;
for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
selectTrackIndex = i;
format = mediaExtractor.getTrackFormat(i);
mime = format.getString(MediaFormat.KEY_MIME);
Log.i(TAG, "mime: " + mime);
if (mime.startsWith("video/")) {
videoWidth = format.getInteger(MediaFormat.KEY_WIDTH);
videoHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
break;
}
}
mediaExtractor.selectTrack(selectTrackIndex);
int frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
long frameIntervalMs = (long)(1000.0/frameRate);
Log.i(TAG, "videoWidth: " + videoWidth + " videoHeight: " + videoHeight);
float sizeScale = videoWidth * 1f / videoHeight;
Log.d(TAG, "sizeScale: " + sizeScale);
LinearLayout.LayoutParams layoutParams =
(LinearLayout.LayoutParams)surface_media_codec.getLayoutParams();
int viewWidth = surface_media_codec.getMeasuredWidth();
int viewHeight = (int)(viewWidth / sizeScale);
Log.i(TAG, "viewWidth: " + viewWidth + " viewHeight: " + viewHeight);
layoutParams.height = viewHeight;
surface_media_codec.setLayoutParams(layoutParams);
surface_media_codec.requestLayout();
Log.i(TAG, "final mime: " + mime);
mediaCodec = MediaCodec.createDecoderByType(mime);
mediaCodec.configure(format, surfaceHolder.getSurface(), null, 0);
mediaCodec.setCallback(new MediaCodec.Callback() {
@Override
public void onInputBufferAvailable(@NonNull MediaCodec mediaCodec, int i) {
ByteBuffer buffer = mediaCodec.getInputBuffer(i);
int bufferSize = mediaExtractor.readSampleData(buffer, 0);
if (frameIdx == -1 || bufferSize <= 0) {
mediaCodec.queueInputBuffer(i, 0, 0,0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
return;
}
mediaCodec.queueInputBuffer(i, 0, bufferSize,
mediaExtractor.getSampleTime() * 1000, 0);
mediaExtractor.advance();
++frameIdx;
}
@Override
public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec, int i,
@NonNull MediaCodec.BufferInfo bufferInfo) {
if (frameIdx == -1 && (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
return;
}
long currentTimeMs = System.currentTimeMillis();
long delay = frameIntervalMs - (currentTimeMs = lastFrameTimeMs);
if (delay > 0) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lastFrameTimeMs = currentTimeMs;
mediaCodec.releaseOutputBuffer(i, true);
}
@Override
public void onError(@NonNull MediaCodec mediaCodec,
@NonNull MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(@NonNull MediaCodec mediaCodec,
@NonNull MediaFormat mediaFormat) {
}
});
mediaCodec.start();
} catch (Exception e) {
e.printStackTrace();
}
}
详细实现参考代码库:e.coding.net/g-ithd5590/…