blob: d4b002c35e97072832bd910cbc444890120421f6 [file] [log] [blame]
/*
* 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 };
class Time {
public:
explicit Time(int64_t offset)
: kNtpJan1970(2208988800UL),
time_now_ms_(offset) {}
void NowNtp(uint32_t* ntp_secs, uint32_t* ntp_frac) const {
*ntp_secs = time_now_ms_ / 1000 + kNtpJan1970;
int64_t remainder = time_now_ms_ % 1000;
*ntp_frac = static_cast<uint32_t>(
static_cast<double>(remainder) / 1000.0 * pow(2.0, 32.0) + 0.5);
}
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);
}
virtual void TearDown() {
delete sync_;
delete send_time_;
delete receive_time_;
}
int DelayedAudio(int delay_ms,
int current_audio_delay_ms,
int* extra_audio_delay_ms,
int* total_video_delay_ms) {
StreamSynchronization::Measurements audio;
StreamSynchronization::Measurements video;
send_time_->NowNtp(&audio.received_ntp_secs, &audio.received_ntp_frac);
send_time_->NowNtp(&video.received_ntp_secs, &video.received_ntp_frac);
receive_time_->NowNtp(&video.rtcp_arrivaltime_secs,
&video.rtcp_arrivaltime_frac);
// Audio later than video.
receive_time_->IncreaseTimeMs(delay_ms);
receive_time_->NowNtp(&audio.rtcp_arrivaltime_secs,
&audio.rtcp_arrivaltime_frac);
return sync_->ComputeDelays(audio,
current_audio_delay_ms,
extra_audio_delay_ms,
video,
total_video_delay_ms);
}
int DelayedVideo(int delay_ms,
int current_audio_delay_ms,
int* extra_audio_delay_ms,
int* total_video_delay_ms) {
StreamSynchronization::Measurements audio;
StreamSynchronization::Measurements video;
send_time_->NowNtp(&audio.received_ntp_secs, &audio.received_ntp_frac);
send_time_->NowNtp(&video.received_ntp_secs, &video.received_ntp_frac);
receive_time_->NowNtp(&audio.rtcp_arrivaltime_secs,
&audio.rtcp_arrivaltime_frac);
// Video later than audio.
receive_time_->IncreaseTimeMs(delay_ms);
receive_time_->NowNtp(&video.rtcp_arrivaltime_secs,
&video.rtcp_arrivaltime_frac);
return sync_->ComputeDelays(audio,
current_audio_delay_ms,
extra_audio_delay_ms,
video,
total_video_delay_ms);
}
int DelayedAudioAndVideo(int audio_delay_ms,
int video_delay_ms,
int current_audio_delay_ms,
int* extra_audio_delay_ms,
int* total_video_delay_ms) {
StreamSynchronization::Measurements audio;
StreamSynchronization::Measurements video;
send_time_->NowNtp(&audio.received_ntp_secs, &audio.received_ntp_frac);
send_time_->NowNtp(&video.received_ntp_secs, &video.received_ntp_frac);
if (audio_delay_ms > video_delay_ms) {
// Audio later than video.
receive_time_->IncreaseTimeMs(video_delay_ms);
receive_time_->NowNtp(&video.rtcp_arrivaltime_secs,
&video.rtcp_arrivaltime_frac);
receive_time_->IncreaseTimeMs(audio_delay_ms - video_delay_ms);
receive_time_->NowNtp(&audio.rtcp_arrivaltime_secs,
&audio.rtcp_arrivaltime_frac);
} else {
// Video later than audio.
receive_time_->IncreaseTimeMs(audio_delay_ms);
receive_time_->NowNtp(&audio.rtcp_arrivaltime_secs,
&audio.rtcp_arrivaltime_frac);
receive_time_->IncreaseTimeMs(video_delay_ms - audio_delay_ms);
receive_time_->NowNtp(&video.rtcp_arrivaltime_secs,
&video.rtcp_arrivaltime_frac);
}
return sync_->ComputeDelays(audio,
current_audio_delay_ms,
extra_audio_delay_ms,
video,
total_video_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 = 0 };
enum { kReceiveTimeOffsetMs = 123456 };
StreamSynchronization* sync_;
Time* send_time_;
Time* receive_time_;
};
TEST_F(StreamSynchronizationTest, NoDelay) {
uint32_t current_audio_delay_ms = 0;
int delay_ms = 0;
int extra_audio_delay_ms = 0;
int total_video_delay_ms = 0;
EXPECT_EQ(0, DelayedAudio(delay_ms, 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_EQ(0, DelayedAudio(delay_ms, 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_EQ(0, DelayedAudio(delay_ms, 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_EQ(0, DelayedAudio(delay_ms, 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(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 current_extra_delay_ms = 0;
int total_video_delay_ms = 0;
EXPECT_EQ(0, DelayedVideo(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;
current_extra_delay_ms = extra_audio_delay_ms;
send_time_->IncreaseTimeMs(1000);
receive_time_->IncreaseTimeMs(800);
EXPECT_EQ(0, DelayedVideo(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_EQ(0, DelayedVideo(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_EQ(0, DelayedVideo(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_EQ(0, DelayedVideo(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 current_extra_delay_ms = 0;
int total_video_delay_ms = 0;
EXPECT_EQ(0, DelayedAudioAndVideo(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;
current_extra_delay_ms = extra_audio_delay_ms;
send_time_->IncreaseTimeMs(1000);
receive_time_->IncreaseTimeMs(800);
EXPECT_EQ(0, DelayedAudioAndVideo(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_EQ(0, DelayedAudioAndVideo(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_EQ(0, DelayedAudioAndVideo(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_EQ(0, DelayedAudioAndVideo(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) {
int current_audio_delay_ms = 0;
int audio_delay_ms = 300;
int video_delay_ms = 100;
int extra_audio_delay_ms = 0;
int current_extra_delay_ms = 0;
int total_video_delay_ms = 0;
EXPECT_EQ(0, DelayedAudioAndVideo(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;
current_extra_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_EQ(0, DelayedAudioAndVideo(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;
current_extra_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_EQ(0, DelayedAudioAndVideo(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);
current_extra_delay_ms = 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_EQ(0, DelayedAudioAndVideo(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);
current_extra_delay_ms = 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_EQ(0, DelayedAudioAndVideo(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);
}
} // namespace webrtc