一、引言:
因为之前方便本地调试,所以对于nuplayer的分析是基于Android5.1
版本的,在熟悉了整个架构之后,在AndroidQ
版本上对nuplayer的所有流程做了一个分析,发现变动不是特别大,主要体现在三个方面:
1.将5.1版本用于协调解码及渲染的主动循环改为了MediaCodec的消息回调方式;
2.handleAnInputBuffer和handleAnOutputBuffer均变为了MediaCodec的消息回调方式;
3.修正了视频帧校准的bug;
二、消息回调的分析:
AndroidQ版本onConfigure
函数有如下代码片段:
onConfigure@NuPlayerDecoder.cpp:
void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
...
sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
mCodec->setCallback(reply);
...
}
看下MediaCodec中的setCallback函数实现:
status_t MediaCodec::setCallback(const sp<AMessage> &callback) {
sp<AMessage> msg = new AMessage(kWhatSetCallback, this);
msg->setMessage("callback", callback);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
这里会将传入的kWhatCodecNotify
消息写入到kWhatSetCallback
中,看下kWhatSetCallback
的消息处理:
case kWhatSetCallback:
{
uint32_t replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
if (mState == UNINITIALIZED
|| mState == INITIALIZING
|| isExecuting()) {
// callback can't be set after codec is executing,
// or before it's initialized (as the callback
// will be cleared when it goes to INITIALIZED)
PostReplyWithError(replyID, INVALID_OPERATION);
break;
}
sp<AMessage> callback;
CHECK(msg->findMessage("callback", &callback));
/* 保存kWhatCodecNotify这个消息 */
mCallback = callback;
if (mCallback != NULL) {
ALOGI("MediaCodec will operate in async mode");
mFlags |= kFlagIsAsync;
} else {
mFlags &= ~kFlagIsAsync;
}
sp<AMessage> response = new AMessage;
response->postReply(replyID);
break;
}
我们可以看到,onConfigure
函数会将kWhatCodecNotify
这个消息传入到MediaCodec中,当有可用的输入输出buffer的时候,OMX会调用MediaCodec的
onInputBufferAvailable
和onOutputBufferAvailable
函数,将这个消息发出来:
void MediaCodec::onInputBufferAvailable() {
int32_t index;
while ((index = dequeuePortBuffer(kPortIndexInput)) >= 0) {
/* 深拷贝该消息 */
sp<AMessage> msg = mCallback->dup();
/* 注明是处理input/output */
msg->setInt32("callbackID", CB_INPUT_AVAILABLE);
msg->setInt32("index", index);
msg->post();
}
}
NuPlayerDecoder.cpp
对该消息的处理就会去调用handleAnInputBuffer
和handleAnInputBuffer
了。
三、同步校准的修改:
5.1版本的代码,会在送显之前去校准同步时间点,结果到了送显的时候,并没有用这个值,因此白校准了,但是AndroidQ上我看逻辑就改过来了,在真正要送显的时候再去校准的:
onDrainVideoQueue@NuPlayerRenderer.cpp:
void NuPlayer::Renderer::onDrainVideoQueue() {
...
/* 1.获得预估送显时间 */
realTimeUs = getRealTimeUs(mediaTimeUs, nowUs);
...
/* 2.校准送显时间 */
realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;
...
}
四、AndroidQ版本逻辑:
逻辑调用图上除了最后处理解码和渲染是使用的消息机制,其他没有区别;