日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

webrtc jitter buffer

發布時間:2023/12/14 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 极品久久久久久 | 国产成人在线一区二区 | 国产亚洲成av人片在线观看桃 | 日本爽爽爽 | 中文字幕在线一 | 欧美福利视频导航 | 亚色在线| 国产真人做爰毛片视频直播 | 欧美一级xxx| 99久久99久久精品国产片果冰 | 超碰免费在线 | 亚洲黄在线观看 | 午夜电影你懂的 | 国产日韩综合 | 亚洲精品水蜜桃 | 99久久精品免费看国产四区 | 欧美男人又粗又长又大 | 最近中文字幕在线观看视频 | 亚洲永久无码精品一区二区 | 最近日韩中文字幕 | 伊人一二三 | 超碰碰碰碰 | 婷婷狠狠 | 99国产精品一区二区三区 | 亚洲综合久久av | 亚洲 高清 成人 动漫 | 亚洲国产欧美在线观看 | 男女激情视频网站 | 无码国产色欲xxxx视频 | 性激情视频 | 蜜桃视频中文字幕 | 国产三区四区视频 | 亚洲影视中文字幕 | 欧美日韩一区不卡 | 国产性―交―乱―色―情人 | 日本大乳奶做爰 | 精品丰满人妻无套内射 | 日韩夜色| 亚洲精品福利在线观看 | 中文字幕日韩亚洲 | 91视频日本| 中文字幕有码无码人妻av蜜桃 | 五色天婷婷 | 人人91| 国产精品7 | 天天透天天干 | 黄频在线 | 91亚洲精品久久久蜜桃 | 国产成人精品一区二区三区在线 | 日韩欧美在线视频观看 | 欧美亚洲另类图片 | 中文字幕乱码在线人视频 | 精品国产av鲁一鲁一区 | 日韩黄色小视频 | 国产精品一区二区三区四 | 天天躁狠狠躁狠狠躁夜夜躁68 | 91jk制服白丝超短裙大长腿 | 97干视频 | 美腿丝袜亚洲综合 | 麻豆精品久久久 | 午夜天堂在线观看 | 日韩美女视频在线观看 | 日本视频免费观看 | www.一区二区| 亚洲激情a | 国产一区二区视频在线 | porn亚洲 | 香蕉视频在线观看视频 | 色老头在线视频 | 日韩欧美视频网站 | 91学生片黄| 91尤物视频 | 免费看黄色大片 | 国产欧美一区二区三区在线 | 性做久久久久久久久 | 色中文字幕在线观看 | 久久久久婷婷 | 你懂的网址在线 | 无码人妻aⅴ一区二区三区 国产高清一区二区三区四区 | www,色| 3344av| 亚洲一级Av无码毛片久久精品 | 麻豆一区二区三区精品视频 | av黄在线| 中文字幕在线观看高清 | 大陆日韩欧美 | 1024精品一区二区三区日韩 | 国产亚洲久一区二区 | 国产精品高潮呻吟AV无码 | 日韩乱码视频 | 亚洲图片在线 | 国产精品一区二 | 亚洲一区二区三区中文字幕 | 亚洲制服在线观看 | 国产一区不卡视频 | 女人扒开腿让男人桶爽 | 国产丝袜美腿一区二区三区 | 欧美深夜福利 | 亚洲一区二区图片 |