webrtc視頻jitterbuffer原理機(jī)制(二)

bool ViEChannel::ChannelDecodeThreadFunction
bool ViEChannel::ChannelDecodeProcess()
int32_t Decode(uint16_t maxWaitTimeMs)
int32_t VideoReceiver::Decode(uint16_t maxWaitTimeMs)
VCMEncodedFrame* VCMReceiver::FrameForDecoding
VCMEncodedFrame* VCMJitterBuffer::ExtractAndSetDecode
void VCMJitterBuffer::UpdateJitterEstimate
void VCMJitterBuffer::UpdateJitterEstimate
VCMInterFrameDelay::CalculateDelay
// Calculates the delay of a frame with the given timestamp.
// This method is called when the frame is complete.
bool
VCMInterFrameDelay::CalculateDelay(uint32_t timestamp,
                                int64_t *delay,
                                int64_t currentWallClock)
{
    if (_prevWallClock == 0)
    {
        // First set of data, initialization, wait for next frame
        _prevWallClock = currentWallClock;
        _prevTimestamp = timestamp;
        *delay = 0;
        return true;
    }

    int32_t prevWrapArounds = _wrapArounds;
    CheckForWrapArounds(timestamp);

    // This will be -1 for backward wrap arounds and +1 for forward wrap arounds
    int32_t wrapAroundsSincePrev = _wrapArounds - prevWrapArounds;

    // Account for reordering in jitter variance estimate in the future?
    // Note that this also captures incomplete frames which are grabbed
    // for decoding after a later frame has been complete, i.e. real
    // packet losses.
    if ((wrapAroundsSincePrev == 0 && timestamp < _prevTimestamp) || wrapAroundsSincePrev < 0)
    {
        *delay = 0;
        return false;
    }

    // Compute the compensated timestamp difference and convert it to ms and
    // round it to closest integer.
    _dTS = static_cast<int64_t>((timestamp + wrapAroundsSincePrev *
                (static_cast<int64_t>(1)<<32) - _prevTimestamp) / 90.0 + 0.5);

    // frameDelay is the difference of dT and dTS -- i.e. the difference of
    // the wall clock time difference and the timestamp difference between
    // two following frames.
    *delay = static_cast<int64_t>(currentWallClock - _prevWallClock - _dTS);

    _prevTimestamp = timestamp;
    _prevWallClock = currentWallClock;

    return true;
}

currentWallClock:此幀最后一個(gè)包到達(dá)時(shí)間戳
_prevWallClock :前一幀最后一個(gè)包到達(dá)時(shí)間戳
timestamp:當(dāng)前幀時(shí)間戳
_prevTimestamp :前一幀時(shí)間戳
_wrapArounds:表示時(shí)間戳有沒(méi)有從2^32-1跳到1,即一個(gè)循環(huán)趾娃。
可以看出系吩,CalculateDelay()這個(gè)函數(shù)主要目標(biāo)是計(jì)算出上一幀最後一個(gè)包到當(dāng)前幀最后一個(gè)包的時(shí)間差懊缺。即相當(dāng)于傳輸當(dāng)前幀所耗費(fèi)的時(shí)間宜肉。

// Updates the estimates with the new measurements
void
VCMJitterEstimator::UpdateEstimate(int64_t frameDelayMS, uint32_t frameSizeBytes,
                                            bool incompleteFrame /* = false */)
{
    if (frameSizeBytes == 0)
    {
        return;
    }
    int deltaFS = frameSizeBytes - _prevFrameSize;
    if (_fsCount < kFsAccuStartupSamples)
    {
        _fsSum += frameSizeBytes;
        _fsCount++;
    }
    else if (_fsCount == kFsAccuStartupSamples)
    {
        // Give the frame size filter
        _avgFrameSize = static_cast<double>(_fsSum) /
                        static_cast<double>(_fsCount);
        _fsCount++;
    }
    if (!incompleteFrame || frameSizeBytes > _avgFrameSize)
    {
        double avgFrameSize = _phi * _avgFrameSize +
                              (1 - _phi) * frameSizeBytes;
        if (frameSizeBytes < _avgFrameSize + 2 * sqrt(_varFrameSize))
        {
            // Only update the average frame size if this sample wasn't a
            // key frame
            _avgFrameSize = avgFrameSize;
        }
        // Update the variance anyway since we want to capture cases where we only get
        // key frames.
        _varFrameSize = VCM_MAX(_phi * _varFrameSize + (1 - _phi) *
                                (frameSizeBytes - avgFrameSize) *
                                (frameSizeBytes - avgFrameSize), 1.0);
    }

    // Update max frameSize estimate
    _maxFrameSize = VCM_MAX(_psi * _maxFrameSize, static_cast<double>(frameSizeBytes));

    if (_prevFrameSize == 0)
    {
        _prevFrameSize = frameSizeBytes;
        return;
    }
    _prevFrameSize = frameSizeBytes;

    // Only update the Kalman filter if the sample is not considered
    // an extreme outlier. Even if it is an extreme outlier from a
    // delay point of view, if the frame size also is large the
    // deviation is probably due to an incorrect line slope.
    double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);

    if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
        frameSizeBytes > _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize))
    {
        // Update the variance of the deviation from the
        // line given by the Kalman filter
        EstimateRandomJitter(deviation, incompleteFrame);
        // Prevent updating with frames which have been congested by a large
        // frame, and therefore arrives almost at the same time as that frame.
        // This can occur when we receive a large frame (key frame) which
        // has been delayed. The next frame is of normal size (delta frame),
        // and thus deltaFS will be << 0. This removes all frame samples
        // which arrives after a key frame.
        if ((!incompleteFrame || deviation >= 0.0) &&
            static_cast<double>(deltaFS) > - 0.25 * _maxFrameSize)
        {
            // Update the Kalman filter with the new data
            KalmanEstimateChannel(frameDelayMS, deltaFS);
        }
    }
    else
    {
        int nStdDev = (deviation >= 0) ? _numStdDevDelayOutlier : -_numStdDevDelayOutlier;
        EstimateRandomJitter(nStdDev * sqrt(_varNoise), incompleteFrame);
    }
    // Post process the total estimated jitter
    if (_startupCount >= kStartupDelaySamples)
    {
        PostProcessEstimate();
    }
    else
    {
        _startupCount++;
    }
}

獲得jitterMS,并設(shè)置到渲染赁酝,以使得平穩(wěn)的顯示葵姥。

bool ViEChannel::ChannelDecodeProcess()
  int32_t Decode(uint16_t maxWaitTimeMs)
int32_t VideoReceiver::Decode
VCMEncodedFrame* VCMReceiver::FrameForDecoding
uint32_t VCMJitterBuffer::EstimatedJitterMs()
int VCMJitterEstimator::GetJitterEstimate(double rttMultiplier)
  // We have a frame - Set timing and render timestamp.
  timing_->SetJitterDelay(jitter_buffer_.EstimatedJitterMs());
bool ViEChannel::ChannelDecodeProcess()
int32_t Decode(uint16_t maxWaitTimeMs)
int32_t VideoReceiver::Decode(uint16_t maxWaitTimeMs)
void VCMTiming::UpdateCurrentDelay
void VCMTiming::UpdateCurrentDelay(int64_t render_time_ms,
                                   int64_t actual_decode_time_ms) {
  CriticalSectionScoped cs(crit_sect_);
  uint32_t target_delay_ms = TargetDelayInternal();
  int64_t delayed_ms = actual_decode_time_ms -
      (render_time_ms - MaxDecodeTimeMs() - render_delay_ms_);
  if (delayed_ms < 0) {
    return;
  }
  if (current_delay_ms_ + delayed_ms <= target_delay_ms) {
    current_delay_ms_ += static_cast<uint32_t>(delayed_ms);
  } else {
    current_delay_ms_ = target_delay_ms;
  }
}
uint32_t VCMTiming::TargetDelayInternal() const {
  return std::max(min_playout_delay_ms_,
      jitter_delay_ms_ + MaxDecodeTimeMs() + render_delay_ms_);
}

這里通過(guò)計(jì)算wait_time_ms ,得出取出下一幀需要等待的時(shí)間。

    uint32_t wait_time_ms = timing_->MaxWaitingTime(
        next_render_time_ms, clock_->TimeInMilliseconds());
uint32_t VCMTiming::MaxWaitingTime(int64_t render_time_ms, int64_t now_ms)
    const {
  CriticalSectionScoped cs(crit_sect_);

  const int64_t max_wait_time_ms = render_time_ms - now_ms -
      MaxDecodeTimeMs() - render_delay_ms_;

  if (max_wait_time_ms < 0) {
    return 0;
  }
  return static_cast<uint32_t>(max_wait_time_ms);
}

獲得next_render_time_ms 琅豆,這里綜合考慮了jitter_delay+decode_delay+render_delay

 next_render_time_ms = timing_->RenderTimeMs(frame_timestamp, now_ms);
int64_t VCMTiming::RenderTimeMs(uint32_t frame_timestamp, int64_t now_ms)
    const {
  CriticalSectionScoped cs(crit_sect_);
  const int64_t render_time_ms = RenderTimeMsInternal(frame_timestamp, now_ms);
  return render_time_ms;
}
int64_t VCMTiming::RenderTimeMsInternal(uint32_t frame_timestamp,
                                        int64_t now_ms) const {
  int64_t estimated_complete_time_ms =
    ts_extrapolator_->ExtrapolateLocalTime(frame_timestamp);
  if (estimated_complete_time_ms == -1) {
    estimated_complete_time_ms = now_ms;
  }

  // Make sure that we have at least the playout delay.
  uint32_t actual_delay = std::max(current_delay_ms_, min_playout_delay_ms_);
  return estimated_complete_time_ms + actual_delay;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末愉豺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子茫因,更是在濱河造成了極大的恐慌蚪拦,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冻押,死亡現(xiàn)場(chǎng)離奇詭異驰贷,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)洛巢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)饱苟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人狼渊,你說(shuō)我怎么就攤上這事箱熬。” “怎么了狈邑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵城须,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我米苹,道長(zhǎng)糕伐,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任蘸嘶,我火速辦了婚禮良瞧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘训唱。我一直安慰自己褥蚯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布况增。 她就那樣靜靜地躺著赞庶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪澳骤。 梳的紋絲不亂的頭發(fā)上歧强,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音为肮,去河邊找鬼摊册。 笑死,一個(gè)胖子當(dāng)著我的面吹牛颊艳,可吹牛的內(nèi)容都是我干的茅特。 我是一名探鬼主播蟆沫,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼温治!你這毒婦竟也來(lái)了饭庞?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤熬荆,失蹤者是張志新(化名)和其女友劉穎舟山,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體卤恳,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡累盗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了突琳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片若债。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拆融,靈堂內(nèi)的尸體忽然破棺而出蠢琳,到底是詐尸還是另有隱情,我是刑警寧澤镜豹,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布傲须,位于F島的核電站,受9級(jí)特大地震影響趟脂,放射性物質(zhì)發(fā)生泄漏泰讽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一昔期、第九天 我趴在偏房一處隱蔽的房頂上張望已卸。 院中可真熱鬧,春花似錦硼一、人聲如沸累澡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)永乌。三九已至,卻和暖如春具伍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背圈驼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工人芽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绩脆。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓萤厅,卻偏偏與公主長(zhǎng)得像橄抹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惕味,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 音視頻同步事關(guān)多媒體產(chǎn)品的最直觀用戶(hù)體驗(yàn)楼誓,是音視頻媒體數(shù)據(jù)傳輸和渲染播放的最基本質(zhì)量保證。音視頻如果不同步名挥,有可能...
    Monktan閱讀 2,213評(píng)論 1 8
  • 音視頻同步事關(guān)多媒體產(chǎn)品的最直觀用戶(hù)體驗(yàn)疟羹,是音視頻媒體數(shù)據(jù)傳輸和渲染播放的最基本質(zhì)量保證。音視頻如果不同步禀倔,有可能...
    weizhenwei閱讀 12,916評(píng)論 7 18
  • 前言 如果網(wǎng)絡(luò)是理想的榄融,即無(wú)丟包,無(wú)抖動(dòng)救湖,低延時(shí)愧杯,那么接收到一幀完整數(shù)據(jù)就直接播放,效果也一定會(huì)非常好鞋既。但是實(shí)際的...
    ai___believe閱讀 18,259評(píng)論 15 31
  • 教程一:視頻截圖(Tutorial 01: Making Screencaps) 首先我們需要了解視頻文件的一些基...
    90后的思維閱讀 4,680評(píng)論 0 3
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理力九,服務(wù)發(fā)現(xiàn),斷路器邑闺,智...
    卡卡羅2017閱讀 134,637評(píng)論 18 139