Migrate VCMInterFrameDelay to use Time units
Additionally, * Moved to its own GN target. * Added unittests. * Removed unused variable `_zeroWallClock`. * Renamed variables to match style guide. * Moved fields _dTS and _wrapArounds to variables. Change-Id: I7aa8b8dec55abab49ceabe838dabf2a7e13d685d Bug: webrtc:13756 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/253580 Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Evan Shrubsole <eshr@webrtc.org> Cr-Commit-Position: refs/heads/main@{#36147}
This commit is contained in:

committed by
WebRTC LUCI CQ

parent
773205dfb2
commit
e9126c18bf
@ -14,6 +14,12 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
absl::optional<Timestamp> EncodedFrame::ReceivedTimestamp() const {
|
||||
return ReceivedTime() >= 0
|
||||
? absl::make_optional(Timestamp::Millis(ReceivedTime()))
|
||||
: absl::nullopt;
|
||||
}
|
||||
|
||||
absl::optional<Timestamp> EncodedFrame::RenderTimestamp() const {
|
||||
return RenderTimeMs() >= 0
|
||||
? absl::make_optional(Timestamp::Millis(RenderTimeMs()))
|
||||
|
@ -34,6 +34,9 @@ class EncodedFrame : public webrtc::VCMEncodedFrame {
|
||||
// When this frame was received.
|
||||
// TODO(bugs.webrtc.org/13756): Use Timestamp instead of int.
|
||||
virtual int64_t ReceivedTime() const = 0;
|
||||
// Returns a Timestamp from `ReceivedTime`, or nullopt if there is no receive
|
||||
// time.
|
||||
absl::optional<webrtc::Timestamp> ReceivedTimestamp() const;
|
||||
|
||||
// When this frame should be rendered.
|
||||
// TODO(bugs.webrtc.org/13756): Use Timestamp instead of int.
|
||||
|
@ -234,6 +234,20 @@ rtc_library("jitter_estimator") {
|
||||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||
}
|
||||
|
||||
rtc_library("inter_frame_delay") {
|
||||
sources = [
|
||||
"inter_frame_delay.cc",
|
||||
"inter_frame_delay.h",
|
||||
]
|
||||
deps = [
|
||||
"..:module_api_public",
|
||||
"../../api/units:frequency",
|
||||
"../../api/units:time_delta",
|
||||
"../../api/units:timestamp",
|
||||
]
|
||||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||
}
|
||||
|
||||
rtc_library("video_coding") {
|
||||
visibility = [ "*" ]
|
||||
sources = [
|
||||
@ -253,8 +267,6 @@ rtc_library("video_coding") {
|
||||
"h264_sps_pps_tracker.cc",
|
||||
"h264_sps_pps_tracker.h",
|
||||
"include/video_codec_initializer.h",
|
||||
"inter_frame_delay.cc",
|
||||
"inter_frame_delay.h",
|
||||
"internal_defines.h",
|
||||
"loss_notification_controller.cc",
|
||||
"loss_notification_controller.h",
|
||||
@ -286,8 +298,8 @@ rtc_library("video_coding") {
|
||||
":encoded_frame",
|
||||
":frame_buffer",
|
||||
":frame_helpers",
|
||||
":inter_frame_delay",
|
||||
":jitter_estimator",
|
||||
":packet_buffer",
|
||||
":rtt_filter",
|
||||
":timing",
|
||||
":video_codec_interface",
|
||||
@ -402,6 +414,7 @@ rtc_library("video_coding_legacy") {
|
||||
deps = [
|
||||
":codec_globals_headers",
|
||||
":encoded_frame",
|
||||
":inter_frame_delay",
|
||||
":jitter_estimator",
|
||||
":timing",
|
||||
":video_codec_interface",
|
||||
@ -1107,6 +1120,7 @@ if (rtc_include_tests) {
|
||||
"h264_sprop_parameter_sets_unittest.cc",
|
||||
"h264_sps_pps_tracker_unittest.cc",
|
||||
"histogram_unittest.cc",
|
||||
"inter_frame_delay_unittest.cc",
|
||||
"jitter_buffer_unittest.cc",
|
||||
"jitter_estimator_tests.cc",
|
||||
"loss_notification_controller_unittest.cc",
|
||||
@ -1151,6 +1165,7 @@ if (rtc_include_tests) {
|
||||
":frame_buffer",
|
||||
":frame_dependencies_calculator",
|
||||
":h264_packet_buffer",
|
||||
":inter_frame_delay",
|
||||
":jitter_estimator",
|
||||
":nack_requester",
|
||||
":packet_buffer",
|
||||
|
@ -64,7 +64,6 @@ FrameBuffer::FrameBuffer(Clock* clock,
|
||||
callback_queue_(nullptr),
|
||||
jitter_estimator_(clock),
|
||||
timing_(timing),
|
||||
inter_frame_delay_(clock_->TimeInMilliseconds()),
|
||||
stopped_(false),
|
||||
protection_mode_(kProtectionNack),
|
||||
stats_callback_(stats_callback),
|
||||
@ -295,12 +294,11 @@ std::unique_ptr<EncodedFrame> FrameBuffer::GetNextFrame() {
|
||||
}
|
||||
|
||||
if (!superframe_delayed_by_retransmission) {
|
||||
int64_t frame_delay;
|
||||
auto frame_delay = inter_frame_delay_.CalculateDelay(
|
||||
first_frame.Timestamp(), Timestamp::Millis(receive_time_ms));
|
||||
|
||||
if (inter_frame_delay_.CalculateDelay(first_frame.Timestamp(), &frame_delay,
|
||||
receive_time_ms)) {
|
||||
jitter_estimator_.UpdateEstimate(TimeDelta::Millis(frame_delay),
|
||||
superframe_size);
|
||||
if (frame_delay) {
|
||||
jitter_estimator_.UpdateEstimate(*frame_delay, superframe_size);
|
||||
}
|
||||
|
||||
float rtt_mult = protection_mode_ == kProtectionNackFEC ? 0.0 : 1.0;
|
||||
|
@ -10,85 +10,62 @@
|
||||
|
||||
#include "modules/video_coding/inter_frame_delay.h"
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/units/frequency.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "modules/include/module_common_types_public.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
VCMInterFrameDelay::VCMInterFrameDelay(int64_t currentWallClock) {
|
||||
Reset(currentWallClock);
|
||||
namespace {
|
||||
constexpr Frequency k90kHz = Frequency::KiloHertz(90);
|
||||
}
|
||||
|
||||
VCMInterFrameDelay::VCMInterFrameDelay() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
// Resets the delay estimate.
|
||||
void VCMInterFrameDelay::Reset(int64_t currentWallClock) {
|
||||
_zeroWallClock = currentWallClock;
|
||||
_wrapArounds = 0;
|
||||
_prevWallClock = 0;
|
||||
_prevTimestamp = 0;
|
||||
_dTS = 0;
|
||||
void VCMInterFrameDelay::Reset() {
|
||||
prev_wall_clock_ = absl::nullopt;
|
||||
prev_rtp_timestamp_unwrapped_ = 0;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
absl::optional<TimeDelta> VCMInterFrameDelay::CalculateDelay(
|
||||
uint32_t rtp_timestamp,
|
||||
Timestamp now) {
|
||||
int64_t rtp_timestamp_unwrapped = unwrapper_.Unwrap(rtp_timestamp);
|
||||
if (!prev_wall_clock_) {
|
||||
// First set of data, initialization, wait for next frame.
|
||||
_prevWallClock = currentWallClock;
|
||||
_prevTimestamp = timestamp;
|
||||
*delay = 0;
|
||||
return true;
|
||||
prev_wall_clock_ = now;
|
||||
prev_rtp_timestamp_unwrapped_ = rtp_timestamp_unwrapped;
|
||||
return TimeDelta::Zero();
|
||||
}
|
||||
|
||||
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;
|
||||
uint32_t cropped_last = static_cast<uint32_t>(prev_rtp_timestamp_unwrapped_);
|
||||
if (rtp_timestamp_unwrapped < prev_rtp_timestamp_unwrapped_ ||
|
||||
!IsNewerTimestamp(rtp_timestamp, cropped_last)) {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
// 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);
|
||||
// Compute the compensated timestamp difference.
|
||||
int64_t d_rtp_ticks = rtp_timestamp_unwrapped - prev_rtp_timestamp_unwrapped_;
|
||||
TimeDelta dts = d_rtp_ticks / k90kHz;
|
||||
TimeDelta dt = now - *prev_wall_clock_;
|
||||
|
||||
// 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);
|
||||
TimeDelta delay = dt - dts;
|
||||
|
||||
_prevTimestamp = timestamp;
|
||||
_prevWallClock = currentWallClock;
|
||||
|
||||
return true;
|
||||
prev_rtp_timestamp_unwrapped_ = rtp_timestamp_unwrapped;
|
||||
prev_wall_clock_ = now;
|
||||
return delay;
|
||||
}
|
||||
|
||||
// Investigates if the timestamp clock has overflowed since the last timestamp
|
||||
// and keeps track of the number of wrap arounds since reset.
|
||||
void VCMInterFrameDelay::CheckForWrapArounds(uint32_t timestamp) {
|
||||
if (timestamp < _prevTimestamp) {
|
||||
// This difference will probably be less than -2^31 if we have had a wrap
|
||||
// around (e.g. timestamp = 1, _prevTimestamp = 2^32 - 1). Since it is cast
|
||||
// to a int32_t, it should be positive.
|
||||
if (static_cast<int32_t>(timestamp - _prevTimestamp) > 0) {
|
||||
// Forward wrap around.
|
||||
_wrapArounds++;
|
||||
}
|
||||
// This difference will probably be less than -2^31 if we have had a
|
||||
// backward wrap around. Since it is cast to a int32_t, it should be
|
||||
// positive.
|
||||
} else if (static_cast<int32_t>(_prevTimestamp - timestamp) > 0) {
|
||||
// Backward wrap around.
|
||||
_wrapArounds--;
|
||||
}
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
@ -13,46 +13,32 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "modules/include/module_common_types_public.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VCMInterFrameDelay {
|
||||
public:
|
||||
explicit VCMInterFrameDelay(int64_t currentWallClock);
|
||||
VCMInterFrameDelay();
|
||||
|
||||
// Resets the estimate. Zeros are given as parameters.
|
||||
void Reset(int64_t currentWallClock);
|
||||
void Reset();
|
||||
|
||||
// Calculates the delay of a frame with the given timestamp.
|
||||
// This method is called when the frame is complete.
|
||||
//
|
||||
// Input:
|
||||
// - timestamp : RTP timestamp of a received frame.
|
||||
// - *delay : Pointer to memory where the result should be
|
||||
// stored.
|
||||
// - currentWallClock : The current time in milliseconds.
|
||||
// Should be -1 for normal operation, only used
|
||||
// for testing.
|
||||
// Return value : true if OK, false when reordered timestamps.
|
||||
bool CalculateDelay(uint32_t timestamp,
|
||||
int64_t* delay,
|
||||
int64_t currentWallClock);
|
||||
absl::optional<TimeDelta> CalculateDelay(uint32_t rtp_timestamp,
|
||||
Timestamp now);
|
||||
|
||||
private:
|
||||
// Controls if the RTP timestamp counter has had a wrap around between the
|
||||
// current and the previously received frame.
|
||||
//
|
||||
// Input:
|
||||
// - timestamp : RTP timestamp of the current frame.
|
||||
void CheckForWrapArounds(uint32_t timestamp);
|
||||
// The previous rtp timestamp passed to the delay estimate
|
||||
int64_t prev_rtp_timestamp_unwrapped_;
|
||||
TimestampUnwrapper unwrapper_;
|
||||
|
||||
int64_t _zeroWallClock; // Local timestamp of the first video packet received
|
||||
int32_t _wrapArounds; // Number of wrapArounds detected
|
||||
// The previous timestamp passed to the delay estimate
|
||||
uint32_t _prevTimestamp;
|
||||
// The previous wall clock timestamp used by the delay estimate
|
||||
int64_t _prevWallClock;
|
||||
// Wrap-around compensated difference between incoming timestamps
|
||||
int64_t _dTS;
|
||||
absl::optional<Timestamp> prev_wall_clock_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
190
modules/video_coding/inter_frame_delay_unittest.cc
Normal file
190
modules/video_coding/inter_frame_delay_unittest.cc
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/video_coding/inter_frame_delay.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/units/frequency.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
// Test is for frames at 30fps. At 30fps, RTP timestamps will increase by
|
||||
// 90000 / 30 = 3000 ticks per frame.
|
||||
constexpr Frequency k30Fps = Frequency::Hertz(30);
|
||||
constexpr TimeDelta kFrameDelay = 1 / k30Fps;
|
||||
constexpr uint32_t kRtpTicksPerFrame = Frequency::KiloHertz(90) / k30Fps;
|
||||
constexpr Timestamp kStartTime = Timestamp::Millis(1337);
|
||||
|
||||
} // namespace
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::Optional;
|
||||
|
||||
TEST(InterFrameDelayTest, OldRtpTimestamp) {
|
||||
VCMInterFrameDelay inter_frame_delay;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(180000, kStartTime),
|
||||
Optional(TimeDelta::Zero()));
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(90000, kStartTime),
|
||||
Eq(absl::nullopt));
|
||||
}
|
||||
|
||||
TEST(InterFrameDelayTest, NegativeWrapAroundIsSameAsOldRtpTimestamp) {
|
||||
VCMInterFrameDelay inter_frame_delay;
|
||||
uint32_t rtp = 1500;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, kStartTime),
|
||||
Optional(TimeDelta::Zero()));
|
||||
// RTP has wrapped around backwards.
|
||||
rtp -= 3000;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, kStartTime),
|
||||
Eq(absl::nullopt));
|
||||
}
|
||||
|
||||
TEST(InterFrameDelayTest, CorrectDelayForFrames) {
|
||||
VCMInterFrameDelay inter_frame_delay;
|
||||
// Use a fake clock to simplify time keeping.
|
||||
SimulatedClock clock(kStartTime);
|
||||
|
||||
// First frame is always delay 0.
|
||||
uint32_t rtp = 90000;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Zero()));
|
||||
|
||||
// Perfectly timed frame has 0 delay.
|
||||
clock.AdvanceTime(kFrameDelay);
|
||||
rtp += kRtpTicksPerFrame;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Zero()));
|
||||
|
||||
// Slightly early frame will have a negative delay.
|
||||
clock.AdvanceTime(kFrameDelay - TimeDelta::Millis(3));
|
||||
rtp += kRtpTicksPerFrame;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(-TimeDelta::Millis(3)));
|
||||
|
||||
// Slightly late frame will have positive delay.
|
||||
clock.AdvanceTime(kFrameDelay + TimeDelta::Micros(5125));
|
||||
rtp += kRtpTicksPerFrame;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Micros(5125)));
|
||||
|
||||
// Simulate faster frame RTP at the same clock delay. The frame arrives late,
|
||||
// since the RTP timestamp is faster than the delay, and thus is positive.
|
||||
clock.AdvanceTime(kFrameDelay);
|
||||
rtp += kRtpTicksPerFrame / 2;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(kFrameDelay / 2.0));
|
||||
|
||||
// Simulate slower frame RTP at the same clock delay. The frame is early,
|
||||
// since the RTP timestamp advanced more than the delay, and thus is negative.
|
||||
clock.AdvanceTime(kFrameDelay);
|
||||
rtp += 1.5 * kRtpTicksPerFrame;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(-kFrameDelay / 2.0));
|
||||
}
|
||||
|
||||
TEST(InterFrameDelayTest, PositiveWrapAround) {
|
||||
VCMInterFrameDelay inter_frame_delay;
|
||||
// Use a fake clock to simplify time keeping.
|
||||
SimulatedClock clock(kStartTime);
|
||||
|
||||
// First frame is behind the max RTP by 1500.
|
||||
uint32_t rtp = std::numeric_limits<uint32_t>::max() - 1500;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Zero()));
|
||||
|
||||
// Rtp wraps around, now 1499.
|
||||
rtp += kRtpTicksPerFrame;
|
||||
|
||||
// Frame delay should be as normal, in this case simulated as 1ms late.
|
||||
clock.AdvanceTime(kFrameDelay + TimeDelta::Millis(1));
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Millis(1)));
|
||||
}
|
||||
|
||||
TEST(InterFrameDelayTest, MultipleWrapArounds) {
|
||||
// Simulate a long pauses which cause wrap arounds multiple times.
|
||||
constexpr Frequency k90Khz = Frequency::KiloHertz(90);
|
||||
constexpr uint32_t kHalfRtp = std::numeric_limits<uint32_t>::max() / 2;
|
||||
constexpr TimeDelta kWrapAroundDelay = kHalfRtp / k90Khz;
|
||||
|
||||
VCMInterFrameDelay inter_frame_delay;
|
||||
// Use a fake clock to simplify time keeping.
|
||||
SimulatedClock clock(kStartTime);
|
||||
uint32_t rtp = 0;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Zero()));
|
||||
|
||||
rtp += kHalfRtp;
|
||||
clock.AdvanceTime(kWrapAroundDelay);
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Zero()));
|
||||
// 1st wrap around.
|
||||
rtp += kHalfRtp + 1;
|
||||
clock.AdvanceTime(kWrapAroundDelay + TimeDelta::Millis(1));
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Millis(1) - (1 / k90Khz)));
|
||||
|
||||
rtp += kHalfRtp;
|
||||
clock.AdvanceTime(kWrapAroundDelay);
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Zero()));
|
||||
// 2nd wrap arounds.
|
||||
rtp += kHalfRtp + 1;
|
||||
clock.AdvanceTime(kWrapAroundDelay - TimeDelta::Millis(1));
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(-TimeDelta::Millis(1) - (1 / k90Khz)));
|
||||
|
||||
// Ensure short delay (large RTP delay) between wrap-arounds has correct
|
||||
// jitter.
|
||||
rtp += kHalfRtp;
|
||||
clock.AdvanceTime(TimeDelta::Millis(10));
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(-(kWrapAroundDelay - TimeDelta::Millis(10))));
|
||||
// 3nd wrap arounds, this time with large RTP delay.
|
||||
rtp += kHalfRtp + 1;
|
||||
clock.AdvanceTime(TimeDelta::Millis(10));
|
||||
EXPECT_THAT(
|
||||
inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(-(kWrapAroundDelay - TimeDelta::Millis(10) + (1 / k90Khz))));
|
||||
}
|
||||
|
||||
TEST(InterFrameDelayTest, NegativeWrapAroundAfterPositiveWrapAround) {
|
||||
VCMInterFrameDelay inter_frame_delay;
|
||||
// Use a fake clock to simplify time keeping.
|
||||
SimulatedClock clock(kStartTime);
|
||||
uint32_t rtp = std::numeric_limits<uint32_t>::max() - 1500;
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Zero()));
|
||||
|
||||
// Rtp wraps around, now 1499.
|
||||
rtp += kRtpTicksPerFrame;
|
||||
// Frame delay should be as normal, in this case simulated as 1ms late.
|
||||
clock.AdvanceTime(kFrameDelay);
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Optional(TimeDelta::Zero()));
|
||||
|
||||
// Wrap back.
|
||||
rtp -= kRtpTicksPerFrame;
|
||||
// Frame delay should be as normal, in this case simulated as 1ms late.
|
||||
clock.AdvanceTime(kFrameDelay);
|
||||
EXPECT_THAT(inter_frame_delay.CalculateDelay(rtp, clock.CurrentTime()),
|
||||
Eq(absl::nullopt));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
@ -9,11 +9,11 @@
|
||||
*/
|
||||
#include "modules/video_coding/jitter_buffer.h"
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "api/units/timestamp.h"
|
||||
#include "modules/video_coding/frame_buffer.h"
|
||||
#include "modules/video_coding/include/video_coding.h"
|
||||
#include "modules/video_coding/inter_frame_delay.h"
|
||||
@ -123,7 +123,6 @@ VCMJitterBuffer::VCMJitterBuffer(Clock* clock,
|
||||
num_packets_(0),
|
||||
num_duplicated_packets_(0),
|
||||
jitter_estimate_(clock),
|
||||
inter_frame_delay_(clock_->TimeInMilliseconds()),
|
||||
missing_sequence_numbers_(SequenceNumberLessThan()),
|
||||
latest_received_sequence_number_(0),
|
||||
max_nack_list_size_(0),
|
||||
@ -192,7 +191,7 @@ void VCMJitterBuffer::Flush() {
|
||||
num_consecutive_old_packets_ = 0;
|
||||
// Also reset the jitter and delay estimates
|
||||
jitter_estimate_.Reset();
|
||||
inter_frame_delay_.Reset(clock_->TimeInMilliseconds());
|
||||
inter_frame_delay_.Reset();
|
||||
waiting_for_completion_.frame_size = 0;
|
||||
waiting_for_completion_.timestamp = 0;
|
||||
waiting_for_completion_.latest_packet_time = -1;
|
||||
@ -392,13 +391,13 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet,
|
||||
if (error != kNoError)
|
||||
return error;
|
||||
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
Timestamp now = clock_->CurrentTime();
|
||||
// We are keeping track of the first and latest seq numbers, and
|
||||
// the number of wraps to be able to calculate how many packets we expect.
|
||||
if (first_packet_since_reset_) {
|
||||
// Now it's time to start estimating jitter
|
||||
// reset the delay estimate.
|
||||
inter_frame_delay_.Reset(now_ms);
|
||||
inter_frame_delay_.Reset();
|
||||
}
|
||||
|
||||
// Empty packets may bias the jitter estimate (lacking size component),
|
||||
@ -408,9 +407,9 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet,
|
||||
// This can get bad if we have a lot of duplicate packets,
|
||||
// we will then count some packet multiple times.
|
||||
waiting_for_completion_.frame_size += packet.sizeBytes;
|
||||
waiting_for_completion_.latest_packet_time = now_ms;
|
||||
waiting_for_completion_.latest_packet_time = now.ms();
|
||||
} else if (waiting_for_completion_.latest_packet_time >= 0 &&
|
||||
waiting_for_completion_.latest_packet_time + 2000 <= now_ms) {
|
||||
waiting_for_completion_.latest_packet_time + 2000 <= now.ms()) {
|
||||
// A packet should never be more than two seconds late
|
||||
UpdateJitterEstimate(waiting_for_completion_, true);
|
||||
waiting_for_completion_.latest_packet_time = -1;
|
||||
@ -425,7 +424,7 @@ VCMFrameBufferEnum VCMJitterBuffer::InsertPacket(const VCMPacket& packet,
|
||||
frame_data.rtt_ms = kDefaultRtt;
|
||||
frame_data.rolling_average_packets_per_frame = average_packets_per_frame_;
|
||||
VCMFrameBufferEnum buffer_state =
|
||||
frame->InsertPacket(packet, now_ms, frame_data);
|
||||
frame->InsertPacket(packet, now.ms(), frame_data);
|
||||
|
||||
if (buffer_state > 0) {
|
||||
if (first_packet_since_reset_) {
|
||||
@ -873,14 +872,14 @@ void VCMJitterBuffer::UpdateJitterEstimate(int64_t latest_packet_time_ms,
|
||||
if (latest_packet_time_ms == -1) {
|
||||
return;
|
||||
}
|
||||
int64_t frame_delay;
|
||||
bool not_reordered = inter_frame_delay_.CalculateDelay(
|
||||
timestamp, &frame_delay, latest_packet_time_ms);
|
||||
auto frame_delay = inter_frame_delay_.CalculateDelay(
|
||||
timestamp, Timestamp::Millis(latest_packet_time_ms));
|
||||
|
||||
bool not_reordered = frame_delay.has_value();
|
||||
// Filter out frames which have been reordered in time by the network
|
||||
if (not_reordered) {
|
||||
// Update the jitter estimate with the new samples
|
||||
jitter_estimate_.UpdateEstimate(TimeDelta::Millis(frame_delay),
|
||||
DataSize::Bytes(frame_size),
|
||||
jitter_estimate_.UpdateEstimate(*frame_delay, DataSize::Bytes(frame_size),
|
||||
incomplete_frame);
|
||||
}
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ rtc_library("video") {
|
||||
"../modules/video_coding:codec_globals_headers",
|
||||
"../modules/video_coding:frame_buffer",
|
||||
"../modules/video_coding:frame_helpers",
|
||||
"../modules/video_coding:inter_frame_delay",
|
||||
"../modules/video_coding:jitter_estimator",
|
||||
"../modules/video_coding:nack_requester",
|
||||
"../modules/video_coding:packet_buffer",
|
||||
|
@ -149,7 +149,7 @@ struct FrameMetadata {
|
||||
contentType(frame.contentType()),
|
||||
delayed_by_retransmission(frame.delayed_by_retransmission()),
|
||||
rtp_timestamp(frame.Timestamp()),
|
||||
receive_time_ms(frame.ReceivedTime()) {}
|
||||
receive_time(frame.ReceivedTimestamp()) {}
|
||||
|
||||
const bool is_last_spatial_layer;
|
||||
const bool is_keyframe;
|
||||
@ -157,9 +157,15 @@ struct FrameMetadata {
|
||||
const VideoContentType contentType;
|
||||
const bool delayed_by_retransmission;
|
||||
const uint32_t rtp_timestamp;
|
||||
const int64_t receive_time_ms;
|
||||
const absl::optional<Timestamp> receive_time;
|
||||
};
|
||||
|
||||
Timestamp ReceiveTime(const EncodedFrame& frame) {
|
||||
absl::optional<Timestamp> ts = frame.ReceivedTimestamp();
|
||||
RTC_DCHECK(ts.has_value()) << "Received frame must have a timestamp set!";
|
||||
return *ts;
|
||||
}
|
||||
|
||||
// Encapsulates use of the new frame buffer for use in VideoReceiveStream. This
|
||||
// behaves the same as the FrameBuffer2Proxy but uses frame_buffer3 instead.
|
||||
// Responsibilities from frame_buffer2, like stats, jitter and frame timing
|
||||
@ -186,7 +192,6 @@ class FrameBuffer3Proxy : public FrameBufferProxy {
|
||||
timing_(timing),
|
||||
frame_decode_scheduler_(std::move(frame_decode_scheduler)),
|
||||
jitter_estimator_(clock_),
|
||||
inter_frame_delay_(clock_->TimeInMilliseconds()),
|
||||
buffer_(std::make_unique<FrameBuffer>(kMaxFramesBuffered,
|
||||
kMaxFramesHistory)),
|
||||
decode_timing_(clock_, timing_),
|
||||
@ -248,12 +253,10 @@ class FrameBuffer3Proxy : public FrameBufferProxy {
|
||||
if (complete_units < buffer_->GetTotalNumberOfContinuousTemporalUnits()) {
|
||||
stats_proxy_->OnCompleteFrame(metadata.is_keyframe, metadata.size,
|
||||
metadata.contentType);
|
||||
RTC_DCHECK_GE(metadata.receive_time_ms, 0)
|
||||
<< "Frame receive time must be positive for received frames, was "
|
||||
<< metadata.receive_time_ms << ".";
|
||||
if (!metadata.delayed_by_retransmission && metadata.receive_time_ms >= 0)
|
||||
RTC_DCHECK(metadata.receive_time) << "Frame receive time must be set!";
|
||||
if (!metadata.delayed_by_retransmission && metadata.receive_time)
|
||||
timing_->IncomingTimestamp(metadata.rtp_timestamp,
|
||||
Timestamp::Millis(metadata.receive_time_ms));
|
||||
*metadata.receive_time);
|
||||
MaybeScheduleFrameForRelease();
|
||||
}
|
||||
|
||||
@ -301,7 +304,7 @@ class FrameBuffer3Proxy : public FrameBufferProxy {
|
||||
bool superframe_delayed_by_retransmission = false;
|
||||
DataSize superframe_size = DataSize::Zero();
|
||||
const EncodedFrame& first_frame = *frames.front();
|
||||
int64_t receive_time_ms = first_frame.ReceivedTime();
|
||||
Timestamp receive_time = ReceiveTime(first_frame);
|
||||
|
||||
if (first_frame.is_keyframe())
|
||||
keyframe_required_ = false;
|
||||
@ -319,17 +322,15 @@ class FrameBuffer3Proxy : public FrameBufferProxy {
|
||||
|
||||
superframe_delayed_by_retransmission |=
|
||||
frame->delayed_by_retransmission();
|
||||
receive_time_ms = std::max(receive_time_ms, frame->ReceivedTime());
|
||||
receive_time = std::max(receive_time, ReceiveTime(*frame));
|
||||
superframe_size += DataSize::Bytes(frame->size());
|
||||
}
|
||||
|
||||
if (!superframe_delayed_by_retransmission) {
|
||||
int64_t frame_delay;
|
||||
|
||||
if (inter_frame_delay_.CalculateDelay(first_frame.Timestamp(),
|
||||
&frame_delay, receive_time_ms)) {
|
||||
jitter_estimator_.UpdateEstimate(TimeDelta::Millis(frame_delay),
|
||||
superframe_size);
|
||||
auto frame_delay = inter_frame_delay_.CalculateDelay(
|
||||
first_frame.Timestamp(), receive_time);
|
||||
if (frame_delay) {
|
||||
jitter_estimator_.UpdateEstimate(*frame_delay, superframe_size);
|
||||
}
|
||||
|
||||
float rtt_mult = protection_mode_ == kProtectionNackFEC ? 0.0 : 1.0;
|
||||
|
Reference in New Issue
Block a user