diff --git a/modules/video_coding/packet_buffer.cc b/modules/video_coding/packet_buffer.cc index 741c11e2aa..4a8904e61e 100644 --- a/modules/video_coding/packet_buffer.cc +++ b/modules/video_coding/packet_buffer.cc @@ -48,6 +48,7 @@ PacketBuffer::PacketBuffer(Clock* clock, data_buffer_(start_buffer_size), sequence_buffer_(start_buffer_size), received_frame_callback_(received_frame_callback), + unique_frames_seen_(0), sps_pps_idr_is_h264_keyframe_( field_trial::IsEnabled("WebRTC-SpsPpsIdrIsH264Keyframe")) { RTC_DCHECK_LE(start_buffer_size, max_buffer_size); @@ -65,6 +66,8 @@ bool PacketBuffer::InsertPacket(VCMPacket* packet) { { rtc::CritScope lock(&crit_); + OnTimestampReceived(packet->timestamp); + uint16_t seq_num = packet->seqNum; size_t index = seq_num % size_; @@ -207,6 +210,11 @@ rtc::Optional PacketBuffer::LastReceivedKeyframePacketMs() const { return last_received_keyframe_packet_ms_; } +int PacketBuffer::GetUniqueFramesSeen() const { + rtc::CritScope lock(&crit_); + return unique_frames_seen_; +} + bool PacketBuffer::ExpandBufferSize() { if (size_ == max_size_) { RTC_LOG(LS_WARNING) << "PacketBuffer is already at max size (" << max_size_ @@ -484,5 +492,18 @@ void PacketBuffer::UpdateMissingPackets(uint16_t seq_num) { } } +void PacketBuffer::OnTimestampReceived(uint32_t rtp_timestamp) { + const size_t kMaxTimestampsHistory = 1000; + if (rtp_timestamps_history_set_.insert(rtp_timestamp).second) { + rtp_timestamps_history_queue_.push(rtp_timestamp); + ++unique_frames_seen_; + if (rtp_timestamps_history_set_.size() > kMaxTimestampsHistory) { + uint32_t discarded_timestamp = rtp_timestamps_history_queue_.front(); + rtp_timestamps_history_set_.erase(discarded_timestamp); + rtp_timestamps_history_queue_.pop(); + } + } +} + } // namespace video_coding } // namespace webrtc diff --git a/modules/video_coding/packet_buffer.h b/modules/video_coding/packet_buffer.h index c1ca7b8d39..869a81c835 100644 --- a/modules/video_coding/packet_buffer.h +++ b/modules/video_coding/packet_buffer.h @@ -12,6 +12,7 @@ #define MODULES_VIDEO_CODING_PACKET_BUFFER_H_ #include +#include #include #include @@ -61,6 +62,9 @@ class PacketBuffer { rtc::Optional LastReceivedPacketMs() const; rtc::Optional LastReceivedKeyframePacketMs() const; + // Returns number of different frames seen in the packet buffer + int GetUniqueFramesSeen() const; + int AddRef() const; int Release() const; @@ -126,6 +130,10 @@ class PacketBuffer { void UpdateMissingPackets(uint16_t seq_num) RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); + // Counts unique received timestamps and updates |unique_frames_seen_|. + void OnTimestampReceived(uint32_t rtp_timestamp) + RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); + rtc::CriticalSection crit_; // Buffer size_ and max_size_ must always be a power of two. @@ -156,6 +164,8 @@ class PacketBuffer { rtc::Optional last_received_keyframe_packet_ms_ RTC_GUARDED_BY(crit_); + int unique_frames_seen_ RTC_GUARDED_BY(crit_); + rtc::Optional newest_inserted_seq_num_ RTC_GUARDED_BY(crit_); std::set> missing_packets_ RTC_GUARDED_BY(crit_); @@ -164,6 +174,11 @@ class PacketBuffer { // RTP timestamp to treat the corresponding frame as a keyframe. const bool sps_pps_idr_is_h264_keyframe_; + // Stores several last seen unique timestamps for quick search. + std::set rtp_timestamps_history_set_ RTC_GUARDED_BY(crit_); + // Stores the same unique timestamps in the order of insertion. + std::queue rtp_timestamps_history_queue_ RTC_GUARDED_BY(crit_); + mutable volatile int ref_count_ = 0; }; diff --git a/modules/video_coding/video_packet_buffer_unittest.cc b/modules/video_coding/video_packet_buffer_unittest.cc index 893649364a..db29b8b728 100644 --- a/modules/video_coding/video_packet_buffer_unittest.cc +++ b/modules/video_coding/video_packet_buffer_unittest.cc @@ -54,14 +54,16 @@ class TestPacketBuffer : public ::testing::Test, enum IsFirst { kFirst, kNotFirst }; enum IsLast { kLast, kNotLast }; - bool Insert(uint16_t seq_num, // packet sequence number - IsKeyFrame keyframe, // is keyframe - IsFirst first, // is first packet of frame - IsLast last, // is last packet of frame - int data_size = 0, // size of data - uint8_t* data = nullptr) { // data pointer + bool Insert(uint16_t seq_num, // packet sequence number + IsKeyFrame keyframe, // is keyframe + IsFirst first, // is first packet of frame + IsLast last, // is last packet of frame + int data_size = 0, // size of data + uint8_t* data = nullptr, // data pointer + uint32_t timestamp = 123u) { // rtp timestamp VCMPacket packet; packet.codec = kVideoCodecGeneric; + packet.timestamp = timestamp; packet.seqNum = seq_num; packet.frameType = keyframe == kKeyFrame ? kVideoFrameKey : kVideoFrameDelta; @@ -195,6 +197,64 @@ TEST_F(TestPacketBuffer, FrameSize) { EXPECT_EQ(20UL, frames_from_callback_.begin()->second->size()); } +TEST_F(TestPacketBuffer, CountsUniqueFrames) { + const uint16_t seq_num = Rand(); + + ASSERT_EQ(0, packet_buffer_->GetUniqueFramesSeen()); + + EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast, 0, nullptr, 100)); + ASSERT_EQ(1, packet_buffer_->GetUniqueFramesSeen()); + // Still the same frame. + EXPECT_TRUE( + Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast, 0, nullptr, 100)); + ASSERT_EQ(1, packet_buffer_->GetUniqueFramesSeen()); + + // Second frame. + EXPECT_TRUE( + Insert(seq_num + 2, kKeyFrame, kFirst, kNotLast, 0, nullptr, 200)); + ASSERT_EQ(2, packet_buffer_->GetUniqueFramesSeen()); + EXPECT_TRUE( + Insert(seq_num + 3, kKeyFrame, kNotFirst, kLast, 0, nullptr, 200)); + ASSERT_EQ(2, packet_buffer_->GetUniqueFramesSeen()); + + // Old packet. + EXPECT_TRUE( + Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast, 0, nullptr, 100)); + ASSERT_EQ(2, packet_buffer_->GetUniqueFramesSeen()); + + // Missing middle packet. + EXPECT_TRUE( + Insert(seq_num + 4, kKeyFrame, kFirst, kNotLast, 0, nullptr, 300)); + EXPECT_TRUE( + Insert(seq_num + 6, kKeyFrame, kNotFirst, kLast, 0, nullptr, 300)); + ASSERT_EQ(3, packet_buffer_->GetUniqueFramesSeen()); +} + +TEST_F(TestPacketBuffer, HasHistoryOfUniqueFrames) { + const int kNumFrames = 1500; + const int kRequiredHistoryLength = 1000; + const uint16_t seq_num = Rand(); + const uint32_t timestamp = 0xFFFFFFF0; // Large enough to cause wrap-around. + + for (int i = 0; i < kNumFrames; ++i) { + EXPECT_TRUE(Insert(seq_num + i, kKeyFrame, kFirst, kNotLast, 0, nullptr, + timestamp + 10 * i)); + } + ASSERT_EQ(kNumFrames, packet_buffer_->GetUniqueFramesSeen()); + + // Old packets within history should not affect number of seen unique frames. + for (int i = kNumFrames - kRequiredHistoryLength; i < kNumFrames; ++i) { + EXPECT_TRUE(Insert(seq_num + i, kKeyFrame, kFirst, kNotLast, 0, nullptr, + timestamp + 10 * i)); + } + ASSERT_EQ(kNumFrames, packet_buffer_->GetUniqueFramesSeen()); + + // Very old packets should be treated as unique. + EXPECT_TRUE( + Insert(seq_num, kKeyFrame, kFirst, kNotLast, 0, nullptr, timestamp)); + ASSERT_EQ(kNumFrames + 1, packet_buffer_->GetUniqueFramesSeen()); +} + TEST_F(TestPacketBuffer, ExpandBuffer) { const uint16_t seq_num = Rand(); diff --git a/video/receive_statistics_proxy.cc b/video/receive_statistics_proxy.cc index b54e2a0f24..ffb925d977 100644 --- a/video/receive_statistics_proxy.cc +++ b/video/receive_statistics_proxy.cc @@ -136,6 +136,16 @@ void ReceiveStatisticsProxy::UpdateHistograms() { << stream_duration_sec << "\n"; } + RTC_LOG(LS_INFO) << "Frames decoded " << stats_.frames_decoded; + + if (num_unique_frames_) { + int num_dropped_frames = *num_unique_frames_ - stats_.frames_decoded; + RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.DroppedFrames.Receiver", + num_dropped_frames); + RTC_LOG(LS_INFO) << "WebRTC.Video.DroppedFrames.Receiver " + << num_dropped_frames; + } + if (first_report_block_time_ms_ != -1 && ((clock_->TimeInMilliseconds() - first_report_block_time_ms_) / 1000) >= metrics::kMinRunTimeInSeconds) { @@ -579,6 +589,11 @@ void ReceiveStatisticsProxy::OnFrameBufferTimingsUpdated( delay_counter_.Add(target_delay_ms + avg_rtt_ms_ / 2); } +void ReceiveStatisticsProxy::OnUniqueFramesCounted(int num_unique_frames) { + rtc::CritScope lock(&crit_); + num_unique_frames_.emplace(num_unique_frames); +} + void ReceiveStatisticsProxy::OnTimingFrameInfoUpdated( const TimingFrameInfo& info) { rtc::CritScope lock(&crit_); diff --git a/video/receive_statistics_proxy.h b/video/receive_statistics_proxy.h index bdeee86343..836bb4b62f 100644 --- a/video/receive_statistics_proxy.h +++ b/video/receive_statistics_proxy.h @@ -60,6 +60,8 @@ class ReceiveStatisticsProxy : public VCMReceiveStatisticsCallback, void OnPreDecode(const EncodedImage& encoded_image, const CodecSpecificInfo* codec_specific_info); + void OnUniqueFramesCounted(int num_unique_frames); + // Indicates video stream has been paused (no incoming packets). void OnStreamInactive(); @@ -188,6 +190,7 @@ class ReceiveStatisticsProxy : public VCMReceiveStatisticsCallback, // called from const GetStats(). mutable rtc::MovingMaxCounter timing_frame_info_counter_ RTC_GUARDED_BY(&crit_); + rtc::Optional num_unique_frames_ RTC_GUARDED_BY(crit_); }; } // namespace webrtc diff --git a/video/rtp_video_stream_receiver.cc b/video/rtp_video_stream_receiver.cc index 7a06f1c479..e01e85fd23 100644 --- a/video/rtp_video_stream_receiver.cc +++ b/video/rtp_video_stream_receiver.cc @@ -589,6 +589,10 @@ void RtpVideoStreamReceiver::SignalNetworkState(NetworkState state) { : RtcpMode::kOff); } +int RtpVideoStreamReceiver::GetUniqueFramesSeen() const { + return packet_buffer_->GetUniqueFramesSeen(); +} + void RtpVideoStreamReceiver::StartReceive() { RTC_DCHECK_CALLED_SEQUENTIALLY(&worker_task_checker_); receiving_ = true; diff --git a/video/rtp_video_stream_receiver.h b/video/rtp_video_stream_receiver.h index aa5b781789..71cb5dbcdd 100644 --- a/video/rtp_video_stream_receiver.h +++ b/video/rtp_video_stream_receiver.h @@ -97,6 +97,9 @@ class RtpVideoStreamReceiver : public RtpData, void SignalNetworkState(NetworkState state); + // Returns number of different frames seen in the packet buffer. + int GetUniqueFramesSeen() const; + // Implements RtpPacketSinkInterface. void OnRtpPacket(const RtpPacketReceived& packet) override; diff --git a/video/video_receive_stream.cc b/video/video_receive_stream.cc index dcffc9f703..9a572cae9f 100644 --- a/video/video_receive_stream.cc +++ b/video/video_receive_stream.cc @@ -234,6 +234,9 @@ void VideoReceiveStream::Stop() { RTC_DCHECK_CALLED_SEQUENTIALLY(&worker_sequence_checker_); rtp_video_stream_receiver_.StopReceive(); + stats_proxy_.OnUniqueFramesCounted( + rtp_video_stream_receiver_.GetUniqueFramesSeen()); + frame_buffer_->Stop(); call_stats_->DeregisterStatsObserver(this); call_stats_->DeregisterStatsObserver(&rtp_video_stream_receiver_);