| /* 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 "modules/video_coding/deprecated/receiver.h" | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <memory> | |
| #include <queue> | |
| #include <vector> | |
| #include "api/field_trials.h" | |
| #include "api/units/time_delta.h" | |
| #include "api/video/video_frame_type.h" | |
| #include "modules/video_coding/deprecated/event_wrapper.h" | |
| #include "modules/video_coding/deprecated/jitter_buffer_common.h" | |
| #include "modules/video_coding/deprecated/packet.h" | |
| #include "modules/video_coding/deprecated/stream_generator.h" | |
| #include "modules/video_coding/encoded_frame.h" | |
| #include "modules/video_coding/timing/timing.h" | |
| #include "rtc_base/checks.h" | |
| #include "system_wrappers/include/clock.h" | |
| #include "test/create_test_field_trials.h" | |
| #include "test/gtest.h" | |
| namespace webrtc { | |
| class TestVCMReceiver : public ::testing::Test { | |
| protected: | |
| TestVCMReceiver() | |
| : field_trials_(CreateTestFieldTrials()), | |
| clock_(0), | |
| timing_(&clock_, field_trials_), | |
| receiver_(&timing_, &clock_, field_trials_), | |
| stream_generator_(0, clock_.TimeInMilliseconds()) {} | |
| 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(VideoFrameType frame_type, bool complete) { | |
| int num_of_packets = complete ? 1 : 2; | |
| stream_generator_.GenerateFrame( | |
| frame_type, | |
| (frame_type != VideoFrameType::kEmptyFrame) ? num_of_packets : 0, | |
| (frame_type == VideoFrameType::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; | |
| } | |
| FieldTrials field_trials_; | |
| SimulatedClock clock_; | |
| VCMTiming timing_; | |
| VCMReceiver receiver_; | |
| StreamGenerator stream_generator_; | |
| }; | |
| TEST_F(TestVCMReceiver, NonDecodableDuration_Empty) { | |
| 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(VideoFrameType::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) { | |
| 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(VideoFrameType::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) { | |
| 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); | |
| timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); | |
| int64_t key_frame_inserted = clock_.TimeInMilliseconds(); | |
| EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); | |
| // Insert an incomplete frame. | |
| EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError); | |
| // Insert enough frames to have too long non-decodable sequence. | |
| for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) { | |
| EXPECT_GE(InsertFrame(VideoFrameType::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) { | |
| 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); | |
| timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); | |
| int64_t key_frame_inserted = clock_.TimeInMilliseconds(); | |
| EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); | |
| // Insert an incomplete frame. | |
| EXPECT_GE(InsertFrame(VideoFrameType::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(VideoFrameType::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) { | |
| 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); | |
| timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); | |
| int64_t key_frame_inserted = clock_.TimeInMilliseconds(); | |
| EXPECT_GE(InsertFrame(VideoFrameType::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(VideoFrameType::kVideoFrameDelta, true), kNoError); | |
| } | |
| // Insert an incomplete frame. | |
| EXPECT_GE(InsertFrame(VideoFrameType::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) { | |
| 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); | |
| timing_.set_min_playout_delay(TimeDelta::Millis(kMinDelayMs)); | |
| int64_t key_frame_inserted = clock_.TimeInMilliseconds(); | |
| EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameKey, true), kNoError); | |
| // Insert an incomplete frame. | |
| EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, false), kNoError); | |
| // Insert enough frames to have too long non-decodable sequence. | |
| for (int i = 0; i < kMaxNonDecodableDurationFrames; ++i) { | |
| EXPECT_GE(InsertFrame(VideoFrameType::kVideoFrameDelta, true), kNoError); | |
| } | |
| EXPECT_GE(InsertFrame(VideoFrameType::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) {} | |
| ~SimulatedClockWithFrames() override {} | |
| // 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_GE(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_GE(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(VideoFrameType::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(int max_time_ms) override { | |
| if (clock_->AdvanceTimeMilliseconds(max_time_ms, 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() | |
| : field_trials_(CreateTestFieldTrials()), | |
| clock_(&stream_generator_, &receiver_), | |
| stream_generator_(0, clock_.TimeInMilliseconds()), | |
| timing_(&clock_, field_trials_), | |
| receiver_( | |
| &timing_, | |
| &clock_, | |
| std::unique_ptr<EventWrapper>(new FrameInjectEvent(&clock_, false)), | |
| std::unique_ptr<EventWrapper>(new FrameInjectEvent(&clock_, true)), | |
| field_trials_) {} | |
| void SetUp() override {} | |
| FieldTrials field_trials_; | |
| 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]; | |
| auto timings = timing_.GetTimings(); | |
| TimeDelta render_delay = timings.render_delay; | |
| TimeDelta max_decode = timings.estimated_max_decode_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; | |
| 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 |