博客摘录「 Nuplayer 音视频同步学习笔记」2024年4月9日

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;

摘自:Nuplayer 音视频同步学习笔记_ondrainaudioqueue-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值