Index: webrtc/modules/video_coding/frame_buffer2.cc |
diff --git a/webrtc/modules/video_coding/frame_buffer2.cc b/webrtc/modules/video_coding/frame_buffer2.cc |
index 766e6a05d722ddc2811f47763d9a1f49711093ec..b2b4c213fe56122ede4477231aaae6378bc4e82c 100644 |
--- a/webrtc/modules/video_coding/frame_buffer2.cc |
+++ b/webrtc/modules/video_coding/frame_buffer2.cc |
@@ -11,11 +11,12 @@ |
#include "webrtc/modules/video_coding/frame_buffer2.h" |
#include <algorithm> |
+#include <cstring> |
+#include <queue> |
#include "webrtc/base/checks.h" |
-#include "webrtc/modules/video_coding/frame_object.h" |
+#include "webrtc/base/logging.h" |
#include "webrtc/modules/video_coding/jitter_estimator.h" |
-#include "webrtc/modules/video_coding/sequence_number_util.h" |
#include "webrtc/modules/video_coding/timing.h" |
#include "webrtc/system_wrappers/include/clock.h" |
@@ -23,32 +24,25 @@ namespace webrtc { |
namespace video_coding { |
namespace { |
-// The maximum age of decoded frames tracked by frame buffer, compared to |
-// |newest_picture_id_|. |
-constexpr int kMaxFrameAge = 4096; |
+// Max number of frames the buffer will hold. |
+constexpr int kMaxFramesBuffered = 600; |
-// The maximum number of decoded frames being tracked by the frame buffer. |
-constexpr int kMaxNumHistoryFrames = 256; |
+// Max number of decoded frame info that will be saved. |
+constexpr int kMaxFramesHistory = 20; |
} // namespace |
-bool FrameBuffer::FrameComp::operator()(const FrameKey& f1, |
- const FrameKey& f2) const { |
- // first = picture id |
- // second = spatial layer |
- if (f1.first == f2.first) |
- return f1.second < f2.second; |
- return AheadOf(f2.first, f1.first); |
-} |
- |
FrameBuffer::FrameBuffer(Clock* clock, |
VCMJitterEstimator* jitter_estimator, |
VCMTiming* timing) |
: clock_(clock), |
- frame_inserted_event_(false, false), |
+ new_countinuous_frame_event_(false, false), |
jitter_estimator_(jitter_estimator), |
timing_(timing), |
inter_frame_delay_(clock_->TimeInMilliseconds()), |
- newest_picture_id_(-1), |
+ last_decoded_frame_it_(frames_.end()), |
+ last_continuous_frame_it_(frames_.end()), |
+ num_frames_history_(0), |
+ num_frames_buffered_(0), |
stopped_(false), |
protection_mode_(kProtectionNack) {} |
@@ -56,73 +50,86 @@ FrameBuffer::ReturnReason FrameBuffer::NextFrame( |
int64_t max_wait_time_ms, |
std::unique_ptr<FrameObject>* frame_out) { |
int64_t latest_return_time = clock_->TimeInMilliseconds() + max_wait_time_ms; |
- int64_t now = clock_->TimeInMilliseconds(); |
int64_t wait_ms = max_wait_time_ms; |
- while (true) { |
- std::map<FrameKey, std::unique_ptr<FrameObject>, FrameComp>::iterator |
- next_frame_it; |
+ FrameMap::iterator next_frame_it; |
+ |
+ do { |
+ int64_t now_ms = clock_->TimeInMilliseconds(); |
{ |
rtc::CritScope lock(&crit_); |
- frame_inserted_event_.Reset(); |
+ new_countinuous_frame_event_.Reset(); |
if (stopped_) |
return kStopped; |
- now = clock_->TimeInMilliseconds(); |
wait_ms = max_wait_time_ms; |
+ |
+ // Need to hold |crit_| in order to use |frames_|, therefore we |
+ // set it here in the loop instead of outside the loop in order to not |
+ // acquire the lock unnecesserily. |
next_frame_it = frames_.end(); |
- for (auto frame_it = frames_.begin(); frame_it != frames_.end(); |
- ++frame_it) { |
- const FrameObject& frame = *frame_it->second; |
- if (IsContinuous(frame)) { |
- next_frame_it = frame_it; |
- int64_t render_time = |
- next_frame_it->second->RenderTime() == -1 |
- ? timing_->RenderTimeMs(frame.timestamp, now) |
- : next_frame_it->second->RenderTime(); |
- wait_ms = timing_->MaxWaitingTime(render_time, now); |
- frame_it->second->SetRenderTime(render_time); |
- |
- // This will cause the frame buffer to prefer high framerate rather |
- // than high resolution in the case of the decoder not decoding fast |
- // enough and the stream has multiple spatial and temporal layers. |
- if (wait_ms == 0) |
- continue; |
- |
- break; |
- } |
- } |
- } |
- wait_ms = std::min<int64_t>(wait_ms, latest_return_time - now); |
- wait_ms = std::max<int64_t>(wait_ms, 0); |
- // If the timeout occurs, return. Otherwise a new frame has been inserted |
- // and the best frame to decode next will be selected again. |
- if (!frame_inserted_event_.Wait(wait_ms)) { |
- rtc::CritScope lock(&crit_); |
- if (next_frame_it != frames_.end()) { |
- int64_t received_timestamp = next_frame_it->second->ReceivedTime(); |
- uint32_t timestamp = next_frame_it->second->Timestamp(); |
- |
- int64_t frame_delay; |
- if (inter_frame_delay_.CalculateDelay(timestamp, &frame_delay, |
- received_timestamp)) { |
- jitter_estimator_->UpdateEstimate(frame_delay, |
- next_frame_it->second->size); |
- } |
- float rtt_mult = protection_mode_ == kProtectionNackFEC ? 0.0 : 1.0; |
- timing_->SetJitterDelay(jitter_estimator_->GetJitterEstimate(rtt_mult)); |
- timing_->UpdateCurrentDelay(next_frame_it->second->RenderTime(), |
- clock_->TimeInMilliseconds()); |
- |
- decoded_frames_.insert(next_frame_it->first); |
- std::unique_ptr<FrameObject> frame = std::move(next_frame_it->second); |
- frames_.erase(frames_.begin(), ++next_frame_it); |
- *frame_out = std::move(frame); |
- return kFrameFound; |
+ // |frame_it| points to the first frame after the |
+ // |last_decoded_frame_it_|. |
+ auto frame_it = frames_.end(); |
+ if (last_decoded_frame_it_ == frames_.end()) { |
+ frame_it = frames_.begin(); |
} else { |
- return kTimeout; |
+ frame_it = last_decoded_frame_it_; |
+ ++frame_it; |
} |
+ |
+ // |continuous_end_it| points to the first frame after the |
+ // |last_continuous_frame_it_|. |
+ auto continuous_end_it = last_continuous_frame_it_; |
+ if (continuous_end_it != frames_.end()) |
+ ++continuous_end_it; |
+ |
+ for (; frame_it != continuous_end_it; ++frame_it) { |
+ if (frame_it->second.num_missing_decodable > 0) |
+ continue; |
+ |
+ FrameObject* frame = frame_it->second.frame.get(); |
+ next_frame_it = frame_it; |
+ if (frame->RenderTime() == -1) |
+ frame->SetRenderTime(timing_->RenderTimeMs(frame->timestamp, now_ms)); |
+ wait_ms = timing_->MaxWaitingTime(frame->RenderTime(), now_ms); |
+ |
+ // This will cause the frame buffer to prefer high framerate rather |
+ // than high resolution in the case of the decoder not decoding fast |
+ // enough and the stream has multiple spatial and temporal layers. |
+ if (wait_ms == 0) |
+ continue; |
+ |
+ break; |
+ } |
+ } // rtc::Critscope lock(&crit_); |
+ |
+ wait_ms = std::min<int64_t>(wait_ms, latest_return_time - now_ms); |
+ wait_ms = std::max<int64_t>(wait_ms, 0); |
+ } while (new_countinuous_frame_event_.Wait(wait_ms)); |
+ |
+ rtc::CritScope lock(&crit_); |
+ if (next_frame_it != frames_.end()) { |
+ std::unique_ptr<FrameObject> frame = std::move(next_frame_it->second.frame); |
+ int64_t received_time = frame->ReceivedTime(); |
+ uint32_t timestamp = frame->Timestamp(); |
+ |
+ int64_t frame_delay; |
+ if (inter_frame_delay_.CalculateDelay(timestamp, &frame_delay, |
+ received_time)) { |
+ jitter_estimator_->UpdateEstimate(frame_delay, frame->size); |
} |
+ float rtt_mult = protection_mode_ == kProtectionNackFEC ? 0.0 : 1.0; |
+ timing_->SetJitterDelay(jitter_estimator_->GetJitterEstimate(rtt_mult)); |
+ timing_->UpdateCurrentDelay(frame->RenderTime(), |
+ clock_->TimeInMilliseconds()); |
+ |
+ PropagateDecodability(next_frame_it->second); |
+ AdvanceLastDecodedFrame(next_frame_it); |
+ *frame_out = std::move(frame); |
+ return kFrameFound; |
+ } else { |
+ return kTimeout; |
} |
} |
@@ -139,55 +146,191 @@ void FrameBuffer::Start() { |
void FrameBuffer::Stop() { |
rtc::CritScope lock(&crit_); |
stopped_ = true; |
- frame_inserted_event_.Set(); |
+ new_countinuous_frame_event_.Set(); |
} |
-void FrameBuffer::InsertFrame(std::unique_ptr<FrameObject> frame) { |
+int FrameBuffer::InsertFrame(std::unique_ptr<FrameObject> frame) { |
rtc::CritScope lock(&crit_); |
- // If |newest_picture_id_| is -1 then this is the first frame we received. |
- if (newest_picture_id_ == -1) |
- newest_picture_id_ = frame->picture_id; |
+ FrameKey key(frame->picture_id, frame->spatial_layer); |
+ int last_continuous_picture_id = |
+ last_continuous_frame_it_ == frames_.end() |
+ ? -1 |
+ : last_continuous_frame_it_->first.picture_id; |
- if (AheadOf<uint16_t>(frame->picture_id, newest_picture_id_)) |
- newest_picture_id_ = frame->picture_id; |
+ if (num_frames_buffered_ >= kMaxFramesBuffered) { |
+ LOG(LS_WARNING) << "Frame with (picture_id:spatial_id) (" << key.picture_id |
+ << ":" << static_cast<int>(key.spatial_layer) |
+ << ") could not be inserted due to the frame " |
+ << "buffer being full, dropping frame."; |
+ return last_continuous_picture_id; |
+ } |
- // Remove frames as long as we have too many, |kMaxNumHistoryFrames|. |
- while (decoded_frames_.size() > kMaxNumHistoryFrames) |
- decoded_frames_.erase(decoded_frames_.begin()); |
+ if (frame->inter_layer_predicted && frame->spatial_layer == 0) { |
+ LOG(LS_WARNING) << "Frame with (picture_id:spatial_id) (" << key.picture_id |
+ << ":" << static_cast<int>(key.spatial_layer) |
+ << ") is marked as inter layer predicted, dropping frame."; |
+ return last_continuous_picture_id; |
+ } |
- // Remove frames that are too old. |
- uint16_t old_picture_id = Subtract<1 << 16>(newest_picture_id_, kMaxFrameAge); |
- auto old_decoded_it = |
- decoded_frames_.lower_bound(FrameKey(old_picture_id, 0)); |
- decoded_frames_.erase(decoded_frames_.begin(), old_decoded_it); |
+ if (last_decoded_frame_it_ != frames_.end() && |
+ key < last_decoded_frame_it_->first) { |
+ LOG(LS_WARNING) << "Frame with (picture_id:spatial_id) (" << key.picture_id |
+ << ":" << static_cast<int>(key.spatial_layer) |
+ << ") inserted after frame (" |
+ << last_decoded_frame_it_->first.picture_id << ":" |
+ << static_cast<int>( |
+ last_decoded_frame_it_->first.spatial_layer) |
+ << ") was handed off for decoding, dropping frame."; |
+ return last_continuous_picture_id; |
+ } |
- FrameKey key(frame->picture_id, frame->spatial_layer); |
- frames_[key] = std::move(frame); |
- frame_inserted_event_.Set(); |
+ auto info = frames_.insert(std::make_pair(key, FrameInfo())).first; |
+ |
+ if (!UpdateFrameInfoWithIncomingFrame(*frame, info)) { |
+ frames_.erase(info); |
+ return last_continuous_picture_id; |
+ } |
+ |
+ info->second.frame = std::move(frame); |
+ ++num_frames_buffered_; |
+ |
+ if (info->second.num_missing_continuous == 0) { |
+ info->second.continuous = true; |
+ PropagateContinuity(info); |
+ last_continuous_picture_id = last_continuous_frame_it_->first.picture_id; |
+ |
+ // Since we now have new continuous frames there might be a better frame |
+ // to return from NextFrame. Signal that thread so that it again can choose |
+ // which frame to return. |
+ new_countinuous_frame_event_.Set(); |
+ } |
+ |
+ return last_continuous_picture_id; |
} |
-bool FrameBuffer::IsContinuous(const FrameObject& frame) const { |
- // If a frame with an earlier picture id was inserted compared to the last |
- // decoded frames picture id then that frame arrived too late. |
- if (!decoded_frames_.empty() && |
- AheadOf(decoded_frames_.rbegin()->first, frame.picture_id)) { |
- return false; |
+void FrameBuffer::PropagateContinuity(FrameMap::iterator start) { |
+ RTC_DCHECK(start->second.continuous); |
+ if (last_continuous_frame_it_ == frames_.end()) |
+ last_continuous_frame_it_ = start; |
+ |
+ std::queue<FrameMap::iterator> continuous_frames; |
+ continuous_frames.push(start); |
+ |
+ // A simple BFS to traverse continuous frames. |
+ while (!continuous_frames.empty()) { |
+ auto frame = continuous_frames.front(); |
+ continuous_frames.pop(); |
+ |
+ if (last_continuous_frame_it_->first < frame->first) |
+ last_continuous_frame_it_ = frame; |
+ |
+ // Loop through all dependent frames, and if that frame no longer has |
+ // any unfulfilled dependencies then that frame is continuous as well. |
+ for (size_t d = 0; d < frame->second.num_dependent_frames; ++d) { |
+ auto frame_ref = frames_.find(frame->second.dependent_frames[d]); |
+ --frame_ref->second.num_missing_continuous; |
+ |
+ if (frame_ref->second.num_missing_continuous == 0) { |
+ frame_ref->second.continuous = true; |
+ continuous_frames.push(frame_ref); |
+ } |
+ } |
+ } |
+} |
+ |
+void FrameBuffer::PropagateDecodability(const FrameInfo& info) { |
+ for (size_t d = 0; d < info.num_dependent_frames; ++d) { |
+ auto ref_info = frames_.find(info.dependent_frames[d]); |
+ RTC_DCHECK_GT(ref_info->second.num_missing_decodable, 0U); |
+ --ref_info->second.num_missing_decodable; |
+ } |
+} |
+ |
+void FrameBuffer::AdvanceLastDecodedFrame(FrameMap::iterator decoded) { |
+ if (last_decoded_frame_it_ == frames_.end()) { |
+ last_decoded_frame_it_ = frames_.begin(); |
+ } else { |
+ RTC_DCHECK(last_decoded_frame_it_->first < decoded->first); |
+ ++last_decoded_frame_it_; |
+ } |
+ --num_frames_buffered_; |
+ ++num_frames_history_; |
+ |
+ // First, delete non-decoded frames from the history. |
+ while (last_decoded_frame_it_ != decoded) { |
+ if (last_decoded_frame_it_->second.frame) |
+ --num_frames_buffered_; |
+ last_decoded_frame_it_ = frames_.erase(last_decoded_frame_it_); |
} |
- // Have we decoded all frames that this frame depend on? |
- for (size_t r = 0; r < frame.num_references; ++r) { |
- FrameKey ref_key(frame.references[r], frame.spatial_layer); |
- if (decoded_frames_.find(ref_key) == decoded_frames_.end()) |
- return false; |
+ // Then remove old history if we have too much history saved. |
+ if (num_frames_history_ > kMaxFramesHistory) { |
+ frames_.erase(frames_.begin()); |
+ --num_frames_history_; |
} |
+} |
+ |
+bool FrameBuffer::UpdateFrameInfoWithIncomingFrame(const FrameObject& frame, |
+ FrameMap::iterator info) { |
+ FrameKey key(frame.picture_id, frame.spatial_layer); |
+ info->second.num_missing_continuous = frame.num_references; |
+ info->second.num_missing_decodable = frame.num_references; |
+ |
+ RTC_DCHECK(last_decoded_frame_it_ == frames_.end() || |
+ last_decoded_frame_it_->first < info->first); |
+ |
+ // Check how many dependencies that have already been fulfilled. |
+ for (size_t i = 0; i < frame.num_references; ++i) { |
+ FrameKey ref_key(frame.references[i], frame.spatial_layer); |
+ auto ref_info = frames_.find(ref_key); |
+ |
+ // Does |frame| depend on a frame earlier than the last decoded frame? |
+ if (last_decoded_frame_it_ != frames_.end() && |
+ ref_key <= last_decoded_frame_it_->first) { |
+ if (ref_info == frames_.end()) { |
+ LOG(LS_WARNING) << "Frame with (picture_id:spatial_id) (" |
+ << key.picture_id << ":" |
+ << static_cast<int>(key.spatial_layer) |
+ << " depends on a non-decoded frame more previous than " |
+ << "the last decoded frame, dropping frame."; |
+ return false; |
+ } |
+ |
+ --info->second.num_missing_continuous; |
+ --info->second.num_missing_decodable; |
+ } else { |
+ if (ref_info == frames_.end()) |
+ ref_info = frames_.insert(std::make_pair(ref_key, FrameInfo())).first; |
- // If this is a layer frame, have we decoded the lower layer of this |
- // super frame. |
+ if (ref_info->second.continuous) |
+ --info->second.num_missing_continuous; |
+ |
+ // Add backwards reference so |frame| can be updated when new |
+ // frames are inserted or decoded. |
+ ref_info->second.dependent_frames[ref_info->second.num_dependent_frames] = |
+ key; |
+ ++ref_info->second.num_dependent_frames; |
+ } |
+ } |
+ |
+ // Check if we have the lower spatial layer frame. |
if (frame.inter_layer_predicted) { |
- RTC_DCHECK_GT(frame.spatial_layer, 0); |
+ ++info->second.num_missing_continuous; |
+ ++info->second.num_missing_decodable; |
+ |
FrameKey ref_key(frame.picture_id, frame.spatial_layer - 1); |
- if (decoded_frames_.find(ref_key) == decoded_frames_.end()) |
- return false; |
+ // Gets or create the FrameInfo for the referenced frame. |
stefan-webrtc
2016/09/27 14:11:40
Get or create
|
+ auto ref_info = frames_.insert(std::make_pair(ref_key, FrameInfo())).first; |
+ if (ref_info->second.continuous) |
+ --info->second.num_missing_continuous; |
+ |
+ if (ref_info == last_decoded_frame_it_) { |
+ --info->second.num_missing_decodable; |
+ } else { |
+ ref_info->second.dependent_frames[ref_info->second.num_dependent_frames] = |
+ key; |
+ ++ref_info->second.num_dependent_frames; |
+ } |
} |
return true; |