| /* |
| * Copyright (c) 2012 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 <math.h> |
| #include <algorithm> |
| |
| #include "gtest/gtest.h" |
| #include "video_engine/stream_synchronization.h" |
| |
| namespace webrtc { |
| |
| // These correspond to the same constants defined in vie_sync_module.cc. |
| enum { kMaxVideoDiffMs = 80 }; |
| enum { kMaxAudioDiffMs = 80 }; |
| enum { kMaxDelay = 1500 }; |
| |
| // Test constants. |
| enum { kDefaultAudioFrequency = 8000 }; |
| enum { kDefaultVideoFrequency = 90000 }; |
| const double kNtpFracPerMs = 4.294967296E6; |
| |
| class Time { |
| public: |
| explicit Time(int64_t offset) |
| : kNtpJan1970(2208988800UL), |
| time_now_ms_(offset) {} |
| |
| synchronization::RtcpMeasurement GenerateRtcp(int frequency, |
| uint32_t offset) const { |
| synchronization::RtcpMeasurement rtcp; |
| NowNtp(&rtcp.ntp_secs, &rtcp.ntp_frac); |
| rtcp.rtp_timestamp = NowRtp(frequency, offset); |
| return rtcp; |
| } |
| |
| void NowNtp(uint32_t* ntp_secs, uint32_t* ntp_frac) const { |
| *ntp_secs = time_now_ms_ / 1000 + kNtpJan1970; |
| int64_t remainder_ms = time_now_ms_ % 1000; |
| *ntp_frac = static_cast<uint32_t>( |
| static_cast<double>(remainder_ms) * kNtpFracPerMs + 0.5); |
| } |
| |
| uint32_t NowRtp(int frequency, uint32_t offset) const { |
| return frequency * time_now_ms_ / 1000 + offset; |
| } |
| |
| void IncreaseTimeMs(int64_t inc) { |
| time_now_ms_ += inc; |
| } |
| |
| int64_t time_now_ms() const { |
| return time_now_ms_; |
| } |
| |
| private: |
| // January 1970, in NTP seconds. |
| const uint32_t kNtpJan1970; |
| int64_t time_now_ms_; |
| }; |
| |
| class StreamSynchronizationTest : public ::testing::Test { |
| protected: |
| virtual void SetUp() { |
| sync_ = new StreamSynchronization(0, 0); |
| send_time_ = new Time(kSendTimeOffsetMs); |
| receive_time_ = new Time(kReceiveTimeOffsetMs); |
| audio_clock_drift_ = 1.0; |
| video_clock_drift_ = 1.0; |
| } |
| |
| virtual void TearDown() { |
| delete sync_; |
| delete send_time_; |
| delete receive_time_; |
| } |
| |
| // Generates the necessary RTCP measurements and RTP timestamps and computes |
| // the audio and video delays needed to get the two streams in sync. |
| // |audio_delay_ms| and |video_delay_ms| are the number of milliseconds after |
| // capture which the frames are rendered. |
| // |current_audio_delay_ms| is the number of milliseconds which audio is |
| // currently being delayed by the receiver. |
| bool DelayedStreams(int audio_delay_ms, |
| int video_delay_ms, |
| int current_audio_delay_ms, |
| int* extra_audio_delay_ms, |
| int* total_video_delay_ms) { |
| int audio_frequency = static_cast<int>(kDefaultAudioFrequency * |
| audio_clock_drift_ + 0.5); |
| int audio_offset = 0; |
| int video_frequency = static_cast<int>(kDefaultVideoFrequency * |
| video_clock_drift_ + 0.5); |
| int video_offset = 0; |
| StreamSynchronization::Measurements audio; |
| StreamSynchronization::Measurements video; |
| // Generate NTP/RTP timestamp pair for both streams corresponding to RTCP. |
| audio.rtcp.push_front(send_time_->GenerateRtcp(audio_frequency, |
| audio_offset)); |
| send_time_->IncreaseTimeMs(100); |
| receive_time_->IncreaseTimeMs(100); |
| video.rtcp.push_front(send_time_->GenerateRtcp(video_frequency, |
| video_offset)); |
| send_time_->IncreaseTimeMs(900); |
| receive_time_->IncreaseTimeMs(900); |
| audio.rtcp.push_front(send_time_->GenerateRtcp(audio_frequency, |
| audio_offset)); |
| send_time_->IncreaseTimeMs(100); |
| receive_time_->IncreaseTimeMs(100); |
| video.rtcp.push_front(send_time_->GenerateRtcp(video_frequency, |
| video_offset)); |
| send_time_->IncreaseTimeMs(900); |
| receive_time_->IncreaseTimeMs(900); |
| |
| // Capture an audio and a video frame at the same time. |
| audio.latest_timestamp = send_time_->NowRtp(audio_frequency, |
| audio_offset); |
| video.latest_timestamp = send_time_->NowRtp(video_frequency, |
| video_offset); |
| |
| if (audio_delay_ms > video_delay_ms) { |
| // Audio later than video. |
| receive_time_->IncreaseTimeMs(video_delay_ms); |
| video.latest_receive_time_ms = receive_time_->time_now_ms(); |
| receive_time_->IncreaseTimeMs(audio_delay_ms - video_delay_ms); |
| audio.latest_receive_time_ms = receive_time_->time_now_ms(); |
| } else { |
| // Video later than audio. |
| receive_time_->IncreaseTimeMs(audio_delay_ms); |
| audio.latest_receive_time_ms = receive_time_->time_now_ms(); |
| receive_time_->IncreaseTimeMs(video_delay_ms - audio_delay_ms); |
| video.latest_receive_time_ms = receive_time_->time_now_ms(); |
| } |
| int relative_delay_ms; |
| StreamSynchronization::ComputeRelativeDelay(audio, video, |
| &relative_delay_ms); |
| EXPECT_EQ(video_delay_ms - audio_delay_ms, relative_delay_ms); |
| return sync_->ComputeDelays(relative_delay_ms, |
| current_audio_delay_ms, |
| extra_audio_delay_ms, |
| total_video_delay_ms); |
| } |
| |
| // Simulate audio playback 300 ms after capture and video rendering 100 ms |
| // after capture. Verify that the correct extra delays are calculated for |
| // audio and video, and that they change correctly when we simulate that |
| // NetEQ or the VCM adds more delay to the streams. |
| // TODO(holmer): This is currently wrong! We should simply change |
| // audio_delay_ms or video_delay_ms since those now include VCM and NetEQ |
| // delays. |
| void BothDelayedAudioLaterTest() { |
| int current_audio_delay_ms = 0; |
| int audio_delay_ms = 300; |
| int video_delay_ms = 100; |
| int extra_audio_delay_ms = 0; |
| int total_video_delay_ms = 0; |
| |
| EXPECT_TRUE(DelayedStreams(audio_delay_ms, |
| video_delay_ms, |
| current_audio_delay_ms, |
| &extra_audio_delay_ms, |
| &total_video_delay_ms)); |
| EXPECT_EQ(kMaxVideoDiffMs, total_video_delay_ms); |
| EXPECT_EQ(0, extra_audio_delay_ms); |
| current_audio_delay_ms = extra_audio_delay_ms; |
| |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(1000 - std::max(audio_delay_ms, |
| video_delay_ms)); |
| // Simulate 0 minimum delay in the VCM. |
| total_video_delay_ms = 0; |
| EXPECT_TRUE(DelayedStreams(audio_delay_ms, |
| video_delay_ms, |
| current_audio_delay_ms, |
| &extra_audio_delay_ms, |
| &total_video_delay_ms)); |
| EXPECT_EQ(2 * kMaxVideoDiffMs, total_video_delay_ms); |
| EXPECT_EQ(0, extra_audio_delay_ms); |
| current_audio_delay_ms = extra_audio_delay_ms; |
| |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(1000 - std::max(audio_delay_ms, |
| video_delay_ms)); |
| // Simulate 0 minimum delay in the VCM. |
| total_video_delay_ms = 0; |
| EXPECT_TRUE(DelayedStreams(audio_delay_ms, |
| video_delay_ms, |
| current_audio_delay_ms, |
| &extra_audio_delay_ms, |
| &total_video_delay_ms)); |
| EXPECT_EQ(audio_delay_ms - video_delay_ms, total_video_delay_ms); |
| EXPECT_EQ(0, extra_audio_delay_ms); |
| |
| // Simulate that NetEQ introduces some audio delay. |
| current_audio_delay_ms = 50; |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(1000 - std::max(audio_delay_ms, |
| video_delay_ms)); |
| // Simulate 0 minimum delay in the VCM. |
| total_video_delay_ms = 0; |
| EXPECT_TRUE(DelayedStreams(audio_delay_ms, |
| video_delay_ms, |
| current_audio_delay_ms, |
| &extra_audio_delay_ms, |
| &total_video_delay_ms)); |
| EXPECT_EQ(audio_delay_ms - video_delay_ms + current_audio_delay_ms, |
| total_video_delay_ms); |
| EXPECT_EQ(0, extra_audio_delay_ms); |
| |
| // Simulate that NetEQ reduces its delay. |
| current_audio_delay_ms = 10; |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(1000 - std::max(audio_delay_ms, |
| video_delay_ms)); |
| // Simulate 0 minimum delay in the VCM. |
| total_video_delay_ms = 0; |
| EXPECT_TRUE(DelayedStreams(audio_delay_ms, |
| video_delay_ms, |
| current_audio_delay_ms, |
| &extra_audio_delay_ms, |
| &total_video_delay_ms)); |
| EXPECT_EQ(audio_delay_ms - video_delay_ms + current_audio_delay_ms, |
| total_video_delay_ms); |
| EXPECT_EQ(0, extra_audio_delay_ms); |
| } |
| |
| int MaxAudioDelayIncrease(int current_audio_delay_ms, int delay_ms) { |
| return std::min((delay_ms - current_audio_delay_ms) / 2, |
| static_cast<int>(kMaxAudioDiffMs)); |
| } |
| |
| int MaxAudioDelayDecrease(int current_audio_delay_ms, int delay_ms) { |
| return std::max((delay_ms - current_audio_delay_ms) / 2, -kMaxAudioDiffMs); |
| } |
| |
| enum { kSendTimeOffsetMs = 98765 }; |
| enum { kReceiveTimeOffsetMs = 43210 }; |
| |
| StreamSynchronization* sync_; |
| Time* send_time_; // The simulated clock at the sender. |
| Time* receive_time_; // The simulated clock at the receiver. |
| double audio_clock_drift_; |
| double video_clock_drift_; |
| }; |
| |
| TEST_F(StreamSynchronizationTest, NoDelay) { |
| uint32_t current_audio_delay_ms = 0; |
| int extra_audio_delay_ms = 0; |
| int total_video_delay_ms = 0; |
| |
| EXPECT_TRUE(DelayedStreams(0, 0, current_audio_delay_ms, |
| &extra_audio_delay_ms, &total_video_delay_ms)); |
| EXPECT_EQ(0, extra_audio_delay_ms); |
| EXPECT_EQ(0, total_video_delay_ms); |
| } |
| |
| TEST_F(StreamSynchronizationTest, VideoDelay) { |
| uint32_t current_audio_delay_ms = 0; |
| int delay_ms = 200; |
| int extra_audio_delay_ms = 0; |
| int total_video_delay_ms = 0; |
| |
| EXPECT_TRUE(DelayedStreams(delay_ms, 0, current_audio_delay_ms, |
| &extra_audio_delay_ms, &total_video_delay_ms)); |
| EXPECT_EQ(0, extra_audio_delay_ms); |
| // The video delay is not allowed to change more than this in 1 second. |
| EXPECT_EQ(kMaxVideoDiffMs, total_video_delay_ms); |
| |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(800); |
| // Simulate 0 minimum delay in the VCM. |
| total_video_delay_ms = 0; |
| EXPECT_TRUE(DelayedStreams(delay_ms, 0, current_audio_delay_ms, |
| &extra_audio_delay_ms, &total_video_delay_ms)); |
| EXPECT_EQ(0, extra_audio_delay_ms); |
| // The video delay is not allowed to change more than this in 1 second. |
| EXPECT_EQ(2*kMaxVideoDiffMs, total_video_delay_ms); |
| |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(800); |
| // Simulate 0 minimum delay in the VCM. |
| total_video_delay_ms = 0; |
| EXPECT_TRUE(DelayedStreams(delay_ms, 0, current_audio_delay_ms, |
| &extra_audio_delay_ms, &total_video_delay_ms)); |
| EXPECT_EQ(0, extra_audio_delay_ms); |
| // Enough time should have elapsed for the requested total video delay to be |
| // equal to the relative delay between audio and video, i.e., we are in sync. |
| EXPECT_EQ(delay_ms, total_video_delay_ms); |
| } |
| |
| TEST_F(StreamSynchronizationTest, AudioDelay) { |
| int current_audio_delay_ms = 0; |
| int delay_ms = 200; |
| int extra_audio_delay_ms = 0; |
| int total_video_delay_ms = 0; |
| |
| EXPECT_TRUE(DelayedStreams(0, delay_ms, current_audio_delay_ms, |
| &extra_audio_delay_ms, &total_video_delay_ms)); |
| EXPECT_EQ(0, total_video_delay_ms); |
| // The audio delay is not allowed to change more than this in 1 second. |
| EXPECT_EQ(kMaxAudioDiffMs, extra_audio_delay_ms); |
| current_audio_delay_ms = extra_audio_delay_ms; |
| int current_extra_delay_ms = extra_audio_delay_ms; |
| |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(800); |
| EXPECT_TRUE(DelayedStreams(0, delay_ms, current_audio_delay_ms, |
| &extra_audio_delay_ms, &total_video_delay_ms)); |
| EXPECT_EQ(0, total_video_delay_ms); |
| // The audio delay is not allowed to change more than the half of the required |
| // change in delay. |
| EXPECT_EQ(current_extra_delay_ms + |
| MaxAudioDelayIncrease(current_audio_delay_ms, delay_ms), |
| extra_audio_delay_ms); |
| current_audio_delay_ms = extra_audio_delay_ms; |
| current_extra_delay_ms = extra_audio_delay_ms; |
| |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(800); |
| EXPECT_TRUE(DelayedStreams(0, delay_ms, current_audio_delay_ms, |
| &extra_audio_delay_ms, &total_video_delay_ms)); |
| EXPECT_EQ(0, total_video_delay_ms); |
| // The audio delay is not allowed to change more than the half of the required |
| // change in delay. |
| EXPECT_EQ(current_extra_delay_ms + |
| MaxAudioDelayIncrease(current_audio_delay_ms, delay_ms), |
| extra_audio_delay_ms); |
| current_extra_delay_ms = extra_audio_delay_ms; |
| |
| // Simulate that NetEQ for some reason reduced the delay. |
| current_audio_delay_ms = 170; |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(800); |
| EXPECT_TRUE(DelayedStreams(0, delay_ms, current_audio_delay_ms, |
| &extra_audio_delay_ms, &total_video_delay_ms)); |
| EXPECT_EQ(0, total_video_delay_ms); |
| // Since we only can ask NetEQ for a certain amount of extra delay, and |
| // we only measure the total NetEQ delay, we will ask for additional delay |
| // here to try to |
| EXPECT_EQ(current_extra_delay_ms + |
| MaxAudioDelayIncrease(current_audio_delay_ms, delay_ms), |
| extra_audio_delay_ms); |
| current_extra_delay_ms = extra_audio_delay_ms; |
| |
| // Simulate that NetEQ for some reason significantly increased the delay. |
| current_audio_delay_ms = 250; |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(800); |
| EXPECT_TRUE(DelayedStreams(0, delay_ms, current_audio_delay_ms, |
| &extra_audio_delay_ms, &total_video_delay_ms)); |
| EXPECT_EQ(0, total_video_delay_ms); |
| // The audio delay is not allowed to change more than the half of the required |
| // change in delay. |
| EXPECT_EQ(current_extra_delay_ms + |
| MaxAudioDelayDecrease(current_audio_delay_ms, delay_ms), |
| extra_audio_delay_ms); |
| } |
| |
| TEST_F(StreamSynchronizationTest, BothDelayedVideoLater) { |
| int current_audio_delay_ms = 0; |
| int audio_delay_ms = 100; |
| int video_delay_ms = 300; |
| int extra_audio_delay_ms = 0; |
| int total_video_delay_ms = 0; |
| |
| EXPECT_TRUE(DelayedStreams(audio_delay_ms, |
| video_delay_ms, |
| current_audio_delay_ms, |
| &extra_audio_delay_ms, |
| &total_video_delay_ms)); |
| EXPECT_EQ(0, total_video_delay_ms); |
| // The audio delay is not allowed to change more than this in 1 second. |
| EXPECT_EQ(kMaxAudioDiffMs, extra_audio_delay_ms); |
| current_audio_delay_ms = extra_audio_delay_ms; |
| int current_extra_delay_ms = extra_audio_delay_ms; |
| |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(800); |
| EXPECT_TRUE(DelayedStreams(audio_delay_ms, |
| video_delay_ms, |
| current_audio_delay_ms, |
| &extra_audio_delay_ms, |
| &total_video_delay_ms)); |
| EXPECT_EQ(0, total_video_delay_ms); |
| // The audio delay is not allowed to change more than the half of the required |
| // change in delay. |
| EXPECT_EQ(current_extra_delay_ms + MaxAudioDelayIncrease( |
| current_audio_delay_ms, video_delay_ms - audio_delay_ms), |
| extra_audio_delay_ms); |
| current_audio_delay_ms = extra_audio_delay_ms; |
| current_extra_delay_ms = extra_audio_delay_ms; |
| |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(800); |
| EXPECT_TRUE(DelayedStreams(audio_delay_ms, |
| video_delay_ms, |
| current_audio_delay_ms, |
| &extra_audio_delay_ms, |
| &total_video_delay_ms)); |
| EXPECT_EQ(0, total_video_delay_ms); |
| // The audio delay is not allowed to change more than the half of the required |
| // change in delay. |
| EXPECT_EQ(current_extra_delay_ms + MaxAudioDelayIncrease( |
| current_audio_delay_ms, video_delay_ms - audio_delay_ms), |
| extra_audio_delay_ms); |
| current_extra_delay_ms = extra_audio_delay_ms; |
| |
| // Simulate that NetEQ for some reason reduced the delay. |
| current_audio_delay_ms = 170; |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(800); |
| EXPECT_TRUE(DelayedStreams(audio_delay_ms, |
| video_delay_ms, |
| current_audio_delay_ms, |
| &extra_audio_delay_ms, |
| &total_video_delay_ms)); |
| EXPECT_EQ(0, total_video_delay_ms); |
| // Since we only can ask NetEQ for a certain amount of extra delay, and |
| // we only measure the total NetEQ delay, we will ask for additional delay |
| // here to try to stay in sync. |
| EXPECT_EQ(current_extra_delay_ms + MaxAudioDelayIncrease( |
| current_audio_delay_ms, video_delay_ms - audio_delay_ms), |
| extra_audio_delay_ms); |
| current_extra_delay_ms = extra_audio_delay_ms; |
| |
| // Simulate that NetEQ for some reason significantly increased the delay. |
| current_audio_delay_ms = 250; |
| send_time_->IncreaseTimeMs(1000); |
| receive_time_->IncreaseTimeMs(800); |
| EXPECT_TRUE(DelayedStreams(audio_delay_ms, |
| video_delay_ms, |
| current_audio_delay_ms, |
| &extra_audio_delay_ms, |
| &total_video_delay_ms)); |
| EXPECT_EQ(0, total_video_delay_ms); |
| // The audio delay is not allowed to change more than the half of the required |
| // change in delay. |
| EXPECT_EQ(current_extra_delay_ms + MaxAudioDelayIncrease( |
| current_audio_delay_ms, video_delay_ms - audio_delay_ms), |
| extra_audio_delay_ms); |
| } |
| |
| TEST_F(StreamSynchronizationTest, BothDelayedAudioLater) { |
| BothDelayedAudioLaterTest(); |
| } |
| |
| TEST_F(StreamSynchronizationTest, BothDelayedAudioClockDrift) { |
| audio_clock_drift_ = 1.05; |
| BothDelayedAudioLaterTest(); |
| } |
| |
| TEST_F(StreamSynchronizationTest, BothDelayedVideoClockDrift) { |
| video_clock_drift_ = 1.05; |
| BothDelayedAudioLaterTest(); |
| } |
| |
| TEST(WrapAroundTests, NoWrap) { |
| EXPECT_EQ(0, synchronization::CheckForWrapArounds(0xFFFFFFFF, 0xFFFFFFFE)); |
| EXPECT_EQ(0, synchronization::CheckForWrapArounds(1, 0)); |
| EXPECT_EQ(0, synchronization::CheckForWrapArounds(0x00010000, 0x0000FFFF)); |
| } |
| |
| TEST(WrapAroundTests, ForwardWrap) { |
| EXPECT_EQ(1, synchronization::CheckForWrapArounds(0, 0xFFFFFFFF)); |
| EXPECT_EQ(1, synchronization::CheckForWrapArounds(0, 0xFFFF0000)); |
| EXPECT_EQ(1, synchronization::CheckForWrapArounds(0x0000FFFF, 0xFFFFFFFF)); |
| EXPECT_EQ(1, synchronization::CheckForWrapArounds(0x0000FFFF, 0xFFFF0000)); |
| } |
| |
| TEST(WrapAroundTests, BackwardWrap) { |
| EXPECT_EQ(-1, synchronization::CheckForWrapArounds(0xFFFFFFFF, 0)); |
| EXPECT_EQ(-1, synchronization::CheckForWrapArounds(0xFFFF0000, 0)); |
| EXPECT_EQ(-1, synchronization::CheckForWrapArounds(0xFFFFFFFF, 0x0000FFFF)); |
| EXPECT_EQ(-1, synchronization::CheckForWrapArounds(0xFFFF0000, 0x0000FFFF)); |
| } |
| |
| TEST(WrapAroundTests, OldRtcpWrapped) { |
| synchronization::RtcpList rtcp; |
| uint32_t ntp_sec = 0; |
| uint32_t ntp_frac = 0; |
| uint32_t timestamp = 0; |
| const uint32_t kOneMsInNtpFrac = 4294967; |
| const uint32_t kTimestampTicksPerMs = 90; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp -= kTimestampTicksPerMs; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp -= kTimestampTicksPerMs; |
| int64_t timestamp_in_ms = -1; |
| // This expected to fail since it's highly unlikely that the older RTCP |
| // has a much smaller RTP timestamp than the newer. |
| EXPECT_FALSE(synchronization::RtpToNtpMs(timestamp, rtcp, ×tamp_in_ms)); |
| } |
| |
| TEST(WrapAroundTests, NewRtcpWrapped) { |
| synchronization::RtcpList rtcp; |
| uint32_t ntp_sec = 0; |
| uint32_t ntp_frac = 0; |
| uint32_t timestamp = 0xFFFFFFFF; |
| const uint32_t kOneMsInNtpFrac = 4294967; |
| const uint32_t kTimestampTicksPerMs = 90; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp += kTimestampTicksPerMs; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| int64_t timestamp_in_ms = -1; |
| EXPECT_TRUE(synchronization::RtpToNtpMs(rtcp.back().rtp_timestamp, rtcp, |
| ×tamp_in_ms)); |
| // Since this RTP packet has the same timestamp as the RTCP packet constructed |
| // at time 0 it should be mapped to 0 as well. |
| EXPECT_EQ(0, timestamp_in_ms); |
| } |
| |
| TEST(WrapAroundTests, RtpWrapped) { |
| const uint32_t kOneMsInNtpFrac = 4294967; |
| const uint32_t kTimestampTicksPerMs = 90; |
| synchronization::RtcpList rtcp; |
| uint32_t ntp_sec = 0; |
| uint32_t ntp_frac = 0; |
| uint32_t timestamp = 0xFFFFFFFF - 2 * kTimestampTicksPerMs; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp += kTimestampTicksPerMs; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp += kTimestampTicksPerMs; |
| int64_t timestamp_in_ms = -1; |
| EXPECT_TRUE(synchronization::RtpToNtpMs(timestamp, rtcp, |
| ×tamp_in_ms)); |
| // Since this RTP packet has the same timestamp as the RTCP packet constructed |
| // at time 0 it should be mapped to 0 as well. |
| EXPECT_EQ(2, timestamp_in_ms); |
| } |
| |
| TEST(WrapAroundTests, OldRtp_RtcpsWrapped) { |
| const uint32_t kOneMsInNtpFrac = 4294967; |
| const uint32_t kTimestampTicksPerMs = 90; |
| synchronization::RtcpList rtcp; |
| uint32_t ntp_sec = 0; |
| uint32_t ntp_frac = 0; |
| uint32_t timestamp = 0; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp += kTimestampTicksPerMs; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp -= 2*kTimestampTicksPerMs; |
| int64_t timestamp_in_ms = -1; |
| EXPECT_FALSE(synchronization::RtpToNtpMs(timestamp, rtcp, |
| ×tamp_in_ms)); |
| } |
| |
| TEST(WrapAroundTests, OldRtp_NewRtcpWrapped) { |
| const uint32_t kOneMsInNtpFrac = 4294967; |
| const uint32_t kTimestampTicksPerMs = 90; |
| synchronization::RtcpList rtcp; |
| uint32_t ntp_sec = 0; |
| uint32_t ntp_frac = 0; |
| uint32_t timestamp = 0xFFFFFFFF; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp += kTimestampTicksPerMs; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp -= kTimestampTicksPerMs; |
| int64_t timestamp_in_ms = -1; |
| EXPECT_TRUE(synchronization::RtpToNtpMs(timestamp, rtcp, |
| ×tamp_in_ms)); |
| // Constructed at the same time as the first RTCP and should therefore be |
| // mapped to zero. |
| EXPECT_EQ(0, timestamp_in_ms); |
| } |
| |
| TEST(WrapAroundTests, OldRtp_OldRtcpWrapped) { |
| const uint32_t kOneMsInNtpFrac = 4294967; |
| const uint32_t kTimestampTicksPerMs = 90; |
| synchronization::RtcpList rtcp; |
| uint32_t ntp_sec = 0; |
| uint32_t ntp_frac = 0; |
| uint32_t timestamp = 0; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp -= kTimestampTicksPerMs; |
| rtcp.push_front(synchronization::RtcpMeasurement(ntp_sec, ntp_frac, |
| timestamp)); |
| ntp_frac += kOneMsInNtpFrac; |
| timestamp += 2*kTimestampTicksPerMs; |
| int64_t timestamp_in_ms = -1; |
| EXPECT_FALSE(synchronization::RtpToNtpMs(timestamp, rtcp, |
| ×tamp_in_ms)); |
| } |
| } // namespace webrtc |