MediaCodec 的整体流程如上图所示,从input 输入数据,从output 输出数据。解码的时候输入的是压缩数据,输出的是解码后的原始数据。
初始化
如果传入了 Surface,那么解码完的数据可以直接渲染到 Surface 上。
public void startDecode(MediaFormat format, SurfaceView surfaceView) {
Surface surface = surfaceView.getHolder().getSurface();
mFormat = format;
final String mimeType = format.getString(MediaFormat.KEY_MIME);
// Check to see if this is actually a video mime type. If it is, then create
// a codec that can decode this mime type.
try {
mCodec = MediaCodec.createDecoderByType(mimeType);
} catch (IOException e) {
throw new RuntimeException(e);
}
mCodec.configure(format,surface, null, 0);
mCodec.start();
}
写数据
将压缩的数据送入解码器。
public void sendPackage(VEAVPacket packet) {
int size = packet.buffer.capacity();
int inputBufferId = mCodec.dequeueInputBuffer(timeOut);
if (inputBufferId >= 0) {
// fill inputBuffers[inputBufferId] with valid data
ByteBuffer buffer = mCodec.getInputBuffer(inputBufferId);
buffer.put(packet.buffer);
mCodec.queueInputBuffer(inputBufferId, 0, size, packet.dts, packet.flag);
}
}
取数据
如果不直接渲染到 Surface 上,我们也可以把解压后的数据取出来。
public VEAVFrame receiveFrame() {
VEAVFrame frame = new VEAVFrame();
int outputBufferId = mCodec.dequeueOutputBuffer(mBufferInfo, timeOut);
if (outputBufferId == MediaCodec.INFO_TRY_AGAIN_LATER) {
Log.d(TAG, "INFO_TRY_AGAIN_LATER");
return frame;
} else if (outputBufferId < 0) {
Log.d(TAG, "receiveFrame:" + outputBufferId);
return frame;
}
// outputBuffers[outputBufferId] is ready to be processed or rendered.
ByteBuffer outputBuffer = mCodec.getOutputBuffer(outputBufferId);
if (outputBuffer == null) {
return frame;
}
byte[] outData = new byte[mBufferInfo.size];
outputBuffer.get(outData);
frame.data = outData;
mCodec.releaseOutputBuffer(outputBufferId, timeOut);
return frame;
}
直接渲染到 Surface 上
releaseOutputBuffer 里面的 boolean 变量控制这一帧是否需要渲染。
public void renderToSurface() {
int outputBufferId = mCodec.dequeueOutputBuffer(mBufferInfo, timeOut);
if (outputBufferId < 0) {
return;
}
mCodec.releaseOutputBuffer(outputBufferId, true);
}
结束
public void stopDecode() {
mCodec.stop();
mCodec.release();
}
注意事项
renderToSurface 的频率需要外部控制,不然会一下子全部渲染完了。
网友评论