此处直接根据NDK实现来源码分析描述问题
问题描述和原理分析:
在我们直接使用MediaCodec进行解码和渲染时,一般情况下大家可以都习惯性在同一个线程中完成MediaCodec的解码和渲染,其实际我们应该拆分成两部分来处理,将解码和渲染放入不同线程完成,如此就会加快解码和渲染,其实现原理是,同一个线程中,解码和渲染将会被互相影响,而渲染是有一个Fence栅栏Buffer标记,可以简单理解为VSync屏幕刷新周期信号,若是60fps则VSync将会在16.67ms通知屏幕刷新信号一次,因此若在调用MediaCodec的渲染接口【AMediaCodec_releaseOutputBuffer】时(java层MediaCodec同理),将会在buffer处理放入Surface时被Fence栅栏wait。
因此若放在不同线程,那么将不会相互影响,即解码和渲染都将会被加快。
先写结论:
软解码时将会被阻塞渲染,而硬解码时将会是异步直接归还BUffer给Surface,但是本文要讨论的是,解码器分为了输入队列和输出队列,我们应该将输入队列和输出队列分两个线程进行解码,原因是输出队列可能会多次执行即一个输入Buffer可能会在收到多次输出Buffer才结束当前帧解码完成,另外若是硬解码器则可以将输出队列和渲染操作放入同一个线程,如此将会加快解码和渲染。
源码分析:
// [frameworks/av/media/ndk/NdkMediaCodec.cpp]
EXPORT
media_status_t AMediaCodec_releaseOutputBuffer(AMediaCodec *mData, size_t idx, bool render) {
if (render) {
// 请求渲染
return translate_error(mData->mCodec->renderOutputBufferAndRelease(idx));
} else {
return translate_error(mData->mCodec->releaseOutputBuffer(idx));
}
}
renderOutputBufferAndRelease实现:
注意该方法调用是同步调用,即会被阻塞的调用
// [frameworks/av/media/libstagefright/MediaCodec.cpp]
status_t MediaCodec::renderOutputBufferAndRelease(size_t index) {
sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, this);
msg->setSize("index", index);
msg->setInt32("render", true);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
接收渲染事件处理
// [frameworks/av/media/libstagefright/MediaCodec.cpp]
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatReleaseOutputBuffer:
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
if (!isExecuting