diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/libwebrtc/modules/video_coding/receiver_unittest.cc | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/video_coding/receiver_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/modules/video_coding/receiver_unittest.cc | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/video_coding/receiver_unittest.cc b/third_party/libwebrtc/modules/video_coding/receiver_unittest.cc new file mode 100644 index 0000000000..2beb97e972 --- /dev/null +++ b/third_party/libwebrtc/modules/video_coding/receiver_unittest.cc @@ -0,0 +1,493 @@ +/* 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/receiver.h" + +#include <string.h> + +#include <cstdint> +#include <memory> +#include <queue> +#include <vector> + +#include "modules/video_coding/encoded_frame.h" +#include "modules/video_coding/jitter_buffer_common.h" +#include "modules/video_coding/packet.h" +#include "modules/video_coding/test/stream_generator.h" +#include "modules/video_coding/timing/timing.h" +#include "rtc_base/checks.h" +#include "system_wrappers/include/clock.h" +#include "test/gtest.h" +#include "test/scoped_key_value_config.h" + +namespace webrtc { + +class TestVCMReceiver : public ::testing::Test { + protected: + TestVCMReceiver() + : 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; + } + + test::ScopedKeyValueConfig 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) {} + 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_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() + : 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_) {} + + virtual void SetUp() {} + + test::ScopedKeyValueConfig 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.max_decode_duration; + + // 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 |