webrtc jitter buffer
?一、jitter buffer 介紹
?二、jitter 估計
?三、buffer 處理 rtp 包邏輯
?四、接收和解碼流程
?五、FrameBuffer 類介紹
/*
?********************************************************************************
?一、jitter buffer 介紹
?jitter buffer 抖動緩沖區
? ?當網絡不穩定時(發生抖動),增加buffer的長度,多緩存一些數據,以應對將來可能發生的抖動。
? ?它對數據包丟失、亂序、延遲到達等情況進行處理,平滑的向解碼模塊輸出數據包/幀,
?抵抗各種弱網情況對播放/渲染造成的影響,降低卡頓,提高用戶體驗。
?視頻從采集到渲染的流程如下:
?采集 -> 編碼 -> 分包 -> 發送 ----------------> 網絡
?渲染 <- 解碼 <- Jitter Buffer <- 組幀 <- 接收 <-|
?********************************************************************************
?*/
/*
?********************************************************************************
?二、jitter 估計
?
?jitter 就是一種抖動。
?RTP數據包從源地址發送到目的地址,會發生不一樣的延遲,這樣的延遲變動就是 jitter
?jitter 會讓音視頻的播放不穩定,如音頻的顫音、視頻的忽快忽慢
?解決 jitter 的方法是增加延時,這個延時稱為抖動延遲(jitter delay)
?抖動延遲由網絡延遲、解碼延遲、渲染延遲構成。解碼、渲染延遲比較穩定,網絡抖動延遲是動態變化的。
?webrtc 認為網絡抖動延遲由兩部分構成:
?1、網絡噪聲帶來的抖動延遲(網絡排隊延遲)
?2、傳輸大的視頻幀(特別是關鍵幀)對網絡造成沖擊帶來的抖動延遲
? ?(計算信道速率,根據信道速率計算大的視頻幀對網絡沖擊帶來的延遲)
?webrtc 使用卡爾曼濾波(kalman) 估算網絡排隊延遲和信道速率
?modules\video_coding\jitter_estimator.cc
?1、更新 jitter 估計
?void VCMJitterEstimator::UpdateEstimate(
? ?int64_t frameDelayMS, uint32_t frameSizeBytes, bool incompleteFrame = false);
?
?其中 frameDelayMS 幀間延遲,指的是一幀數據因為分包和網絡傳輸所造成的延時。
?frameSizeBytes 指當前數據幀大小, incompleteFrame 指是否為完整的幀。
?1.1、噪聲閾值計算
?VCMJitterEstimator::NoiseThreshold()
? ?double noiseThreshold = _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset;
?1.2、計算 jitter 估計值
?VCMJitterEstimator::CalculateEstimate()
? ?double ret = _theta[0] * (_maxFrameSize - _avgFrameSize) + NoiseThreshold();
?
?即
?jitterDelay = _theta[0] * (_maxFrameSize - _avgFrameSize)
? ? ? ? ? ? ? ?+ _noiseStdDevs * sqrt(_varNoise) - _noiseStdDevOffset;?
?
?其中:
?_theta[0] 為信道傳輸速率的倒數
?_maxFrameSize 自會話開始以來所收到的最大幀大小
?_avgFrameSize 平均幀大小
?_noiseStdDevs 表示噪聲系數,值為2.33
?_varNoise 表示噪聲方差,默認值為4.0 EstimateRandomJitter()中會不斷更新該值
?_noiseStdDevOffset 為噪聲扣除常數,值為30.0
?2、獲取 jitter 估計
?返回以毫秒為單位的當前抖動估計,并在重傳情況下添加一個RTT相關項
?int VCMJitterEstimator::GetJitterEstimate(
? ?double rttMultiplier, absl::optional<double> rttMultAddCapMs)
?其中: rttMultiplier 為RTT參數乘數
?參考: http://www.ctiforum.com/news/guonei/512085.html
?********************************************************************************
?*/
/*
?********************************************************************************
?三、buffer 處理 rtp 包邏輯
?modules\video_coding\jitter_buffer.h
?buffer 接收 rtp 包的處理邏輯主要使用到以下三個隊列
?class VCMJitterBuffer {
? ?UnorderedFrameList free_frames_ RTC_GUARDED_BY(crit_sect_);
? ?FrameList decodable_frames_ RTC_GUARDED_BY(crit_sect_);
? ?FrameList incomplete_frames_ RTC_GUARDED_BY(crit_sect_);
?}
?free_frames_ 隊列用于管理空的frame,彈出空的frame來存放rtp包,解碼完成后的frame重置后再次存入該隊列。
?incomplete_frames_ 隊列用于存放尚未完整的frame,當frame完整時將其push到 decodable_frames_
?decodable_frames_ 隊列用于存放完整的可以解碼的frame
?
?1、第一次接收到一個rtp視頻包,從 free_frames_ 隊列中彈出一個空 frame 塊,用來放置這個包。
? ? 之后每次接收一個rtp包,根據時間戳在 incomplete_frames_ 和 decodable_frames_?
? ? 中尋找,看是否已經接收到過相同時間戳的包,如果找到則彈出該 frame 塊。
? ? 否則,從 free_frames_ 中彈出一個空 frame
?2、根據包的序列號,找到應該插入 frame 的位置將包插入,并更新 frame 的 state (frame中存放多個rtp包)
? ? 其中 state 的狀態為?
? ? enum VCMFrameBufferStateEnum {
? ? ? kStateEmpty, ? ? ? // frame popped by the RTP receiver
? ? ? kStateIncomplete, ?// frame that have one or more packet(s) stored
? ? ? kStateComplete, ? ?// frame that have all packets
? ? };
?3、根據不同的 buffer_state 將 frame 幀 push 回到隊列中。
? ? 如果 buffer_state 為 kCompleteSession 并且 frame 已經在 decodable list (continuous 為 true)
? ? 將 frame push 到 decodable_frames_ 隊列 decodable_frames_.InsertFrame(frame);
? ? 如果 buffer_state 為 kCompleteSession 或 kIncomplete
? ? 將 frame push 到 incomplete_frames_ 隊列 incomplete_frames_.InsertFrame(frame);
? ? 如果 buffer_state 為 kNoError 或 kOutOfBoundsPacket 或 kDuplicatePacket
? ? 將 frame push 到 frame_list 隊列 frame_list->InsertFrame(frame);
? ? VCMJitterBuffer::InsertPacket -> VCMJitterBuffer::FindAndInsertContinuousFramesWithState()?
? ? 將 incomplete_frames_ 隊列中的 frame push 到 decodable_frames_ 隊列
?
?4、free_frames_ 隊列初始化大小為 kStartNumberOfFrames = 6 (在構造函數 VCMJitterBuffer() 中初始化)
? ? 如果 free_frames_ 隊列為空時,增加隊列大小,最大值為 kMaxNumberOfFrames = 300
? ? 定期從 incomplete_frames_ 和 decodable_frames_ 隊列中清除一些過時的frame?
? ? push 到 free_frames_ 隊列 VCMJitterBuffer::RecycleFramesUntilKeyFrame()
?
?5、解碼線程取出 frame 并解碼完成后,將 frame 重置并 push 到 free_frames_ 隊列
? ? VideoReceiver::Decode() -> VCMReceiver::ReleaseFrame() -> VCMJitterBuffer::ReleaseFrame()
? ? -> VCMJitterBuffer::RecycleFrameBuffer() -> frame->Reset()/free_frames_.push_back(frame)
?********************************************************************************
?*/
/*
?********************************************************************************
?四、接收和解碼流程
?webrtc jitter buffer 被兩個線程操作
?(1) 寫線程負責組幀之后把數據寫入 jitter buffer 里
?(2) 讀線程負責從 jitter buffer 里讀取數據然后解碼
?1、接收數據
?1.1、讀線程 創建流程
?WebRtcVideoChannel::WebRtcVideoReceiveStream::WebRtcVideoReceiveStream()
?-> WebRtcVideoChannel::WebRtcVideoReceiveStream::RecreateWebRtcVideoStream()
? ? [調用 stream_ = call_->CreateVideoReceiveStream(std::move(config));
? ? ?調用 stream_->Start()
? ? ?其中 stream_ 類型為 webrtc::VideoReceiveStream*
? ? ]
? ? [Call::CreateVideoReceiveStream()
? ? ?VideoReceiveStream::Start()
? ? ]
?-> webrtc::VideoReceiveStream* Call::CreateVideoReceiveStream()
? ? [調用構造函數?
? ? ?VideoReceiveStream::VideoReceiveStream(module_process_thread_.get())
? ? ?其中 Call::Call() 設置為 module_process_thread_ 為 ModuleProcessThread 線程
? ? ]
? ? [Call::Create(const Call::Config& config)
? ? ? ? [調用 Create(config, Clock::GetRealTimeClock(),
? ? ? ? ? ? ? ? ? ? ProcessThread::Create("ModuleProcessThread"),
? ? ? ? ? ? ? ? ? ? ProcessThread::Create("PacerThread"));
? ? ? ? ?創建兩個線程: ModuleProcessThread 和 PacerThread
? ? ? ? ]
? ? ?-> Call::Create()
? ? ? ? [調用 new internal::Call()]
? ? ?-> Call::Call()
? ? ? ? [其中設置 module_process_thread_ 為 ModuleProcessThread 線程]
? ? ]
? ? [VideoReceiveStream::VideoReceiveStream()
? ? ? ? [設置 rtp_stream_sync_(this) 即 rtp_stream_sync_ 為 VideoReceiveStream 類型
? ? ? ? ?設置 process_thread_ 為 ModuleProcessThread 線程
? ? ? ? ?調用 process_thread_->RegisterModule(&rtp_stream_sync_, RTC_FROM_HERE);
? ? ? ? ]
? ? ]
?-> VideoReceiveStream::Start()
?-> VideoReceiveStream::StartNextDecode()
? ? [將數據幀從 jitter buffer 中取出
? ? ?調用 frame_buffer_->NextFrame()
? ? ]
?1.2、寫線程 創建流程
?VideoReceiveStream::VideoReceiveStream()
? ? [創建 rtp_video_stream_receiver_(process_thread_)
? ? ?其中 rtp_video_stream_receiver_ 類型為 RtpVideoStreamReceiver
? ? ?process_thread_ 為 ModuleProcessThread 線程
? ? ]
?-> RtpVideoStreamReceiver::RtpVideoStreamReceiver()
? ? [設置 process_thread_ 為 ModuleProcessThread 線程]
?-> RtpVideoStreamReceiver::OnCompleteFrame() [從網絡接收到RTP數據]
?-> VideoReceiveStream::OnCompleteFrame()
? ? [調用 frame_buffer_->InsertFrame()]
?-> FrameBuffer::InsertFrame() [將數據幀存放到 jitter buffer ]
?2、解碼流程?
?// 創建一個工作線程,用于解碼數據幀
?// 從 frame_buffer_ 中獲取數據幀進行解碼
?VideoStreamDecoderImpl::VideoStreamDecoderImpl()
? ? [創建線程 PlatformThread 一個簡單的工作線程
? ? ?decode_thread_(&DecodeLoop,
? ? ? ? ? ? ? ? ? ? this,
? ? ? ? ? ? ? ? ? ? "video_stream_decoder_decode_thread",
? ? ? ? ? ? ? ? ? ? rtc::kHighestPriority)
? ? ?啟動線程 decode_thread_.Start()
? ? ]
?-> VideoStreamDecoderImpl::DecodeLoop(void* ptr)
? ? [auto* vs_decoder = static_cast<VideoStreamDecoderImpl*>(ptr);
? ? ?啟用循環 while (true)
? ? ?DecodeResult decode_result =
? ? ? ? vs_decoder->DecodeNextFrame(max_wait_time_ms, keyframe_required);
? ? ?如果 decode_result 為 kShutdown 則返回?
? ? ]
?-> VideoStreamDecoderImpl::DecodeNextFrame()
? ? [調用 frame_buffer_.NextFrame()
? ? ?VideoDecoder* decoder = GetDecoder(frame->PayloadType());
? ? ?decoder->Decode()
? ? ]
? ? [FrameBuffer::NextFrame()
? ? ?VideoStreamDecoderImpl::GetDecoder()
? ? ?VideoDecoder::Decode() 純虛函數
? ? ?[視頻編碼方式(h264 vp8 vp9),下面以vp8為例]
? ? ]
?-> LibvpxVp8Decoder::Decode()
?// 創建一個任務隊列,在上面的工作線程中以異步的方式執行任務
?// 將 frame 存放到 frame_buffer_
?VideoStreamDecoderImpl::VideoStreamDecoderImpl()
? ? [創建任務隊列 bookkeeping_queue_(task_queue_factory->CreateTaskQueue())]
?VideoStreamDecoderImpl::OnFrame()
? ? [如果 bookkeeping_queue_.IsCurrent() 為 false
? ? ?調用 bookkeeping_queue_.PostTask(
? ? ? ? ? ? std::make_unique<OnFrameTask>(std::move(frame), this));
? ? ?其中 OnFrameTask::Run() 調用?
? ? ?video_stream_decoder_->OnFrame() 即 VideoStreamDecoderImpl::OnFrame()
? ? ]
? ? [調用 frame_buffer_.InsertFrame(std::move(frame))]
?********************************************************************************
?*/
/*
?********************************************************************************
?五、FrameBuffer 類介紹
?********************************************************************************
?*/
/**
?* jitter buffer實現 frame_buffer2.cc?
?* class FrameBuffer
?*?
*/
FrameBuffer(Clock* clock,
? ? ? ? ? ? VCMTiming* timing,
? ? ? ? ? ? VCMReceiveStatisticsCallback* stats_callback);
// Insert a frame into the frame buffer. Returns the picture id
// of the last continuous frame or -1 if there is no continuous frame.
int64_t InsertFrame(std::unique_ptr<EncodedFrame> frame);
// Get the next frame for decoding. Will return at latest after
// |max_wait_time_ms|.
// ?- If a frame is available within |max_wait_time_ms| it will return
// ? ?kFrameFound and set |frame_out| to the resulting frame.
// ?- If no frame is available after |max_wait_time_ms| it will return
// ? ?kTimeout.
// ?- If the FrameBuffer is stopped then it will return kStopped.
ReturnReason NextFrame(int64_t max_wait_time_ms,
? ? ? ? ? ? ? ? ? ? ? ?std::unique_ptr<EncodedFrame>* frame_out,
? ? ? ? ? ? ? ? ? ? ? ?bool keyframe_required);
void NextFrame(
? ? int64_t max_wait_time_ms,
? ? bool keyframe_required,
? ? rtc::TaskQueue* callback_queue,
? ? std::function<void(std::unique_ptr<EncodedFrame>, ReturnReason)> handler);
// Tells the FrameBuffer which protection mode that is in use. Affects
// the frame timing. (影響幀定時)
void SetProtectionMode(VCMVideoProtection mode);
// Start the frame buffer, has no effect if the frame buffer is started.
// The frame buffer is started upon construction.
void Start();
// Stop the frame buffer, causing any sleeping thread in NextFrame to
// return immediately.
void Stop();
// Updates the RTT for jitter buffer estimation.
void UpdateRtt(int64_t rtt_ms);
// Clears the FrameBuffer, removing all the buffered frames.
void Clear();
class FrameBuffer {
?public:
?private:
??
? FrameMap frames_; ?// 只存儲未解碼的幀
? int64_t latest_return_time_ms_; ?// 下次返回幀的時間
};
void FrameBuffer::NextFrame(
? ? int64_t max_wait_time_ms,
? ? bool keyframe_required,
? ? rtc::TaskQueue* callback_queue,
? ? std::function<void(std::unique_ptr<EncodedFrame>, ReturnReason)> handler) {
? //?
? int64_t latest_return_time_ms =
? ? ? clock_->TimeInMilliseconds() + max_wait_time_ms;
? latest_return_time_ms_ = latest_return_time_ms;
? keyframe_required_ = keyframe_required;
? frame_handler_ = handler;
? callback_queue_ = callback_queue;
? StartWaitForNextFrameOnQueue();
}
void FrameBuffer::StartWaitForNextFrameOnQueue() {
? // 查找下一個待解碼的幀存放到 frames_to_decode_ 并返回最大等待時間
? int64_t wait_ms = FindNextFrame(clock_->TimeInMilliseconds());
? // 啟動一個重復任務
? // 如果在等待時間內有可以解碼的幀( frames_to_decode_ 不為空)
? // 則調用 frame_handler_ 處理幀
? callback_task_ = RepeatingTaskHandle::DelayedStart(
? ? ? callback_queue_->Get(), TimeDelta::ms(wait_ms), [this] {
? ? ? ? // 如果這個任務沒有被取消,我們在等待時沒有得到任何新的幀,
? ? ? ? // 則繼續 delivery frame
? ? ? ? if (!frames_to_decode_.empty()) {
? ? ? ? ? // 還有 frame 繼續 deliver
? ? ? ? ? frame_handler_(absl::WrapUnique(GetNextFrame()), kFrameFound);
? ? ? ? ? CancelCallback();
? ? ? ? ? return TimeDelta::Zero(); ?// Ignored.
? ? ? ? } else if (clock_->TimeInMilliseconds() >= latest_return_time_ms_) {
? ? ? ? ? // 超時,發送信號并停止重復任務
? ? ? ? ? frame_handler_(nullptr, kTimeout);
? ? ? ? ? CancelCallback();
? ? ? ? ? return TimeDelta::Zero(); ?// Ignored.
? ? ? ? } else {
? ? ? ? ? // 如果沒有幀用于解碼,并且還有時間
? ? ? ? ? // 這意味著在創建和執行此任務之間清除了幀緩沖區
? ? ? ? ? // 繼續等待剩余時間
? ? ? ? ? int64_t wait_ms = FindNextFrame(clock_->TimeInMilliseconds());
? ? ? ? ? return TimeDelta::ms(wait_ms);
? ? ? ? }
? ? ? });
}
/**
?* 從未解碼的幀 frames_ 中查找 superframe 及其剩余幀,并將其存放到 frames_to_decode_
?* 計算幀的渲染時間戳和最大需要等待的時間
?* 返回等待時間
*/
int64_t FrameBuffer::FindNextFrame(int64_t now_ms) {
? // 根據下次返回幀的時間和當前時間計算等待時間
? int64_t wait_ms = latest_return_time_ms_ - now_ms;
? // 清理容器
? frames_to_decode_.clear();
? // 遍歷未解碼的幀 frames_
? for (auto frame_it = frames_.begin();
? ? ? ?frame_it != frames_.end() && frame_it->first <= last_continuous_frame_;
? ? ? ?++frame_it) {
? ? if (!frame_it->second.continuous ||
? ? ? ? frame_it->second.num_missing_decodable > 0) {
? ? ? continue;
? ? }
? ? // 獲取已編碼的幀
? ? EncodedFrame* frame = frame_it->second.frame.get();
? ? // 如果需要關鍵幀,但當前幀不是關鍵幀,則跳過
? ? if (keyframe_required_ && !frame->is_keyframe())
? ? ? continue;
? ? // 只會返回 superframe 的所有部分
? ? // 因此如果不是 superframe 的開始,則跳過
? ? if (frame->inter_layer_predicted) {
? ? ? continue;
? ? }
? ? // 收集同一 superframe 的所有剩余幀?
? ? std::vector<FrameMap::iterator> current_superframe;
? ? current_superframe.push_back(frame_it);
? ? // 判斷下一個幀是否和當前幀為同一 superframe
? ? // 如果是,則將其存放到 current_superframe
? ? bool last_layer_completed = frame_it->second.frame->is_last_spatial_layer;
? ? FrameMap::iterator next_frame_it = frame_it;
? ? // 將同一 superframe 的所有幀存放到 current_superframe
? ? while (true) {
? ? ? ++next_frame_it;
? ? ? if (next_frame_it == frames_.end() ||
? ? ? ? ? next_frame_it->first.picture_id != frame->id.picture_id ||
? ? ? ? ? !next_frame_it->second.continuous) {
? ? ? ? break;
? ? ? }
? ? ? // Check if the next frame has some undecoded references other than
? ? ? // the previous frame in the same superframe.
? ? ? size_t num_allowed_undecoded_refs =
? ? ? ? ? (next_frame_it->second.frame->inter_layer_predicted) ? 1 : 0;
? ? ? if (next_frame_it->second.num_missing_decodable >
? ? ? ? ? num_allowed_undecoded_refs) {
? ? ? ? break;
? ? ? }
? ? ? // All frames in the superframe should have the same timestamp.
? ? ? if (frame->Timestamp() != next_frame_it->second.frame->Timestamp()) {
? ? ? ? RTC_LOG(LS_WARNING) << "Frames in a single superframe have different"
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?" timestamps. Skipping undecodable superframe.";
? ? ? ? break;
? ? ? }
? ? ? current_superframe.push_back(next_frame_it);
? ? ? last_layer_completed = next_frame_it->second.frame->is_last_spatial_layer;
? ? }
? ??
? ? // 檢查當前的 superframe 是否完整?
? ? if (!last_layer_completed) {
? ? ? continue;
? ? }
? ? // 將當前 superframe 及其剩下的所有幀傳遞給 frames_to_decode_
? ? frames_to_decode_ = std::move(current_superframe);
? ? // 如果幀的渲染時間戳無效
? ? // 根據當前時間和幀的時間戳計算幀的渲染時間戳,并保存到幀信息中
? ? if (frame->RenderTime() == -1) {
? ? ? frame->SetRenderTime(timing_->RenderTimeMs(frame->Timestamp(), now_ms));
? ? }
? ? // 根據幀的渲染時間戳和當前時間計算最大需要等待的時間
? ? wait_ms = timing_->MaxWaitingTime(frame->RenderTime(), now_ms);
? ? break;
? }
? // 更新等待時間
? wait_ms = std::min<int64_t>(wait_ms, latest_return_time_ms_ - now_ms);
? wait_ms = std::max<int64_t>(wait_ms, 0);
? return wait_ms;
}
/**
?* 根據首幀的時間戳和幀的接收時間,計算幀的延遲
?* 根據幀的延遲和幀的大小,更新抖動估計
?*?
?* 設置 frames_to_decode_ 中幀的渲染時間為首幀的渲染時間
?* 返回待編碼幀 frames_to_decode_
?*?
*/
EncodedFrame* FrameBuffer::GetNextFrame() {
? int64_t now_ms = clock_->TimeInMilliseconds();
? // TODO(ilnik): remove |frames_out| use frames_to_decode_ directly.
? std::vector<EncodedFrame*> frames_out;
? bool superframe_delayed_by_retransmission = false;
? // 統計 superframe 大小,用于更新抖動
? size_t superframe_size = 0;
? // 獲取 frames_to_decode_ 中第一個待解碼的幀
? EncodedFrame* first_frame = frames_to_decode_[0]->second.frame.get();
? // 獲取第一個待解碼幀的渲染時間和接收時間
? int64_t render_time_ms = first_frame->RenderTime();
? int64_t receive_time_ms = first_frame->ReceivedTime();
??
? // 優雅地處理壞的RTP時間戳和渲染時間問題
? if (HasBadRenderTiming(*first_frame, now_ms)) {
? ? jitter_estimator_.Reset();
? ? timing_->Reset();
? ? render_time_ms = timing_->RenderTimeMs(first_frame->Timestamp(), now_ms);
? }
? // 遍歷 frames_to_decode_
? for (FrameMap::iterator& frame_it : frames_to_decode_) {
? ? // 獲取待解碼的幀
? ? EncodedFrame* frame = frame_it->second.frame.release();
? ? // 設置幀的渲染時間
? ? frame->SetRenderTime(render_time_ms);
? ? superframe_delayed_by_retransmission |= frame->delayed_by_retransmission();
? ? // 計算接收時間
? ? receive_time_ms = std::max(receive_time_ms, frame->ReceivedTime());
? ? // 計算 superframe 幀大小
? ? superframe_size += frame->size();
? ? PropagateDecodability(frame_it->second);
? ? decoded_frames_history_.InsertDecoded(frame_it->first, frame->Timestamp());
? ? // Remove decoded frame and all undecoded frames before it.
? ? if (stats_callback_) {
? ? ? unsigned int dropped_frames = std::count_if(
? ? ? ? ? frames_.begin(), frame_it,
? ? ? ? ? [](const std::pair<const VideoLayerFrameId, FrameInfo>& frame) {
? ? ? ? ? ? return frame.second.frame != nullptr;
? ? ? ? ? });
? ? ? if (dropped_frames > 0) {
? ? ? ? stats_callback_->OnDroppedFrames(dropped_frames);
? ? ? }
? ? }
? ? frames_.erase(frames_.begin(), ++frame_it);
? ? // 將幀存放到 frames_out
? ? frames_out.push_back(frame);
? }
? if (!superframe_delayed_by_retransmission) {
? ? int64_t frame_delay;
? ? // 根據首幀的時間戳和接收時間 計算幀延遲并保存到 frame_delay
? ? if (inter_frame_delay_.CalculateDelay(first_frame->Timestamp(),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &frame_delay, receive_time_ms)) {
? ? ? // 根據 幀延遲 和 superframe 幀大小 更新抖動估計
? ? ? jitter_estimator_.UpdateEstimate(frame_delay, superframe_size);
? ? }
? ? float rtt_mult = protection_mode_ == kProtectionNackFEC ? 0.0 : 1.0;
? ? absl::optional<float> rtt_mult_add_cap_ms = absl::nullopt;
? ? if (rtt_mult_settings_.has_value()) {
? ? ? rtt_mult = rtt_mult_settings_->rtt_mult_setting;
? ? ? rtt_mult_add_cap_ms = rtt_mult_settings_->rtt_mult_add_cap_ms;
? ? }
? ? // 設置 jitter 延遲
? ? timing_->SetJitterDelay(
? ? ? ? jitter_estimator_.GetJitterEstimate(rtt_mult, rtt_mult_add_cap_ms));
? ? // 更新當前延遲
? ? timing_->UpdateCurrentDelay(render_time_ms, now_ms);
? } else {
? ? if (RttMultExperiment::RttMultEnabled() || add_rtt_to_playout_delay_)
? ? ? jitter_estimator_.FrameNacked();
? }
? UpdateJitterDelay();
? UpdateTimingFrameInfo();
? if (frames_out.size() == 1) {
? ? return frames_out[0];
? } else {
? ? return CombineAndDeleteFrames(frames_out);
? }
}
?
總結
以上是生活随笔為你收集整理的webrtc jitter buffer的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PLSQL无客户端连接ORACLE
- 下一篇: webrtc jitterbuffer-