|  | /*  Copyright (c) 2013 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 <string.h> | 
|  |  | 
|  | #include <list> | 
|  | #include <memory> | 
|  | #include <queue> | 
|  | #include <vector> | 
|  |  | 
|  | #include "webrtc/base/checks.h" | 
|  | #include "webrtc/modules/video_coding/encoded_frame.h" | 
|  | #include "webrtc/modules/video_coding/packet.h" | 
|  | #include "webrtc/modules/video_coding/receiver.h" | 
|  | #include "webrtc/modules/video_coding/test/stream_generator.h" | 
|  | #include "webrtc/modules/video_coding/test/test_util.h" | 
|  | #include "webrtc/modules/video_coding/timing.h" | 
|  | #include "webrtc/system_wrappers/include/clock.h" | 
|  | #include "webrtc/system_wrappers/include/critical_section_wrapper.h" | 
|  | #include "webrtc/test/gtest.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | class TestVCMReceiver : public ::testing::Test { | 
|  | protected: | 
|  | TestVCMReceiver() | 
|  | : clock_(new SimulatedClock(0)), | 
|  | timing_(clock_.get()), | 
|  | receiver_(&timing_, clock_.get(), &event_factory_) { | 
|  | stream_generator_.reset( | 
|  | new StreamGenerator(0, clock_->TimeInMilliseconds())); | 
|  | } | 
|  |  | 
|  | virtual void SetUp() { receiver_.Reset(); } | 
|  |  | 
|  | int32_t InsertPacket(int index) { | 
|  | VCMPacket packet; | 
|  | bool packet_available = stream_generator_->GetPacket(&packet, index); | 
|  | EXPECT_TRUE(packet_available); | 
|  | if (!packet_available) | 
|  | return kGeneralError;  // Return here to avoid crashes below. | 
|  | return receiver_.InsertPacket(packet); | 
|  | } | 
|  |  | 
|  | int32_t InsertPacketAndPop(int index) { | 
|  | VCMPacket packet; | 
|  | bool packet_available = stream_generator_->PopPacket(&packet, index); | 
|  | EXPECT_TRUE(packet_available); | 
|  | if (!packet_available) | 
|  | return kGeneralError;  // Return here to avoid crashes below. | 
|  | return receiver_.InsertPacket(packet); | 
|  | } | 
|  |  | 
|  | int32_t InsertFrame(FrameType frame_type, bool complete) { | 
|  | int num_of_packets = complete ? 1 : 2; | 
|  | stream_generator_->GenerateFrame( | 
|  | frame_type, (frame_type != kEmptyFrame) ? num_of_packets : 0, | 
|  | (frame_type == kEmptyFrame) ? 1 : 0, clock_->TimeInMilliseconds()); | 
|  | int32_t ret = InsertPacketAndPop(0); | 
|  | if (!complete) { | 
|  | // Drop the second packet. | 
|  | VCMPacket packet; | 
|  | stream_generator_->PopPacket(&packet, 0); | 
|  | } | 
|  | clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | bool DecodeNextFrame() { | 
|  | VCMEncodedFrame* frame = receiver_.FrameForDecoding(0, false); | 
|  | if (!frame) | 
|  | return false; | 
|  | receiver_.ReleaseFrame(frame); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<SimulatedClock> clock_; | 
|  | VCMTiming timing_; | 
|  | NullEventFactory event_factory_; | 
|  | VCMReceiver receiver_; | 
|  | std::unique_ptr<StreamGenerator> stream_generator_; | 
|  | }; | 
|  |  | 
|  | TEST_F(TestVCMReceiver, NonDecodableDuration_Empty) { | 
|  | // Enable NACK and with no RTT thresholds for disabling retransmission delay. | 
|  | receiver_.SetNackMode(kNack, -1, -1); | 
|  | const size_t kMaxNackListSize = 1000; | 
|  | const int kMaxPacketAgeToNack = 1000; | 
|  | const int kMaxNonDecodableDuration = 500; | 
|  | const int kMinDelayMs = 500; | 
|  | receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, | 
|  | kMaxNonDecodableDuration); | 
|  | EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError); | 
|  | // Advance time until it's time to decode the key frame. | 
|  | clock_->AdvanceTimeMilliseconds(kMinDelayMs); | 
|  | EXPECT_TRUE(DecodeNextFrame()); | 
|  | bool request_key_frame = false; | 
|  | std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame); | 
|  | EXPECT_FALSE(request_key_frame); | 
|  | } | 
|  |  | 
|  | TEST_F(TestVCMReceiver, NonDecodableDuration_NoKeyFrame) { | 
|  | // Enable NACK and with no RTT thresholds for disabling retransmission delay. | 
|  | receiver_.SetNackMode(kNack, -1, -1); | 
|  | const size_t kMaxNackListSize = 1000; | 
|  | const int kMaxPacketAgeToNack = 1000; | 
|  | const int kMaxNonDecodableDuration = 500; | 
|  | receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, | 
|  | kMaxNonDecodableDuration); | 
|  | const int kNumFrames = kDefaultFrameRate * kMaxNonDecodableDuration / 1000; | 
|  | for (int i = 0; i < kNumFrames; ++i) { | 
|  | EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError); | 
|  | } | 
|  | bool request_key_frame = false; | 
|  | std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame); | 
|  | EXPECT_TRUE(request_key_frame); | 
|  | } | 
|  |  | 
|  | TEST_F(TestVCMReceiver, NonDecodableDuration_OneIncomplete) { | 
|  | // Enable NACK and with no RTT thresholds for disabling retransmission delay. | 
|  | receiver_.SetNackMode(kNack, -1, -1); | 
|  | const size_t kMaxNackListSize = 1000; | 
|  | const int kMaxPacketAgeToNack = 1000; | 
|  | const int kMaxNonDecodableDuration = 500; | 
|  | const int kMaxNonDecodableDurationFrames = | 
|  | (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000; | 
|  | const int kMinDelayMs = 500; | 
|  | receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, | 
|  | kMaxNonDecodableDuration); | 
|  | receiver_.SetMinReceiverDelay(kMinDelayMs); | 
|  | int64_t key_frame_inserted = clock_->TimeInMilliseconds(); | 
|  | EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError); | 
|  | // Insert an incomplete frame. | 
|  | EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError); | 
|  | // Insert enough frames to have too long non-decodable sequence. | 
|  | for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) { | 
|  | EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError); | 
|  | } | 
|  | // Advance time until it's time to decode the key frame. | 
|  | clock_->AdvanceTimeMilliseconds(kMinDelayMs - clock_->TimeInMilliseconds() - | 
|  | key_frame_inserted); | 
|  | EXPECT_TRUE(DecodeNextFrame()); | 
|  | // Make sure we get a key frame request. | 
|  | bool request_key_frame = false; | 
|  | std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame); | 
|  | EXPECT_TRUE(request_key_frame); | 
|  | } | 
|  |  | 
|  | TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger) { | 
|  | // Enable NACK and with no RTT thresholds for disabling retransmission delay. | 
|  | receiver_.SetNackMode(kNack, -1, -1); | 
|  | const size_t kMaxNackListSize = 1000; | 
|  | const int kMaxPacketAgeToNack = 1000; | 
|  | const int kMaxNonDecodableDuration = 500; | 
|  | const int kMaxNonDecodableDurationFrames = | 
|  | (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000; | 
|  | const int kMinDelayMs = 500; | 
|  | receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, | 
|  | kMaxNonDecodableDuration); | 
|  | receiver_.SetMinReceiverDelay(kMinDelayMs); | 
|  | int64_t key_frame_inserted = clock_->TimeInMilliseconds(); | 
|  | EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError); | 
|  | // Insert an incomplete frame. | 
|  | EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError); | 
|  | // Insert all but one frame to not trigger a key frame request due to | 
|  | // too long duration of non-decodable frames. | 
|  | for (int i = 0; i < kMaxNonDecodableDurationFrames - 1; ++i) { | 
|  | EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError); | 
|  | } | 
|  | // Advance time until it's time to decode the key frame. | 
|  | clock_->AdvanceTimeMilliseconds(kMinDelayMs - clock_->TimeInMilliseconds() - | 
|  | key_frame_inserted); | 
|  | EXPECT_TRUE(DecodeNextFrame()); | 
|  | // Make sure we don't get a key frame request since we haven't generated | 
|  | // enough frames. | 
|  | bool request_key_frame = false; | 
|  | std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame); | 
|  | EXPECT_FALSE(request_key_frame); | 
|  | } | 
|  |  | 
|  | TEST_F(TestVCMReceiver, NonDecodableDuration_NoTrigger2) { | 
|  | // Enable NACK and with no RTT thresholds for disabling retransmission delay. | 
|  | receiver_.SetNackMode(kNack, -1, -1); | 
|  | const size_t kMaxNackListSize = 1000; | 
|  | const int kMaxPacketAgeToNack = 1000; | 
|  | const int kMaxNonDecodableDuration = 500; | 
|  | const int kMaxNonDecodableDurationFrames = | 
|  | (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000; | 
|  | const int kMinDelayMs = 500; | 
|  | receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, | 
|  | kMaxNonDecodableDuration); | 
|  | receiver_.SetMinReceiverDelay(kMinDelayMs); | 
|  | int64_t key_frame_inserted = clock_->TimeInMilliseconds(); | 
|  | EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError); | 
|  | // Insert enough frames to have too long non-decodable sequence, except that | 
|  | // we don't have any losses. | 
|  | for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) { | 
|  | EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError); | 
|  | } | 
|  | // Insert an incomplete frame. | 
|  | EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError); | 
|  | // Advance time until it's time to decode the key frame. | 
|  | clock_->AdvanceTimeMilliseconds(kMinDelayMs - clock_->TimeInMilliseconds() - | 
|  | key_frame_inserted); | 
|  | EXPECT_TRUE(DecodeNextFrame()); | 
|  | // Make sure we don't get a key frame request since the non-decodable duration | 
|  | // is only one frame. | 
|  | bool request_key_frame = false; | 
|  | std::vector<uint16_t> nack_list = receiver_.NackList(&request_key_frame); | 
|  | EXPECT_FALSE(request_key_frame); | 
|  | } | 
|  |  | 
|  | TEST_F(TestVCMReceiver, NonDecodableDuration_KeyFrameAfterIncompleteFrames) { | 
|  | // Enable NACK and with no RTT thresholds for disabling retransmission delay. | 
|  | receiver_.SetNackMode(kNack, -1, -1); | 
|  | const size_t kMaxNackListSize = 1000; | 
|  | const int kMaxPacketAgeToNack = 1000; | 
|  | const int kMaxNonDecodableDuration = 500; | 
|  | const int kMaxNonDecodableDurationFrames = | 
|  | (kDefaultFrameRate * kMaxNonDecodableDuration + 500) / 1000; | 
|  | const int kMinDelayMs = 500; | 
|  | receiver_.SetNackSettings(kMaxNackListSize, kMaxPacketAgeToNack, | 
|  | kMaxNonDecodableDuration); | 
|  | receiver_.SetMinReceiverDelay(kMinDelayMs); | 
|  | int64_t key_frame_inserted = clock_->TimeInMilliseconds(); | 
|  | EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError); | 
|  | // Insert an incomplete frame. | 
|  | EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError); | 
|  | // Insert enough frames to have too long non-decodable sequence. | 
|  | for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) { | 
|  | EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError); | 
|  | } | 
|  | EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError); | 
|  | // Advance time until it's time to decode the key frame. | 
|  | clock_->AdvanceTimeMilliseconds(kMinDelayMs - clock_->TimeInMilliseconds() - | 
|  | key_frame_inserted); | 
|  | EXPECT_TRUE(DecodeNextFrame()); | 
|  | // Make sure we don't get a key frame request since we have a key frame | 
|  | // in the list. | 
|  | bool request_key_frame = false; | 
|  | 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) { | 
|  | RTC_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++) { | 
|  | RTC_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); | 
|  | } | 
|  |  | 
|  | 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 {  // NOLINT | 
|  | 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_, | 
|  | std::unique_ptr<EventWrapper>(new FrameInjectEvent(&clock_, false)), | 
|  | std::unique_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]; | 
|  |  | 
|  | // 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, 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); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test whether VCMReceiver::FrameForDecoding handles parameter | 
|  | // |prefer_late_decoding| and |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 and the end time must be equal to the render timestamp - delay | 
|  | //    for decoding and rendering. | 
|  | TEST_F(VCMReceiverTimingTest, FrameForDecodingPreferLateDecoding) { | 
|  | const size_t kNumFrames = 100; | 
|  | const int kFramePeriod = 40; | 
|  |  | 
|  | int64_t arrive_timestamps[kNumFrames]; | 
|  | int64_t render_timestamps[kNumFrames]; | 
|  |  | 
|  | int render_delay_ms; | 
|  | int max_decode_ms; | 
|  | int dummy; | 
|  | timing_.GetTimings(&dummy, &max_decode_ms, &dummy, &dummy, &dummy, &dummy, | 
|  | &render_delay_ms); | 
|  |  | 
|  | // 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; | 
|  | bool prefer_late_decoding = true; | 
|  | while (num_frames_return < kNumFrames) { | 
|  | int64_t start_time = clock_.TimeInMilliseconds(); | 
|  |  | 
|  | VCMEncodedFrame* frame = | 
|  | receiver_.FrameForDecoding(kMaxWaitTime, prefer_late_decoding); | 
|  | int64_t end_time = clock_.TimeInMilliseconds(); | 
|  | if (frame) { | 
|  | EXPECT_EQ(frame->RenderTimeMs() - max_decode_ms - render_delay_ms, | 
|  | end_time); | 
|  | receiver_.ReleaseFrame(frame); | 
|  | ++num_frames_return; | 
|  | } else { | 
|  | EXPECT_EQ(kMaxWaitTime, end_time - start_time); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |