4. AVsync Audio更新锚点时间
(1) AVsync原理
系统时间和媒体时间应该是线性关系: (mediaTimeUs - anchorTimeMediaUs) = PlaybackRate*(nowUs - anchorTimeRealUs)。
所以理论上,我们可以根据锚点, 计算出任意一点的媒体时间对应的系统时间(Buffer应该播放的时间). AVsync 的进本机理就是通过Audio每隔一段时间更新锚点, Video的Buffer根据锚点和媒体时间计算出应该播放的时(系统时间)
更新锚点时间 anchorTimeMediaUs anchorTimeRealUs
anchorTimeRealUs 更新锚点时的 系统时间
anchorTimeMediaUs 更新锚点时的 媒体时间(正在播放的媒体的时间)
下面我们主要来看一下Audio如何更新锚点
(2) Audio如何更新锚点
anchorTimeRealUs = nowUs
锚点系统时间被设置为当前系统时间, 所以 anchorTimeMediaUs 就应该当前正在播放的媒体时间,接下来重点是如何确定anchorTimeMediaUs.
<1> 如何计算当前正在播放的媒体时间
当前正在播放的媒体时间 = 正在写入的媒体时间 - 已经写入但是没有播放的数据需要播放的时间
pendingAudioPlayoutDurationUs: 已经写入但是没有播放的数据需要播放的时间(主要是在AudioBuffer里面的数据)
anchorTimeMediaUs = mediaTimeUs - pendingAudioPlayoutDurationUs;
<2> 计算已经写入但是没有播放的数据需要播放的时间
writtenAudioDurationUs: 已经写入的数据的持续时间
PlayedOutDurationUs: 当前已经播放的数据的持续时间
两者相减就是pendingAudioPlayoutDurationUs
pendingAudioPlayoutDurationUs = writtenAudioDurationUs - PlayedOutDurationUs
<3> 已经写入的数据的持续时间
mNumFramesWritten 已经写入的数据的帧的个数 * 每一帧多少时间(1000000LL / sampleRate)
writtenAudioDurationUs = mNumFramesWritten * (1000000LL / sampleRate)
<4> 计算当前已经播放的数据的持续时间
ts.mPosition * 1000000LL/mSampleRateHz 在 ts.mTime时间已经播放的数据的时间
因为nowUs与ts.mTime不相等, 最后需要根据nowUs进行调整
PlayedOutDurationUs = ts.mPosition * 1000000LL/mSampleRateHz + nowUs - ts.mTime
void NuPlayer::Renderer::onNewAudioMediaTime(int64_t mediaTimeUs)
// 设置初始锚点媒体时间
setAudioFirstAnchorTimeIfNeeded_l(mediaTimeUs);
int64_t nowUs = ALooper::GetNowUs();
if (mNextAudioClockUpdateTimeUs >= 0)
//是否需要更新锚点时间, 根据kMinimumAudioClockUpdatePeriodUs的时间
if (nowUs >= mNextAudioClockUpdateTimeUs)
// (1) nowMediaUs: 当前正在播放的媒体时间
// mediaTimeUs: 当前正在写入到Audio的数据的媒体时间
// getPendingAudioPlayoutDurationUs
// 已经写入到Audio但是还没有播放的数据持续时间
int64_t nowMediaUs = mediaTimeUs -
getPendingAudioPlayoutDurationUs(nowUs);
// 根据nowMediaUs和mediaTimeUs更新锚点时间
mMediaClock->updateAnchor(nowMediaUs, nowUs, mediaTimeUs);
mNextAudioClockUpdateTimeUs = nowUs + kMinimumAudioClockUpdatePeriodUs;
// (2) 需要分析一下getPendingAudioPlayoutDurationUs
// 计算方法使用 writtenAudioDurationUs - PlayedOutDurationUs
int64_t NuPlayer::Renderer::getPendingAudioPlayoutDurationUs(int64_t nowUs)
// (3) writtenAudioDurationUs 已经写入的数据的持续时间
// mNumFramesWritten * (1000000LL / sampleRate)
// 写入的帧的个数 * 每一帧的持续时间(us)
// (1000000LL / sampleRate): sampleRate一秒的采样数, 取倒数每一个采样的持续时间
int64_t writtenAudioDurationUs =
getDurationUsIfPlayedAtSampleRate(mNumFramesWritten);
// PlayedOutDurationUs 已经播放时间, 需要从Audio侧获取
return writtenAudioDurationUs - mAudioSink->getPlayedOutDurationUs(nowUs);
//(4) 当前已经播放的数据的持续时间
int64_t MediaPlayerService::AudioOutput::getPlayedOutDurationUs(int64_t nowUs)
// 从Audio侧获取已经获取当前播放的帧,和对应的系统时间
// 注意ts.mPosition并不是正在播放的帧的位置,
// 应该是ts.mTime这个系统时间点正在播放的帧的位置
// ts.mTime与当前时间nowUs并不相等,会有ms级的差别
status_t res = mTrack->getTimestamp(ts);
if (res == OK)
numFramesPlayed = ts.mPosition;
numFramesPlayedAt = ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
// numFramesPlayed * 1000000LL / mSampleRateHz: ts.mTime时间点已经播放的时间
// 最后计算的时候需要考虑ts.mTime与当前时间nowUs之间的差异
// durationUs nowUs时间点已经播放的时间(正在播放的媒体时间)
int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000000LL /
mSampleRateHz) + nowUs - numFramesPlayedAt;
// (5) 调用MediaClock::updateAnchor更新锚点
// 传入参数:
// anchorTimeMediaUs 正在播放的媒体时间
// anchorTimeRealUs anchorTimeMediaUs对应的系统时间
void MediaClock::updateAnchor(int64_t anchorTimeMediaUs,
int64_t anchorTimeRealUs, int64_t maxTimeMediaUs)
// 获得当前的系统时间, 可能与anchorTimeRealUs有差别
int64_t nowUs = ALooper::GetNowUs();
// 获得当前正在播放的媒体时间 nowMediaUs
int64_t nowMediaUs =
anchorTimeMediaUs + (nowUs - anchorTimeRealUs) * (double)mPlaybackRate;
// 更新当前播放的媒体时间为锚点媒体时间
// 更新当前系统时间为锚点系统时间
mAnchorTimeRealUs = nowUs;
mAnchorTimeMediaUs = nowMediaUs;
5. AVsync Video 获取显示的时间(系统时间)
AVsync的目的是获得Buffer显示的时间(buffer的系统时间).我们可以根据媒体时间和系统时间的线性关系计算出显示的时间
(mediaTimeUs - anchorTimeMediaUs) = PlaybackRate*(nowUs - anchorTimeMediaUs)
(1) nowMediaUs 当前播放媒体时间
根据当前的系统时间和锚点时间计算出当前播放媒体时间
nowMediaUs = mAnchorTimeMediaUs + (realUs - mAnchorTimeRealUs) * mPlaybackRate
(2) outRealUs Buffer的显示时间
根据当前播放的媒体时间和系统时间, 计算出Buffer的显示时间(系统时间)
outRealUs = (targetMediaUs - nowMediaUs) / (double)mPlaybackRate + nowUs
// outRealUs 获得当前Buffer播放的系统时间(应该在这个时间点播放)
// 传入参数 targetMediaUs. 当前Buffer的媒体时间
status_t MediaClock::getRealTimeFor(int64_t targetMediaUs, int64_t *outRealUs)
int64_t nowUs = ALooper::GetNowUs();
// (1) nowMediaUs video正在播放的媒体时间, nowUs对应的媒体时间
getMediaTime_l(nowUs, &nowMediaUs, true /* allowPastMaxTime */);
// (2) 计算出Buffer的显示时间
*outRealUs = (targetMediaUs - nowMediaUs) / (double)mPlaybackRate + nowUs;
// outMediaUs video正在播放的媒体时间, nowUs对应的媒体时间
// realUs 当前系统时间
status_t MediaClock::getMediaTime_l(
int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime)
//mediaUs 当前Audio正在播放的媒体时间, 对应video正在播放的媒体时间
int64_t mediaUs = mAnchorTimeMediaUs
+ (realUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
*outMediaUs = mediaUs;