一、引言:
在上一篇博客中,分析了音频部分的buffer处理。nuplayer在音视频buffer的处理上共用了很多代码,这篇博客将直接从差异上开始分析,nuplayer的同步机制整体来说,和exoplayer大同小异,都是基于码流中的pts和系统时间来进行预估,并结合垂直同步信号时间点来确定最终的送显时间。不同点在于nuplayer对于送显时间的校准太过复杂,很多都看不懂,但是如果不重点关注校准的内容的话,其他部分还是很好理解的。
二、确定视频帧送显时间:
音视频的buffer处理函数很多都共用,我们直接定位到NuPlayerRenderer.cpp
的postDrainVideoQueue_l
函数:
void NuPlayer::Renderer::postDrainVideoQueue_l() {
if (mDrainVideoQueuePending
|| mSyncQueues
|| (mPaused && mVideoSampleReceived)) {
return;
}
if (mVideoQueue.empty()) {
return;
}
QueueEntry &entry = *mVideoQueue.begin();
/* 1.创建kWhatDrainVideoQueue消息用于后续投递处理 */
sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
msg->setInt32("generation", mVideoQueueGeneration);
if (entry.mBuffer == NULL) {
// EOS doesn't carry a timestamp.
msg->post();
mDrainVideoQueuePending = true;
return;
}
int64_t delayUs;
int64_t nowUs = ALooper::GetNowUs();
int64_t realTimeUs;
if (mFlags & FLAG_REAL_TIME) {
int64_t mediaTimeUs;
CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
realTimeUs = mediaTimeUs;
} else {
int64_t mediaTimeUs;
CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
if (mAnchorTimeMediaUs < 0) {
setAnchorTime(mediaTimeUs, nowUs);
mPausePositionMediaTimeUs = mediaTimeUs;
mAnchorMaxMediaUs = mediaTimeUs;
realTimeUs = nowUs;
} else {
/* 2.获取当前帧送显时间 */
realTimeUs = getRealTimeUs(mediaTimeUs, nowUs);
}
if (!mHasAudio) {
mAnchorMaxMediaUs = mediaTimeUs + 100000; // smooth out videos >= 10fps
}
// Heuristics to handle situation when media time changed without a
// discontinuity. If we have not drained an audio buffer that was
// received after this buffer, repost in 10 msec. Otherwise repost
// in 500 msec.
/* 当前帧渲染时间差 = 当前帧时间戳 - 当前帧送显时间 */
/* 当前帧渲染时间差 = 当前帧送显时间 - 系统时间 */
delayUs = realTimeUs - nowUs;
if (delayUs > 500000) {
int64_t postDelayUs = 500000;
if (mHasAudio && (mLastAudioBufferDrained - entry.mBufferOrdinal) <= 0) {
postDelayUs = 10000;
}
msg->setWhat(kWhatPostDrainVideoQueue);
msg->post(postDelayUs);
mVideoScheduler->restart();
ALOGI("possible video time jump of %dms, retrying in %dms",
(int)(delayUs / 1000), (int)(postDelayUs / 1000));
mDrainVideoQueuePending = true;
return;
}
}
/* 3.校准送显时间 */
realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;