Index: webrtc/modules/video_coding/main/source/receiver_unittest.cc |
diff --git a/webrtc/modules/video_coding/main/source/receiver_unittest.cc b/webrtc/modules/video_coding/main/source/receiver_unittest.cc |
index 2e16984720f4c195b294e4f361396bc4fb8e3a0f..dc63e8100298f18a97cf89bcdb4f2079de252298 100644 |
--- a/webrtc/modules/video_coding/main/source/receiver_unittest.cc |
+++ b/webrtc/modules/video_coding/main/source/receiver_unittest.cc |
@@ -10,8 +10,10 @@ |
#include <string.h> |
#include <list> |
+#include <queue> |
#include "testing/gtest/include/gtest/gtest.h" |
+#include "webrtc/base/checks.h" |
#include "webrtc/modules/video_coding/main/source/packet.h" |
#include "webrtc/modules/video_coding/main/source/receiver.h" |
#include "webrtc/modules/video_coding/main/source/test/stream_generator.h" |
@@ -31,8 +33,9 @@ class TestVCMReceiver : public ::testing::Test { |
: clock_(new SimulatedClock(0)), |
timing_(clock_.get()), |
receiver_(&timing_, clock_.get(), &event_factory_) { |
- stream_generator_.reset( |
- new StreamGenerator(0, clock_->TimeInMilliseconds())); |
+ |
+ stream_generator_.reset(new |
+ StreamGenerator(0, clock_->TimeInMilliseconds())); |
} |
virtual void SetUp() { |
@@ -314,4 +317,212 @@ TEST_F(TestVCMReceiver, NonDecodableDuration_KeyFrameAfterIncompleteFrames) { |
std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame); |
EXPECT_FALSE(request_key_frame); |
} |
+ |
+// A simulated clock, when time elapses, will insert frames into the jitter |
+// buffer, based on initial settings. |
+class SimulatedClockWithFrames : public SimulatedClock { |
+ public: |
+ SimulatedClockWithFrames(StreamGenerator* stream_generator, |
+ VCMReceiver* receiver) |
+ : SimulatedClock(0), |
+ stream_generator_(stream_generator), |
+ receiver_(receiver) {} |
+ virtual ~SimulatedClockWithFrames() {} |
+ |
+ // If |stop_on_frame| is true and next frame arrives between now and |
+ // now+|milliseconds|, the clock will be advanced to the arrival time of next |
+ // frame. |
+ // Otherwise, the clock will be advanced by |milliseconds|. |
+ // |
+ // For both cases, a frame will be inserted into the jitter buffer at the |
+ // instant when the clock time is timestamps_.front().arrive_time. |
+ // |
+ // Return true if some frame arrives between now and now+|milliseconds|. |
+ bool AdvanceTimeMilliseconds(int64_t milliseconds, bool stop_on_frame) { |
+ return AdvanceTimeMicroseconds(milliseconds * 1000, stop_on_frame); |
+ }; |
+ |
+ bool AdvanceTimeMicroseconds(int64_t microseconds, bool stop_on_frame) { |
+ int64_t start_time = TimeInMicroseconds(); |
+ int64_t end_time = start_time + microseconds; |
+ bool frame_injected = false; |
+ while (!timestamps_.empty() && |
+ timestamps_.front().arrive_time <= end_time) { |
+ DCHECK(timestamps_.front().arrive_time >= start_time); |
+ |
+ SimulatedClock::AdvanceTimeMicroseconds(timestamps_.front().arrive_time - |
+ TimeInMicroseconds()); |
+ GenerateAndInsertFrame((timestamps_.front().render_time + 500) / 1000); |
+ timestamps_.pop(); |
+ frame_injected = true; |
+ |
+ if (stop_on_frame) |
+ return frame_injected; |
+ } |
+ |
+ if (TimeInMicroseconds() < end_time) { |
+ SimulatedClock::AdvanceTimeMicroseconds(end_time - TimeInMicroseconds()); |
+ } |
+ return frame_injected; |
+ }; |
+ |
+ // Input timestamps are in unit Milliseconds. |
+ // And |arrive_timestamps| must be positive and in increasing order. |
+ // |arrive_timestamps| determine when we are going to insert frames into the |
+ // jitter buffer. |
+ // |render_timestamps| are the timestamps on the frame. |
+ void SetFrames(const int64_t* arrive_timestamps, |
+ const int64_t* render_timestamps, |
+ size_t size) { |
+ int64_t previous_arrive_timestamp = 0; |
+ for (size_t i = 0; i < size; i++) { |
+ CHECK(arrive_timestamps[i] >= previous_arrive_timestamp); |
+ timestamps_.push(TimestampPair(arrive_timestamps[i] * 1000, |
+ render_timestamps[i] * 1000)); |
+ previous_arrive_timestamp = arrive_timestamps[i]; |
+ } |
+ } |
+ |
+ private: |
+ struct TimestampPair { |
+ TimestampPair(int64_t arrive_timestamp, int64_t render_timestamp) |
+ : arrive_time(arrive_timestamp), render_time(render_timestamp) {} |
+ |
+ int64_t arrive_time; |
+ int64_t render_time; |
+ }; |
+ |
+ void GenerateAndInsertFrame(int64_t render_timestamp_ms) { |
+ VCMPacket packet; |
+ stream_generator_->GenerateFrame(FrameType::kVideoFrameKey, |
+ 1, // media packets |
+ 0, // empty packets |
+ render_timestamp_ms); |
+ |
+ bool packet_available = stream_generator_->PopPacket(&packet, 0); |
+ EXPECT_TRUE(packet_available); |
+ if (!packet_available) |
+ return; // Return here to avoid crashes below. |
+ receiver_->InsertPacket(packet, 640, 480); |
+ } |
+ |
+ std::queue<TimestampPair> timestamps_; |
+ StreamGenerator* stream_generator_; |
+ VCMReceiver* receiver_; |
+}; |
+ |
+// Use a SimulatedClockWithFrames |
+// Wait call will do either of these: |
+// 1. If |stop_on_frame| is true, the clock will be turned to the exact instant |
+// that the first frame comes and the frame will be inserted into the jitter |
+// buffer, or the clock will be turned to now + |max_time| if no frame comes in |
+// the window. |
+// 2. If |stop_on_frame| is false, the clock will be turn to now + |max_time|, |
+// and all the frames arriving between now and now + |max_time| will be |
+// inserted into the jitter buffer. |
+// |
+// This is used to simulate the JitterBuffer getting packets from internet as |
+// time elapses. |
+ |
+class FrameInjectEvent : public EventWrapper { |
+ public: |
+ FrameInjectEvent(SimulatedClockWithFrames* clock, bool stop_on_frame) |
+ : clock_(clock), stop_on_frame_(stop_on_frame) {} |
+ |
+ bool Set() override { return true; } |
+ |
+ EventTypeWrapper Wait(unsigned long max_time) override { |
+ if (clock_->AdvanceTimeMilliseconds(max_time, stop_on_frame_) && |
+ stop_on_frame_) { |
+ return EventTypeWrapper::kEventSignaled; |
+ } else { |
+ return EventTypeWrapper::kEventTimeout; |
+ } |
+ } |
+ |
+ private: |
+ SimulatedClockWithFrames* clock_; |
+ bool stop_on_frame_; |
+}; |
+ |
+class VCMReceiverTimingTest : public ::testing::Test { |
+ protected: |
+ |
+ VCMReceiverTimingTest() |
+ |
+ : clock_(&stream_generator_, &receiver_), |
+ stream_generator_(0, clock_.TimeInMilliseconds()), |
+ timing_(&clock_), |
+ receiver_( |
+ &timing_, |
+ &clock_, |
+ rtc::scoped_ptr<EventWrapper>(new FrameInjectEvent(&clock_, false)), |
+ rtc::scoped_ptr<EventWrapper>( |
+ new FrameInjectEvent(&clock_, true))) {} |
+ |
+ |
+ virtual void SetUp() { receiver_.Reset(); } |
+ |
+ SimulatedClockWithFrames clock_; |
+ StreamGenerator stream_generator_; |
+ VCMTiming timing_; |
+ VCMReceiver receiver_; |
+}; |
+ |
+// Test whether VCMReceiver::FrameForDecoding handles parameter |
+// |max_wait_time_ms| correctly: |
+// 1. The function execution should never take more than |max_wait_time_ms|. |
+// 2. If the function exit before now + |max_wait_time_ms|, a frame must be |
+// returned. |
+TEST_F(VCMReceiverTimingTest, FrameForDecoding) { |
+ const size_t kNumFrames = 100; |
+ const int kFramePeriod = 40; |
+ int64_t arrive_timestamps[kNumFrames]; |
+ int64_t render_timestamps[kNumFrames]; |
+ int64_t next_render_time; |
+ |
+ // Construct test samples. |
+ // render_timestamps are the timestamps stored in the Frame; |
+ // arrive_timestamps controls when the Frame packet got received. |
+ for (size_t i = 0; i < kNumFrames; i++) { |
+ // Preset frame rate to 25Hz. |
+ // But we add a reasonable deviation to arrive_timestamps to mimic Internet |
+ // fluctuation. |
+ arrive_timestamps[i] = |
+ (i + 1) * kFramePeriod + (i % 10) * ((i % 2) ? 1 : -1); |
+ render_timestamps[i] = (i + 1) * kFramePeriod; |
+ } |
+ |
+ clock_.SetFrames(arrive_timestamps, render_timestamps, kNumFrames); |
+ |
+ // Record how many frames we finally get out of the receiver. |
+ size_t num_frames_return = 0; |
+ |
+ const int64_t kMaxWaitTime = 30; |
+ |
+ // Ideally, we should get all frames that we input in InitializeFrames. |
+ // In the case that FrameForDecoding kills frames by error, we rely on the |
+ // build bot to kill the test. |
+ while (num_frames_return < kNumFrames) { |
+ int64_t start_time = clock_.TimeInMilliseconds(); |
+ VCMEncodedFrame* frame = |
+ receiver_.FrameForDecoding(kMaxWaitTime, next_render_time, false); |
+ int64_t end_time = clock_.TimeInMilliseconds(); |
+ |
+ // In any case the FrameForDecoding should not wait longer than |
+ // max_wait_time. |
+ // In the case that we did not get a frame, it should have been waiting for |
+ // exactly max_wait_time. (By the testing samples we constructed above, we |
+ // are sure there is no timing error, so the only case it returns with NULL |
+ // is that it runs out of time.) |
+ if (frame) { |
+ receiver_.ReleaseFrame(frame); |
+ ++num_frames_return; |
+ EXPECT_GE(kMaxWaitTime, end_time - start_time); |
+ } else { |
+ EXPECT_EQ(kMaxWaitTime, end_time - start_time); |
+ } |
+ } |
+} |
+ |
} // namespace webrtc |