| /* | 
 |  *  Copyright (c) 2015 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 "call/rtp_video_sender.h" | 
 |  | 
 | #include <atomic> | 
 | #include <memory> | 
 | #include <string> | 
 | #include <utility> | 
 |  | 
 | #include "absl/functional/any_invocable.h" | 
 | #include "call/rtp_transport_controller_send.h" | 
 | #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" | 
 | #include "modules/rtp_rtcp/source/byte_io.h" | 
 | #include "modules/rtp_rtcp/source/rtcp_packet/nack.h" | 
 | #include "modules/rtp_rtcp/source/rtp_dependency_descriptor_extension.h" | 
 | #include "modules/rtp_rtcp/source/rtp_packet.h" | 
 | #include "modules/video_coding/fec_controller_default.h" | 
 | #include "modules/video_coding/include/video_codec_interface.h" | 
 | #include "rtc_base/rate_limiter.h" | 
 | #include "test/gmock.h" | 
 | #include "test/gtest.h" | 
 | #include "test/mock_frame_transformer.h" | 
 | #include "test/mock_transport.h" | 
 | #include "test/scenario/scenario.h" | 
 | #include "test/scoped_key_value_config.h" | 
 | #include "test/time_controller/simulated_time_controller.h" | 
 | #include "video/send_statistics_proxy.h" | 
 |  | 
 | namespace webrtc { | 
 | namespace { | 
 |  | 
 | using ::testing::_; | 
 | using ::testing::NiceMock; | 
 | using ::testing::SaveArg; | 
 | using ::testing::SizeIs; | 
 |  | 
 | const int8_t kPayloadType = 96; | 
 | const uint32_t kSsrc1 = 12345; | 
 | const uint32_t kSsrc2 = 23456; | 
 | const uint32_t kRtxSsrc1 = 34567; | 
 | const uint32_t kRtxSsrc2 = 45678; | 
 | const int16_t kInitialPictureId1 = 222; | 
 | const int16_t kInitialPictureId2 = 44; | 
 | const int16_t kInitialTl0PicIdx1 = 99; | 
 | const int16_t kInitialTl0PicIdx2 = 199; | 
 | const int64_t kRetransmitWindowSizeMs = 500; | 
 | const int kTransportsSequenceExtensionId = 7; | 
 | const int kDependencyDescriptorExtensionId = 8; | 
 |  | 
 | class MockRtcpIntraFrameObserver : public RtcpIntraFrameObserver { | 
 |  public: | 
 |   MOCK_METHOD(void, OnReceivedIntraFrameRequest, (uint32_t), (override)); | 
 | }; | 
 |  | 
 | RtpSenderObservers CreateObservers( | 
 |     RtcpIntraFrameObserver* intra_frame_callback, | 
 |     ReportBlockDataObserver* report_block_data_observer, | 
 |     StreamDataCountersCallback* rtp_stats, | 
 |     BitrateStatisticsObserver* bitrate_observer, | 
 |     FrameCountObserver* frame_count_observer, | 
 |     RtcpPacketTypeCounterObserver* rtcp_type_observer) { | 
 |   RtpSenderObservers observers; | 
 |   observers.rtcp_rtt_stats = nullptr; | 
 |   observers.intra_frame_callback = intra_frame_callback; | 
 |   observers.rtcp_loss_notification_observer = nullptr; | 
 |   observers.report_block_data_observer = report_block_data_observer; | 
 |   observers.rtp_stats = rtp_stats; | 
 |   observers.bitrate_observer = bitrate_observer; | 
 |   observers.frame_count_observer = frame_count_observer; | 
 |   observers.rtcp_type_observer = rtcp_type_observer; | 
 |   observers.send_packet_observer = nullptr; | 
 |   return observers; | 
 | } | 
 |  | 
 | BitrateConstraints GetBitrateConfig() { | 
 |   BitrateConstraints bitrate_config; | 
 |   bitrate_config.min_bitrate_bps = 30000; | 
 |   bitrate_config.start_bitrate_bps = 300000; | 
 |   bitrate_config.max_bitrate_bps = 3000000; | 
 |   return bitrate_config; | 
 | } | 
 |  | 
 | VideoSendStream::Config CreateVideoSendStreamConfig( | 
 |     Transport* transport, | 
 |     const std::vector<uint32_t>& ssrcs, | 
 |     const std::vector<uint32_t>& rtx_ssrcs, | 
 |     int payload_type) { | 
 |   VideoSendStream::Config config(transport); | 
 |   config.rtp.ssrcs = ssrcs; | 
 |   config.rtp.rtx.ssrcs = rtx_ssrcs; | 
 |   config.rtp.payload_type = payload_type; | 
 |   config.rtp.rtx.payload_type = payload_type + 1; | 
 |   config.rtp.nack.rtp_history_ms = 1000; | 
 |   config.rtp.extensions.emplace_back(RtpExtension::kTransportSequenceNumberUri, | 
 |                                      kTransportsSequenceExtensionId); | 
 |   config.rtp.extensions.emplace_back(RtpDependencyDescriptorExtension::Uri(), | 
 |                                      kDependencyDescriptorExtensionId); | 
 |   config.rtp.extmap_allow_mixed = true; | 
 |   return config; | 
 | } | 
 |  | 
 | class RtpVideoSenderTestFixture { | 
 |  public: | 
 |   RtpVideoSenderTestFixture( | 
 |       const std::vector<uint32_t>& ssrcs, | 
 |       const std::vector<uint32_t>& rtx_ssrcs, | 
 |       int payload_type, | 
 |       const std::map<uint32_t, RtpPayloadState>& suspended_payload_states, | 
 |       FrameCountObserver* frame_count_observer, | 
 |       rtc::scoped_refptr<FrameTransformerInterface> frame_transformer, | 
 |       const FieldTrialsView* field_trials = nullptr) | 
 |       : time_controller_(Timestamp::Millis(1000000)), | 
 |         config_(CreateVideoSendStreamConfig(&transport_, | 
 |                                             ssrcs, | 
 |                                             rtx_ssrcs, | 
 |                                             payload_type)), | 
 |         bitrate_config_(GetBitrateConfig()), | 
 |         transport_controller_( | 
 |             time_controller_.GetClock(), | 
 |             RtpTransportConfig{ | 
 |                 .bitrate_config = bitrate_config_, | 
 |                 .event_log = &event_log_, | 
 |                 .task_queue_factory = time_controller_.GetTaskQueueFactory(), | 
 |                 .trials = field_trials ? field_trials : &field_trials_, | 
 |             }), | 
 |         stats_proxy_(time_controller_.GetClock(), | 
 |                      config_, | 
 |                      VideoEncoderConfig::ContentType::kRealtimeVideo, | 
 |                      field_trials ? *field_trials : field_trials_), | 
 |         retransmission_rate_limiter_(time_controller_.GetClock(), | 
 |                                      kRetransmitWindowSizeMs) { | 
 |     transport_controller_.EnsureStarted(); | 
 |     std::map<uint32_t, RtpState> suspended_ssrcs; | 
 |     router_ = std::make_unique<RtpVideoSender>( | 
 |         time_controller_.GetClock(), suspended_ssrcs, suspended_payload_states, | 
 |         config_.rtp, config_.rtcp_report_interval_ms, &transport_, | 
 |         CreateObservers(&encoder_feedback_, &stats_proxy_, &stats_proxy_, | 
 |                         &stats_proxy_, frame_count_observer, &stats_proxy_), | 
 |         &transport_controller_, &event_log_, &retransmission_rate_limiter_, | 
 |         std::make_unique<FecControllerDefault>(time_controller_.GetClock()), | 
 |         nullptr, CryptoOptions{}, frame_transformer, | 
 |         field_trials ? *field_trials : field_trials_, | 
 |         time_controller_.GetTaskQueueFactory()); | 
 |   } | 
 |  | 
 |   RtpVideoSenderTestFixture( | 
 |       const std::vector<uint32_t>& ssrcs, | 
 |       const std::vector<uint32_t>& rtx_ssrcs, | 
 |       int payload_type, | 
 |       const std::map<uint32_t, RtpPayloadState>& suspended_payload_states, | 
 |       FrameCountObserver* frame_count_observer, | 
 |       const FieldTrialsView* field_trials = nullptr) | 
 |       : RtpVideoSenderTestFixture(ssrcs, | 
 |                                   rtx_ssrcs, | 
 |                                   payload_type, | 
 |                                   suspended_payload_states, | 
 |                                   frame_count_observer, | 
 |                                   /*frame_transformer=*/nullptr, | 
 |                                   field_trials) {} | 
 |  | 
 |   RtpVideoSenderTestFixture( | 
 |       const std::vector<uint32_t>& ssrcs, | 
 |       const std::vector<uint32_t>& rtx_ssrcs, | 
 |       int payload_type, | 
 |       const std::map<uint32_t, RtpPayloadState>& suspended_payload_states, | 
 |       const FieldTrialsView* field_trials = nullptr) | 
 |       : RtpVideoSenderTestFixture(ssrcs, | 
 |                                   rtx_ssrcs, | 
 |                                   payload_type, | 
 |                                   suspended_payload_states, | 
 |                                   /*frame_count_observer=*/nullptr, | 
 |                                   /*frame_transformer=*/nullptr, | 
 |                                   field_trials) {} | 
 |  | 
 |   ~RtpVideoSenderTestFixture() { Stop(); } | 
 |  | 
 |   RtpVideoSender* router() { return router_.get(); } | 
 |   MockTransport& transport() { return transport_; } | 
 |   void AdvanceTime(TimeDelta delta) { time_controller_.AdvanceTime(delta); } | 
 |  | 
 |   void Stop() { router_->Stop(); } | 
 |  | 
 |   void SetActiveModules(const std::vector<bool>& active_modules) { | 
 |     router_->SetActiveModules(active_modules); | 
 |   } | 
 |  | 
 |  private: | 
 |   test::ScopedKeyValueConfig field_trials_; | 
 |   NiceMock<MockTransport> transport_; | 
 |   NiceMock<MockRtcpIntraFrameObserver> encoder_feedback_; | 
 |   GlobalSimulatedTimeController time_controller_; | 
 |   RtcEventLogNull event_log_; | 
 |   VideoSendStream::Config config_; | 
 |   BitrateConstraints bitrate_config_; | 
 |   RtpTransportControllerSend transport_controller_; | 
 |   SendStatisticsProxy stats_proxy_; | 
 |   RateLimiter retransmission_rate_limiter_; | 
 |   std::unique_ptr<RtpVideoSender> router_; | 
 | }; | 
 |  | 
 | BitrateAllocationUpdate CreateBitrateAllocationUpdate(int target_bitrate_bps) { | 
 |   BitrateAllocationUpdate update; | 
 |   update.target_bitrate = DataRate::BitsPerSec(target_bitrate_bps); | 
 |   update.round_trip_time = TimeDelta::Zero(); | 
 |   return update; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | TEST(RtpVideoSenderTest, SendOnOneModule) { | 
 |   constexpr uint8_t kPayload = 'a'; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); | 
 |  | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {kRtxSsrc1}, kPayloadType, {}); | 
 |   EXPECT_NE(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image, nullptr).error); | 
 |  | 
 |   test.SetActiveModules({true}); | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image, nullptr).error); | 
 |  | 
 |   test.SetActiveModules({false}); | 
 |   EXPECT_NE(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image, nullptr).error); | 
 |  | 
 |   test.SetActiveModules({true}); | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image, nullptr).error); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, SendSimulcastSetActive) { | 
 |   constexpr uint8_t kPayload = 'a'; | 
 |   EncodedImage encoded_image_1; | 
 |   encoded_image_1.SetRtpTimestamp(1); | 
 |   encoded_image_1.capture_time_ms_ = 2; | 
 |   encoded_image_1._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image_1.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); | 
 |  | 
 |   RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, | 
 |                                  kPayloadType, {}); | 
 |  | 
 |   CodecSpecificInfo codec_info; | 
 |   codec_info.codecType = kVideoCodecVP8; | 
 |  | 
 |   test.SetActiveModules({true, true}); | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_1, &codec_info).error); | 
 |  | 
 |   EncodedImage encoded_image_2(encoded_image_1); | 
 |   encoded_image_2.SetSimulcastIndex(1); | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_2, &codec_info).error); | 
 |  | 
 |   // Inactive. | 
 |   test.Stop(); | 
 |   EXPECT_NE(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_1, &codec_info).error); | 
 |   EXPECT_NE(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_2, &codec_info).error); | 
 | } | 
 |  | 
 | // Tests how setting individual rtp modules to active affects the overall | 
 | // behavior of the payload router. First sets one module to active and checks | 
 | // that outgoing data can be sent on this module, and checks that no data can | 
 | // be sent if both modules are inactive. | 
 | TEST(RtpVideoSenderTest, SendSimulcastSetActiveModules) { | 
 |   constexpr uint8_t kPayload = 'a'; | 
 |   EncodedImage encoded_image_1; | 
 |   encoded_image_1.SetRtpTimestamp(1); | 
 |   encoded_image_1.capture_time_ms_ = 2; | 
 |   encoded_image_1._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image_1.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); | 
 |  | 
 |   EncodedImage encoded_image_2(encoded_image_1); | 
 |   encoded_image_2.SetSimulcastIndex(1); | 
 |  | 
 |   RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, | 
 |                                  kPayloadType, {}); | 
 |   CodecSpecificInfo codec_info; | 
 |   codec_info.codecType = kVideoCodecVP8; | 
 |  | 
 |   // Only setting one stream to active will still set the payload router to | 
 |   // active and allow sending data on the active stream. | 
 |   std::vector<bool> active_modules({true, false}); | 
 |   test.SetActiveModules(active_modules); | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_1, &codec_info).error); | 
 |  | 
 |   // Setting both streams to inactive will turn the payload router to | 
 |   // inactive. | 
 |   active_modules = {false, false}; | 
 |   test.SetActiveModules(active_modules); | 
 |   // An incoming encoded image will not ask the module to send outgoing data | 
 |   // because the payload router is inactive. | 
 |   EXPECT_NE(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_1, &codec_info).error); | 
 |   EXPECT_NE(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_1, &codec_info).error); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, | 
 |      DiscardsHigherSimulcastFramesAfterLayerDisabledInVideoLayersAllocation) { | 
 |   constexpr uint8_t kPayload = 'a'; | 
 |   EncodedImage encoded_image_1; | 
 |   encoded_image_1.SetRtpTimestamp(1); | 
 |   encoded_image_1.capture_time_ms_ = 2; | 
 |   encoded_image_1._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image_1.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); | 
 |   EncodedImage encoded_image_2(encoded_image_1); | 
 |   encoded_image_2.SetSimulcastIndex(1); | 
 |   CodecSpecificInfo codec_info; | 
 |   codec_info.codecType = kVideoCodecVP8; | 
 |   RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, | 
 |                                  kPayloadType, {}); | 
 |   test.SetActiveModules({true, true}); | 
 |   // A layer is sent on both rtp streams. | 
 |   test.router()->OnVideoLayersAllocationUpdated( | 
 |       {.active_spatial_layers = {{.rtp_stream_index = 0}, | 
 |                                  {.rtp_stream_index = 1}}}); | 
 |  | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_1, &codec_info).error); | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_2, &codec_info).error); | 
 |  | 
 |   // Only rtp stream index 0 is configured to send a stream. | 
 |   test.router()->OnVideoLayersAllocationUpdated( | 
 |       {.active_spatial_layers = {{.rtp_stream_index = 0}}}); | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_1, &codec_info).error); | 
 |   EXPECT_NE(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image_2, &codec_info).error); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, CreateWithNoPreviousStates) { | 
 |   RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, | 
 |                                  kPayloadType, {}); | 
 |   test.SetActiveModules({true, true}); | 
 |  | 
 |   std::map<uint32_t, RtpPayloadState> initial_states = | 
 |       test.router()->GetRtpPayloadStates(); | 
 |   EXPECT_EQ(2u, initial_states.size()); | 
 |   EXPECT_NE(initial_states.find(kSsrc1), initial_states.end()); | 
 |   EXPECT_NE(initial_states.find(kSsrc2), initial_states.end()); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, CreateWithPreviousStates) { | 
 |   const int64_t kState1SharedFrameId = 123; | 
 |   const int64_t kState2SharedFrameId = 234; | 
 |   RtpPayloadState state1; | 
 |   state1.picture_id = kInitialPictureId1; | 
 |   state1.tl0_pic_idx = kInitialTl0PicIdx1; | 
 |   state1.shared_frame_id = kState1SharedFrameId; | 
 |   RtpPayloadState state2; | 
 |   state2.picture_id = kInitialPictureId2; | 
 |   state2.tl0_pic_idx = kInitialTl0PicIdx2; | 
 |   state2.shared_frame_id = kState2SharedFrameId; | 
 |   std::map<uint32_t, RtpPayloadState> states = {{kSsrc1, state1}, | 
 |                                                 {kSsrc2, state2}}; | 
 |  | 
 |   RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, | 
 |                                  kPayloadType, states); | 
 |   test.SetActiveModules({true, true}); | 
 |  | 
 |   std::map<uint32_t, RtpPayloadState> initial_states = | 
 |       test.router()->GetRtpPayloadStates(); | 
 |   EXPECT_EQ(2u, initial_states.size()); | 
 |   EXPECT_EQ(kInitialPictureId1, initial_states[kSsrc1].picture_id); | 
 |   EXPECT_EQ(kInitialTl0PicIdx1, initial_states[kSsrc1].tl0_pic_idx); | 
 |   EXPECT_EQ(kInitialPictureId2, initial_states[kSsrc2].picture_id); | 
 |   EXPECT_EQ(kInitialTl0PicIdx2, initial_states[kSsrc2].tl0_pic_idx); | 
 |   EXPECT_EQ(kState2SharedFrameId, initial_states[kSsrc1].shared_frame_id); | 
 |   EXPECT_EQ(kState2SharedFrameId, initial_states[kSsrc2].shared_frame_id); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, FrameCountCallbacks) { | 
 |   class MockFrameCountObserver : public FrameCountObserver { | 
 |    public: | 
 |     MOCK_METHOD(void, | 
 |                 FrameCountUpdated, | 
 |                 (const FrameCounts& frame_counts, uint32_t ssrc), | 
 |                 (override)); | 
 |   } callback; | 
 |  | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {kRtxSsrc1}, kPayloadType, {}, | 
 |                                  &callback); | 
 |  | 
 |   constexpr uint8_t kPayload = 'a'; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); | 
 |  | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |  | 
 |   // No callbacks when not active. | 
 |   EXPECT_CALL(callback, FrameCountUpdated).Times(0); | 
 |   EXPECT_NE(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image, nullptr).error); | 
 |   ::testing::Mock::VerifyAndClearExpectations(&callback); | 
 |  | 
 |   test.SetActiveModules({true}); | 
 |  | 
 |   FrameCounts frame_counts; | 
 |   EXPECT_CALL(callback, FrameCountUpdated(_, kSsrc1)) | 
 |       .WillOnce(SaveArg<0>(&frame_counts)); | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image, nullptr).error); | 
 |  | 
 |   EXPECT_EQ(1, frame_counts.key_frames); | 
 |   EXPECT_EQ(0, frame_counts.delta_frames); | 
 |  | 
 |   ::testing::Mock::VerifyAndClearExpectations(&callback); | 
 |  | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameDelta; | 
 |   EXPECT_CALL(callback, FrameCountUpdated(_, kSsrc1)) | 
 |       .WillOnce(SaveArg<0>(&frame_counts)); | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image, nullptr).error); | 
 |  | 
 |   EXPECT_EQ(1, frame_counts.key_frames); | 
 |   EXPECT_EQ(1, frame_counts.delta_frames); | 
 | } | 
 |  | 
 | // Integration test verifying that ack of packet via TransportFeedback means | 
 | // that the packet is removed from RtpPacketHistory and won't be retransmitted | 
 | // again. | 
 | TEST(RtpVideoSenderTest, DoesNotRetrasmitAckedPackets) { | 
 |   RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, | 
 |                                  kPayloadType, {}); | 
 |   test.SetActiveModules({true, true}); | 
 |  | 
 |   constexpr uint8_t kPayload = 'a'; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); | 
 |  | 
 |   // Send two tiny images, mapping to two RTP packets. Capture sequence numbers. | 
 |   std::vector<uint16_t> rtp_sequence_numbers; | 
 |   std::vector<uint16_t> transport_sequence_numbers; | 
 |   EXPECT_CALL(test.transport(), SendRtp) | 
 |       .Times(2) | 
 |       .WillRepeatedly([&rtp_sequence_numbers, &transport_sequence_numbers]( | 
 |                           rtc::ArrayView<const uint8_t> packet, | 
 |                           const PacketOptions& options) { | 
 |         RtpPacket rtp_packet; | 
 |         EXPECT_TRUE(rtp_packet.Parse(packet)); | 
 |         rtp_sequence_numbers.push_back(rtp_packet.SequenceNumber()); | 
 |         transport_sequence_numbers.push_back(options.packet_id); | 
 |         return true; | 
 |       }); | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image, nullptr).error); | 
 |   encoded_image.SetRtpTimestamp(2); | 
 |   encoded_image.capture_time_ms_ = 3; | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage(encoded_image, nullptr).error); | 
 |  | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |  | 
 |   // Construct a NACK message for requesting retransmission of both packet. | 
 |   rtcp::Nack nack; | 
 |   nack.SetMediaSsrc(kSsrc1); | 
 |   nack.SetPacketIds(rtp_sequence_numbers); | 
 |   rtc::Buffer nack_buffer = nack.Build(); | 
 |  | 
 |   std::vector<uint16_t> retransmitted_rtp_sequence_numbers; | 
 |   EXPECT_CALL(test.transport(), SendRtp) | 
 |       .Times(2) | 
 |       .WillRepeatedly([&retransmitted_rtp_sequence_numbers]( | 
 |                           rtc::ArrayView<const uint8_t> packet, | 
 |                           const PacketOptions& options) { | 
 |         RtpPacket rtp_packet; | 
 |         EXPECT_TRUE(rtp_packet.Parse(packet)); | 
 |         EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); | 
 |         // Capture the retransmitted sequence number from the RTX header. | 
 |         rtc::ArrayView<const uint8_t> payload = rtp_packet.payload(); | 
 |         retransmitted_rtp_sequence_numbers.push_back( | 
 |             ByteReader<uint16_t>::ReadBigEndian(payload.data())); | 
 |         return true; | 
 |       }); | 
 |   test.router()->DeliverRtcp(nack_buffer.data(), nack_buffer.size()); | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |  | 
 |   // Verify that both packets were retransmitted. | 
 |   EXPECT_EQ(retransmitted_rtp_sequence_numbers, rtp_sequence_numbers); | 
 |  | 
 |   // Simulate transport feedback indicating fist packet received, next packet | 
 |   // lost (not other way around as that would trigger early retransmit). | 
 |   StreamFeedbackObserver::StreamPacketInfo lost_packet_feedback; | 
 |   lost_packet_feedback.rtp_sequence_number = rtp_sequence_numbers[0]; | 
 |   lost_packet_feedback.ssrc = kSsrc1; | 
 |   lost_packet_feedback.received = false; | 
 |   lost_packet_feedback.is_retransmission = false; | 
 |  | 
 |   StreamFeedbackObserver::StreamPacketInfo received_packet_feedback; | 
 |   received_packet_feedback.rtp_sequence_number = rtp_sequence_numbers[1]; | 
 |   received_packet_feedback.ssrc = kSsrc1; | 
 |   received_packet_feedback.received = true; | 
 |   lost_packet_feedback.is_retransmission = false; | 
 |  | 
 |   test.router()->OnPacketFeedbackVector( | 
 |       {lost_packet_feedback, received_packet_feedback}); | 
 |  | 
 |   // Advance time to make sure retransmission would be allowed and try again. | 
 |   // This time the retransmission should not happen for the first packet since | 
 |   // the history has been notified of the ack and removed the packet. The | 
 |   // second packet, included in the feedback but not marked as received, should | 
 |   // still be retransmitted. | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |   EXPECT_CALL(test.transport(), SendRtp) | 
 |       .WillOnce([&lost_packet_feedback](rtc::ArrayView<const uint8_t> packet, | 
 |                                         const PacketOptions& options) { | 
 |         RtpPacket rtp_packet; | 
 |         EXPECT_TRUE(rtp_packet.Parse(packet)); | 
 |         EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); | 
 |         // Capture the retransmitted sequence number from the RTX header. | 
 |         rtc::ArrayView<const uint8_t> payload = rtp_packet.payload(); | 
 |         EXPECT_EQ(lost_packet_feedback.rtp_sequence_number, | 
 |                   ByteReader<uint16_t>::ReadBigEndian(payload.data())); | 
 |         return true; | 
 |       }); | 
 |   test.router()->DeliverRtcp(nack_buffer.data(), nack_buffer.size()); | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 | } | 
 |  | 
 | // This tests that we utilize transport wide feedback to retransmit lost | 
 | // packets. This is tested by dropping all ordinary packets from a "lossy" | 
 | // stream sent along with a secondary untouched stream. The transport wide | 
 | // feedback packets from the secondary stream allows the sending side to | 
 | // detect and retreansmit the lost packets from the lossy stream. | 
 | TEST(RtpVideoSenderTest, RetransmitsOnTransportWideLossInfo) { | 
 |   int rtx_packets; | 
 |   test::Scenario s(test_info_); | 
 |   test::CallClientConfig call_conf; | 
 |   // Keeping the bitrate fixed to avoid RTX due to probing. | 
 |   call_conf.transport.rates.max_rate = DataRate::KilobitsPerSec(300); | 
 |   call_conf.transport.rates.start_rate = DataRate::KilobitsPerSec(300); | 
 |   test::NetworkSimulationConfig net_conf; | 
 |   net_conf.bandwidth = DataRate::KilobitsPerSec(300); | 
 |   auto send_node = s.CreateSimulationNode(net_conf); | 
 |   auto* callee = s.CreateClient("return", call_conf); | 
 |   auto* route = s.CreateRoutes(s.CreateClient("send", call_conf), {send_node}, | 
 |                                callee, {s.CreateSimulationNode(net_conf)}); | 
 |  | 
 |   test::VideoStreamConfig lossy_config; | 
 |   lossy_config.source.framerate = 5; | 
 |   auto* lossy = s.CreateVideoStream(route->forward(), lossy_config); | 
 |   // The secondary stream acts a driver for transport feedback messages, | 
 |   // ensuring that lost packets on the lossy stream are retransmitted. | 
 |   s.CreateVideoStream(route->forward(), test::VideoStreamConfig()); | 
 |  | 
 |   send_node->router()->SetFilter([&](const EmulatedIpPacket& packet) { | 
 |     RtpPacket rtp; | 
 |     if (rtp.Parse(packet.data)) { | 
 |       // Drops all regular packets for the lossy stream and counts all RTX | 
 |       // packets. Since no packets are let trough, NACKs can't be triggered | 
 |       // by the receiving side. | 
 |       if (lossy->send()->UsingSsrc(rtp.Ssrc())) { | 
 |         return false; | 
 |       } else if (lossy->send()->UsingRtxSsrc(rtp.Ssrc())) { | 
 |         ++rtx_packets; | 
 |       } | 
 |     } | 
 |     return true; | 
 |   }); | 
 |  | 
 |   // Run for a short duration and reset counters to avoid counting RTX packets | 
 |   // from initial probing. | 
 |   s.RunFor(TimeDelta::Seconds(1)); | 
 |   rtx_packets = 0; | 
 |   int decoded_baseline = 0; | 
 |   callee->SendTask([&decoded_baseline, &lossy]() { | 
 |     decoded_baseline = lossy->receive()->GetStats().frames_decoded; | 
 |   }); | 
 |   s.RunFor(TimeDelta::Seconds(1)); | 
 |   // We expect both that RTX packets were sent and that an appropriate number of | 
 |   // frames were received. This is somewhat redundant but reduces the risk of | 
 |   // false positives in future regressions (e.g. RTX is send due to probing). | 
 |   EXPECT_GE(rtx_packets, 1); | 
 |   int frames_decoded = 0; | 
 |   callee->SendTask([&decoded_baseline, &frames_decoded, &lossy]() { | 
 |     frames_decoded = | 
 |         lossy->receive()->GetStats().frames_decoded - decoded_baseline; | 
 |   }); | 
 |   EXPECT_EQ(frames_decoded, 5); | 
 | } | 
 |  | 
 | // Integration test verifying that retransmissions are sent for packets which | 
 | // can be detected as lost early, using transport wide feedback. | 
 | TEST(RtpVideoSenderTest, EarlyRetransmits) { | 
 |   RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, | 
 |                                  kPayloadType, {}); | 
 |   test.SetActiveModules({true, true}); | 
 |  | 
 |   const uint8_t kPayload[1] = {'a'}; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image.SetEncodedData( | 
 |       EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); | 
 |   encoded_image.SetSimulcastIndex(0); | 
 |  | 
 |   CodecSpecificInfo codec_specific; | 
 |   codec_specific.codecType = VideoCodecType::kVideoCodecGeneric; | 
 |  | 
 |   // Send two tiny images, mapping to single RTP packets. Capture sequence | 
 |   // numbers. | 
 |   uint16_t frame1_rtp_sequence_number = 0; | 
 |   uint16_t frame1_transport_sequence_number = 0; | 
 |   EXPECT_CALL(test.transport(), SendRtp) | 
 |       .WillOnce( | 
 |           [&frame1_rtp_sequence_number, &frame1_transport_sequence_number]( | 
 |               rtc::ArrayView<const uint8_t> packet, | 
 |               const PacketOptions& options) { | 
 |             RtpPacket rtp_packet; | 
 |             EXPECT_TRUE(rtp_packet.Parse(packet)); | 
 |             frame1_rtp_sequence_number = rtp_packet.SequenceNumber(); | 
 |             frame1_transport_sequence_number = options.packet_id; | 
 |             EXPECT_EQ(rtp_packet.Ssrc(), kSsrc1); | 
 |             return true; | 
 |           }); | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |  | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |  | 
 |   uint16_t frame2_rtp_sequence_number = 0; | 
 |   uint16_t frame2_transport_sequence_number = 0; | 
 |   encoded_image.SetSimulcastIndex(1); | 
 |   EXPECT_CALL(test.transport(), SendRtp) | 
 |       .WillOnce( | 
 |           [&frame2_rtp_sequence_number, &frame2_transport_sequence_number]( | 
 |               rtc::ArrayView<const uint8_t> packet, | 
 |               const PacketOptions& options) { | 
 |             RtpPacket rtp_packet; | 
 |             EXPECT_TRUE(rtp_packet.Parse(packet)); | 
 |             frame2_rtp_sequence_number = rtp_packet.SequenceNumber(); | 
 |             frame2_transport_sequence_number = options.packet_id; | 
 |             EXPECT_EQ(rtp_packet.Ssrc(), kSsrc2); | 
 |             return true; | 
 |           }); | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |  | 
 |   EXPECT_NE(frame1_transport_sequence_number, frame2_transport_sequence_number); | 
 |  | 
 |   // Inject a transport feedback where the packet for the first frame is lost, | 
 |   // expect a retransmission for it. | 
 |   EXPECT_CALL(test.transport(), SendRtp) | 
 |       .WillOnce([&frame1_rtp_sequence_number]( | 
 |                     rtc::ArrayView<const uint8_t> packet, | 
 |                     const PacketOptions& options) { | 
 |         RtpPacket rtp_packet; | 
 |         EXPECT_TRUE(rtp_packet.Parse(packet)); | 
 |         EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); | 
 |  | 
 |         // Retransmitted sequence number from the RTX header should match | 
 |         // the lost packet. | 
 |         rtc::ArrayView<const uint8_t> payload = rtp_packet.payload(); | 
 |         EXPECT_EQ(ByteReader<uint16_t>::ReadBigEndian(payload.data()), | 
 |                   frame1_rtp_sequence_number); | 
 |         return true; | 
 |       }); | 
 |  | 
 |   StreamFeedbackObserver::StreamPacketInfo first_packet_feedback; | 
 |   first_packet_feedback.rtp_sequence_number = frame1_rtp_sequence_number; | 
 |   first_packet_feedback.ssrc = kSsrc1; | 
 |   first_packet_feedback.received = false; | 
 |   first_packet_feedback.is_retransmission = false; | 
 |  | 
 |   StreamFeedbackObserver::StreamPacketInfo second_packet_feedback; | 
 |   second_packet_feedback.rtp_sequence_number = frame2_rtp_sequence_number; | 
 |   second_packet_feedback.ssrc = kSsrc2; | 
 |   second_packet_feedback.received = true; | 
 |   first_packet_feedback.is_retransmission = false; | 
 |  | 
 |   test.router()->OnPacketFeedbackVector( | 
 |       {first_packet_feedback, second_packet_feedback}); | 
 |  | 
 |   // Wait for pacer to run and send the RTX packet. | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, SupportsDependencyDescriptor) { | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); | 
 |   test.SetActiveModules({true}); | 
 |  | 
 |   RtpHeaderExtensionMap extensions; | 
 |   extensions.Register<RtpDependencyDescriptorExtension>( | 
 |       kDependencyDescriptorExtensionId); | 
 |   std::vector<RtpPacket> sent_packets; | 
 |   ON_CALL(test.transport(), SendRtp) | 
 |       .WillByDefault([&](rtc::ArrayView<const uint8_t> packet, | 
 |                          const PacketOptions& options) { | 
 |         sent_packets.emplace_back(&extensions); | 
 |         EXPECT_TRUE(sent_packets.back().Parse(packet)); | 
 |         return true; | 
 |       }); | 
 |  | 
 |   const uint8_t kPayload[1] = {'a'}; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image.SetEncodedData( | 
 |       EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); | 
 |  | 
 |   CodecSpecificInfo codec_specific; | 
 |   codec_specific.codecType = VideoCodecType::kVideoCodecGeneric; | 
 |   codec_specific.template_structure.emplace(); | 
 |   codec_specific.template_structure->num_decode_targets = 1; | 
 |   codec_specific.template_structure->templates = { | 
 |       FrameDependencyTemplate().T(0).Dtis("S"), | 
 |       FrameDependencyTemplate().T(0).Dtis("S").FrameDiffs({2}), | 
 |       FrameDependencyTemplate().T(1).Dtis("D").FrameDiffs({1}), | 
 |   }; | 
 |  | 
 |   // Send two tiny images, mapping to single RTP packets. | 
 |   // Send in key frame. | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   codec_specific.generic_frame_info = | 
 |       GenericFrameInfo::Builder().T(0).Dtis("S").Build(); | 
 |   codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}}; | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |   ASSERT_THAT(sent_packets, SizeIs(1)); | 
 |   EXPECT_TRUE( | 
 |       sent_packets.back().HasExtension<RtpDependencyDescriptorExtension>()); | 
 |  | 
 |   // Send in delta frame. | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameDelta; | 
 |   codec_specific.template_structure = absl::nullopt; | 
 |   codec_specific.generic_frame_info = | 
 |       GenericFrameInfo::Builder().T(1).Dtis("D").Build(); | 
 |   codec_specific.generic_frame_info->encoder_buffers = {{0, true, false}}; | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |   ASSERT_THAT(sent_packets, SizeIs(2)); | 
 |   EXPECT_TRUE( | 
 |       sent_packets.back().HasExtension<RtpDependencyDescriptorExtension>()); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, | 
 |      SupportsDependencyDescriptorForVp8NotProvidedByEncoder) { | 
 |   constexpr uint8_t kPayload[1] = {'a'}; | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); | 
 |   RtpHeaderExtensionMap extensions; | 
 |   extensions.Register<RtpDependencyDescriptorExtension>( | 
 |       kDependencyDescriptorExtensionId); | 
 |   std::vector<RtpPacket> sent_packets; | 
 |   ON_CALL(test.transport(), SendRtp) | 
 |       .WillByDefault( | 
 |           [&](rtc::ArrayView<const uint8_t> packet, const PacketOptions&) { | 
 |             EXPECT_TRUE(sent_packets.emplace_back(&extensions).Parse(packet)); | 
 |             return true; | 
 |           }); | 
 |   test.SetActiveModules({true}); | 
 |  | 
 |   EncodedImage key_frame_image; | 
 |   key_frame_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   key_frame_image.SetEncodedData( | 
 |       EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); | 
 |   CodecSpecificInfo key_frame_info; | 
 |   key_frame_info.codecType = VideoCodecType::kVideoCodecVP8; | 
 |   ASSERT_EQ( | 
 |       test.router()->OnEncodedImage(key_frame_image, &key_frame_info).error, | 
 |       EncodedImageCallback::Result::OK); | 
 |  | 
 |   EncodedImage delta_image; | 
 |   delta_image._frameType = VideoFrameType::kVideoFrameDelta; | 
 |   delta_image.SetEncodedData( | 
 |       EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); | 
 |   CodecSpecificInfo delta_info; | 
 |   delta_info.codecType = VideoCodecType::kVideoCodecVP8; | 
 |   ASSERT_EQ(test.router()->OnEncodedImage(delta_image, &delta_info).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |  | 
 |   test.AdvanceTime(TimeDelta::Millis(123)); | 
 |  | 
 |   DependencyDescriptor key_frame_dd; | 
 |   DependencyDescriptor delta_dd; | 
 |   ASSERT_THAT(sent_packets, SizeIs(2)); | 
 |   EXPECT_TRUE(sent_packets[0].GetExtension<RtpDependencyDescriptorExtension>( | 
 |       /*structure=*/nullptr, &key_frame_dd)); | 
 |   EXPECT_TRUE(sent_packets[1].GetExtension<RtpDependencyDescriptorExtension>( | 
 |       key_frame_dd.attached_structure.get(), &delta_dd)); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, SupportsDependencyDescriptorForVp9) { | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); | 
 |   test.SetActiveModules({true}); | 
 |  | 
 |   RtpHeaderExtensionMap extensions; | 
 |   extensions.Register<RtpDependencyDescriptorExtension>( | 
 |       kDependencyDescriptorExtensionId); | 
 |   std::vector<RtpPacket> sent_packets; | 
 |   ON_CALL(test.transport(), SendRtp) | 
 |       .WillByDefault([&](rtc::ArrayView<const uint8_t> packet, | 
 |                          const PacketOptions& options) { | 
 |         sent_packets.emplace_back(&extensions); | 
 |         EXPECT_TRUE(sent_packets.back().Parse(packet)); | 
 |         return true; | 
 |       }); | 
 |  | 
 |   const uint8_t kPayload[1] = {'a'}; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image.SetEncodedData( | 
 |       EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); | 
 |  | 
 |   CodecSpecificInfo codec_specific; | 
 |   codec_specific.codecType = VideoCodecType::kVideoCodecVP9; | 
 |   codec_specific.template_structure.emplace(); | 
 |   codec_specific.template_structure->num_decode_targets = 2; | 
 |   codec_specific.template_structure->templates = { | 
 |       FrameDependencyTemplate().S(0).Dtis("SS"), | 
 |       FrameDependencyTemplate().S(1).Dtis("-S").FrameDiffs({1}), | 
 |   }; | 
 |  | 
 |   // Send two tiny images, each mapping to single RTP packet. | 
 |   // Send in key frame for the base spatial layer. | 
 |   codec_specific.generic_frame_info = | 
 |       GenericFrameInfo::Builder().S(0).Dtis("SS").Build(); | 
 |   codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}}; | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |   // Send in 2nd spatial layer. | 
 |   codec_specific.template_structure = absl::nullopt; | 
 |   codec_specific.generic_frame_info = | 
 |       GenericFrameInfo::Builder().S(1).Dtis("-S").Build(); | 
 |   codec_specific.generic_frame_info->encoder_buffers = {{0, true, false}, | 
 |                                                         {1, false, true}}; | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |  | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |   ASSERT_THAT(sent_packets, SizeIs(2)); | 
 |   EXPECT_TRUE(sent_packets[0].HasExtension<RtpDependencyDescriptorExtension>()); | 
 |   EXPECT_TRUE(sent_packets[1].HasExtension<RtpDependencyDescriptorExtension>()); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, | 
 |      SupportsDependencyDescriptorForVp9NotProvidedByEncoder) { | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); | 
 |   test.SetActiveModules({true}); | 
 |  | 
 |   RtpHeaderExtensionMap extensions; | 
 |   extensions.Register<RtpDependencyDescriptorExtension>( | 
 |       kDependencyDescriptorExtensionId); | 
 |   std::vector<RtpPacket> sent_packets; | 
 |   ON_CALL(test.transport(), SendRtp) | 
 |       .WillByDefault([&](rtc::ArrayView<const uint8_t> packet, | 
 |                          const PacketOptions& options) { | 
 |         sent_packets.emplace_back(&extensions); | 
 |         EXPECT_TRUE(sent_packets.back().Parse(packet)); | 
 |         return true; | 
 |       }); | 
 |  | 
 |   const uint8_t kPayload[1] = {'a'}; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image._encodedWidth = 320; | 
 |   encoded_image._encodedHeight = 180; | 
 |   encoded_image.SetEncodedData( | 
 |       EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); | 
 |  | 
 |   CodecSpecificInfo codec_specific; | 
 |   codec_specific.codecType = VideoCodecType::kVideoCodecVP9; | 
 |   codec_specific.codecSpecific.VP9.num_spatial_layers = 1; | 
 |   codec_specific.codecSpecific.VP9.temporal_idx = kNoTemporalIdx; | 
 |   codec_specific.codecSpecific.VP9.first_frame_in_picture = true; | 
 |   codec_specific.end_of_picture = true; | 
 |   codec_specific.codecSpecific.VP9.inter_pic_predicted = false; | 
 |  | 
 |   // Send two tiny images, each mapping to single RTP packet. | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |  | 
 |   // Send in 2nd picture. | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameDelta; | 
 |   encoded_image.SetRtpTimestamp(3000); | 
 |   codec_specific.codecSpecific.VP9.inter_pic_predicted = true; | 
 |   codec_specific.codecSpecific.VP9.num_ref_pics = 1; | 
 |   codec_specific.codecSpecific.VP9.p_diff[0] = 1; | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |  | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |   ASSERT_THAT(sent_packets, SizeIs(2)); | 
 |   EXPECT_TRUE(sent_packets[0].HasExtension<RtpDependencyDescriptorExtension>()); | 
 |   EXPECT_TRUE(sent_packets[1].HasExtension<RtpDependencyDescriptorExtension>()); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, GenerateDependecyDescriptorForGenericCodecs) { | 
 |   test::ScopedKeyValueConfig field_trials( | 
 |       "WebRTC-GenericCodecDependencyDescriptor/Enabled/"); | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}, &field_trials); | 
 |   test.SetActiveModules({true}); | 
 |  | 
 |   RtpHeaderExtensionMap extensions; | 
 |   extensions.Register<RtpDependencyDescriptorExtension>( | 
 |       kDependencyDescriptorExtensionId); | 
 |   std::vector<RtpPacket> sent_packets; | 
 |   ON_CALL(test.transport(), SendRtp) | 
 |       .WillByDefault([&](rtc::ArrayView<const uint8_t> packet, | 
 |                          const PacketOptions& options) { | 
 |         sent_packets.emplace_back(&extensions); | 
 |         EXPECT_TRUE(sent_packets.back().Parse(packet)); | 
 |         return true; | 
 |       }); | 
 |  | 
 |   const uint8_t kPayload[1] = {'a'}; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image._encodedWidth = 320; | 
 |   encoded_image._encodedHeight = 180; | 
 |   encoded_image.SetEncodedData( | 
 |       EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); | 
 |  | 
 |   CodecSpecificInfo codec_specific; | 
 |   codec_specific.codecType = VideoCodecType::kVideoCodecGeneric; | 
 |   codec_specific.end_of_picture = true; | 
 |  | 
 |   // Send two tiny images, each mapping to single RTP packet. | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |  | 
 |   // Send in 2nd picture. | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameDelta; | 
 |   encoded_image.SetRtpTimestamp(3000); | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |  | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |   ASSERT_THAT(sent_packets, SizeIs(2)); | 
 |   EXPECT_TRUE(sent_packets[0].HasExtension<RtpDependencyDescriptorExtension>()); | 
 |   EXPECT_TRUE(sent_packets[1].HasExtension<RtpDependencyDescriptorExtension>()); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, SupportsStoppingUsingDependencyDescriptor) { | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}); | 
 |   test.SetActiveModules({true}); | 
 |  | 
 |   RtpHeaderExtensionMap extensions; | 
 |   extensions.Register<RtpDependencyDescriptorExtension>( | 
 |       kDependencyDescriptorExtensionId); | 
 |   std::vector<RtpPacket> sent_packets; | 
 |   ON_CALL(test.transport(), SendRtp) | 
 |       .WillByDefault([&](rtc::ArrayView<const uint8_t> packet, | 
 |                          const PacketOptions& options) { | 
 |         sent_packets.emplace_back(&extensions); | 
 |         EXPECT_TRUE(sent_packets.back().Parse(packet)); | 
 |         return true; | 
 |       }); | 
 |  | 
 |   const uint8_t kPayload[1] = {'a'}; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image.SetEncodedData( | 
 |       EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); | 
 |  | 
 |   CodecSpecificInfo codec_specific; | 
 |   codec_specific.codecType = VideoCodecType::kVideoCodecGeneric; | 
 |   codec_specific.template_structure.emplace(); | 
 |   codec_specific.template_structure->num_decode_targets = 1; | 
 |   codec_specific.template_structure->templates = { | 
 |       FrameDependencyTemplate().T(0).Dtis("S"), | 
 |       FrameDependencyTemplate().T(0).Dtis("S").FrameDiffs({2}), | 
 |       FrameDependencyTemplate().T(1).Dtis("D").FrameDiffs({1}), | 
 |   }; | 
 |  | 
 |   // Send two tiny images, mapping to single RTP packets. | 
 |   // Send in a key frame. | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   codec_specific.generic_frame_info = | 
 |       GenericFrameInfo::Builder().T(0).Dtis("S").Build(); | 
 |   codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}}; | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |   ASSERT_THAT(sent_packets, SizeIs(1)); | 
 |   EXPECT_TRUE( | 
 |       sent_packets.back().HasExtension<RtpDependencyDescriptorExtension>()); | 
 |  | 
 |   // Send in a new key frame without the support for the dependency descriptor. | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   codec_specific.template_structure = absl::nullopt; | 
 |   EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error, | 
 |             EncodedImageCallback::Result::OK); | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |   ASSERT_THAT(sent_packets, SizeIs(2)); | 
 |   EXPECT_FALSE( | 
 |       sent_packets.back().HasExtension<RtpDependencyDescriptorExtension>()); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, CanSetZeroBitrate) { | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {kRtxSsrc1}, kPayloadType, {}); | 
 |   test.router()->OnBitrateUpdated(CreateBitrateAllocationUpdate(0), | 
 |                                   /*framerate*/ 0); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, SimulcastSenderRegistersFrameTransformers) { | 
 |   rtc::scoped_refptr<MockFrameTransformer> transformer = | 
 |       rtc::make_ref_counted<MockFrameTransformer>(); | 
 |  | 
 |   EXPECT_CALL(*transformer, RegisterTransformedFrameSinkCallback(_, kSsrc1)); | 
 |   EXPECT_CALL(*transformer, RegisterTransformedFrameSinkCallback(_, kSsrc2)); | 
 |   RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, | 
 |                                  kPayloadType, {}, nullptr, transformer); | 
 |  | 
 |   EXPECT_CALL(*transformer, UnregisterTransformedFrameSinkCallback(kSsrc1)); | 
 |   EXPECT_CALL(*transformer, UnregisterTransformedFrameSinkCallback(kSsrc2)); | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, OverheadIsSubtractedFromTargetBitrate) { | 
 |   test::ScopedKeyValueConfig field_trials( | 
 |       "WebRTC-Video-UseFrameRateForOverhead/Enabled/"); | 
 |  | 
 |   // TODO(jakobi): RTP header size should not be hard coded. | 
 |   constexpr uint32_t kRtpHeaderSizeBytes = 20; | 
 |   constexpr uint32_t kTransportPacketOverheadBytes = 40; | 
 |   constexpr uint32_t kOverheadPerPacketBytes = | 
 |       kRtpHeaderSizeBytes + kTransportPacketOverheadBytes; | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {}, kPayloadType, {}, &field_trials); | 
 |   test.router()->OnTransportOverheadChanged(kTransportPacketOverheadBytes); | 
 |   test.SetActiveModules({true}); | 
 |  | 
 |   { | 
 |     test.router()->OnBitrateUpdated(CreateBitrateAllocationUpdate(300000), | 
 |                                     /*framerate*/ 15); | 
 |     // 1 packet per frame. | 
 |     EXPECT_EQ(test.router()->GetPayloadBitrateBps(), | 
 |               300000 - kOverheadPerPacketBytes * 8 * 30); | 
 |   } | 
 |   { | 
 |     test.router()->OnBitrateUpdated(CreateBitrateAllocationUpdate(150000), | 
 |                                     /*framerate*/ 15); | 
 |     // 1 packet per frame. | 
 |     EXPECT_EQ(test.router()->GetPayloadBitrateBps(), | 
 |               150000 - kOverheadPerPacketBytes * 8 * 15); | 
 |   } | 
 |   { | 
 |     test.router()->OnBitrateUpdated(CreateBitrateAllocationUpdate(1000000), | 
 |                                     /*framerate*/ 30); | 
 |     // 3 packets per frame. | 
 |     EXPECT_EQ(test.router()->GetPayloadBitrateBps(), | 
 |               1000000 - kOverheadPerPacketBytes * 8 * 30 * 3); | 
 |   } | 
 | } | 
 |  | 
 | TEST(RtpVideoSenderTest, ClearsPendingPacketsOnInactivation) { | 
 |   RtpVideoSenderTestFixture test({kSsrc1}, {kRtxSsrc1}, kPayloadType, {}); | 
 |   test.SetActiveModules({true}); | 
 |  | 
 |   RtpHeaderExtensionMap extensions; | 
 |   extensions.Register<RtpDependencyDescriptorExtension>( | 
 |       kDependencyDescriptorExtensionId); | 
 |   std::vector<RtpPacket> sent_packets; | 
 |   ON_CALL(test.transport(), SendRtp) | 
 |       .WillByDefault([&](rtc::ArrayView<const uint8_t> packet, | 
 |                          const PacketOptions& options) { | 
 |         sent_packets.emplace_back(&extensions); | 
 |         EXPECT_TRUE(sent_packets.back().Parse(packet)); | 
 |         return true; | 
 |       }); | 
 |  | 
 |   // Set a very low bitrate. | 
 |   test.router()->OnBitrateUpdated( | 
 |       CreateBitrateAllocationUpdate(/*rate_bps=*/10'000), | 
 |       /*framerate=*/30); | 
 |  | 
 |   // Create and send a large keyframe. | 
 |   const size_t kImageSizeBytes = 10000; | 
 |   constexpr uint8_t kPayload[kImageSizeBytes] = {'a'}; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image.SetEncodedData( | 
 |       EncodedImageBuffer::Create(kPayload, sizeof(kPayload))); | 
 |   EXPECT_EQ(test.router() | 
 |                 ->OnEncodedImage(encoded_image, /*codec_specific=*/nullptr) | 
 |                 .error, | 
 |             EncodedImageCallback::Result::OK); | 
 |  | 
 |   // Advance time a small amount, check that sent data is only part of the | 
 |   // image. | 
 |   test.AdvanceTime(TimeDelta::Millis(5)); | 
 |   DataSize transmittedPayload = DataSize::Zero(); | 
 |   for (const RtpPacket& packet : sent_packets) { | 
 |     transmittedPayload += DataSize::Bytes(packet.payload_size()); | 
 |     // Make sure we don't see the end of the frame. | 
 |     EXPECT_FALSE(packet.Marker()); | 
 |   } | 
 |   EXPECT_GT(transmittedPayload, DataSize::Zero()); | 
 |   EXPECT_LT(transmittedPayload, DataSize::Bytes(kImageSizeBytes / 3)); | 
 |  | 
 |   // Record the RTP timestamp of the first frame. | 
 |   const uint32_t first_frame_timestamp = sent_packets[0].Timestamp(); | 
 |   sent_packets.clear(); | 
 |  | 
 |   // Disable the sending module and advance time slightly. No packets should be | 
 |   // sent. | 
 |   test.SetActiveModules({false}); | 
 |   test.AdvanceTime(TimeDelta::Millis(20)); | 
 |   EXPECT_TRUE(sent_packets.empty()); | 
 |  | 
 |   // Reactive the send module - any packets should have been removed, so nothing | 
 |   // should be transmitted. | 
 |   test.SetActiveModules({true}); | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |   EXPECT_TRUE(sent_packets.empty()); | 
 |  | 
 |   // Send a new frame. | 
 |   encoded_image.SetRtpTimestamp(3); | 
 |   encoded_image.capture_time_ms_ = 4; | 
 |   EXPECT_EQ(test.router() | 
 |                 ->OnEncodedImage(encoded_image, /*codec_specific=*/nullptr) | 
 |                 .error, | 
 |             EncodedImageCallback::Result::OK); | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |  | 
 |   // Advance time, check we get new packets - but only for the second frame. | 
 |   EXPECT_FALSE(sent_packets.empty()); | 
 |   EXPECT_NE(sent_packets[0].Timestamp(), first_frame_timestamp); | 
 | } | 
 |  | 
 | // Integration test verifying that when retransmission mode is set to | 
 | // kRetransmitBaseLayer,only base layer is retransmitted. | 
 | TEST(RtpVideoSenderTest, RetransmitsBaseLayerOnly) { | 
 |   RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2}, | 
 |                                  kPayloadType, {}); | 
 |   test.SetActiveModules({true, true}); | 
 |  | 
 |   test.router()->SetRetransmissionMode(kRetransmitBaseLayer); | 
 |   constexpr uint8_t kPayload = 'a'; | 
 |   EncodedImage encoded_image; | 
 |   encoded_image.SetRtpTimestamp(1); | 
 |   encoded_image.capture_time_ms_ = 2; | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameKey; | 
 |   encoded_image.SetEncodedData(EncodedImageBuffer::Create(&kPayload, 1)); | 
 |  | 
 |   // Send two tiny images, mapping to two RTP packets. Capture sequence numbers. | 
 |   std::vector<uint16_t> rtp_sequence_numbers; | 
 |   std::vector<uint16_t> transport_sequence_numbers; | 
 |   std::vector<uint16_t> base_sequence_numbers; | 
 |   EXPECT_CALL(test.transport(), SendRtp) | 
 |       .Times(2) | 
 |       .WillRepeatedly([&rtp_sequence_numbers, &transport_sequence_numbers]( | 
 |                           rtc::ArrayView<const uint8_t> packet, | 
 |                           const PacketOptions& options) { | 
 |         RtpPacket rtp_packet; | 
 |         EXPECT_TRUE(rtp_packet.Parse(packet)); | 
 |         rtp_sequence_numbers.push_back(rtp_packet.SequenceNumber()); | 
 |         transport_sequence_numbers.push_back(options.packet_id); | 
 |         return true; | 
 |       }); | 
 |   CodecSpecificInfo key_codec_info; | 
 |   key_codec_info.codecType = kVideoCodecVP8; | 
 |   key_codec_info.codecSpecific.VP8.temporalIdx = 0; | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage( | 
 |                 encoded_image, &key_codec_info).error); | 
 |   encoded_image.SetRtpTimestamp(2); | 
 |   encoded_image.capture_time_ms_ = 3; | 
 |   encoded_image._frameType = VideoFrameType::kVideoFrameDelta; | 
 |   CodecSpecificInfo delta_codec_info; | 
 |   delta_codec_info.codecType = kVideoCodecVP8; | 
 |   delta_codec_info.codecSpecific.VP8.temporalIdx = 1; | 
 |   EXPECT_EQ(EncodedImageCallback::Result::OK, | 
 |             test.router()->OnEncodedImage( | 
 |                 encoded_image, &delta_codec_info).error); | 
 |  | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |  | 
 |   // Construct a NACK message for requesting retransmission of both packet. | 
 |   rtcp::Nack nack; | 
 |   nack.SetMediaSsrc(kSsrc1); | 
 |   nack.SetPacketIds(rtp_sequence_numbers); | 
 |   rtc::Buffer nack_buffer = nack.Build(); | 
 |  | 
 |   std::vector<uint16_t> retransmitted_rtp_sequence_numbers; | 
 |   EXPECT_CALL(test.transport(), SendRtp) | 
 |       .Times(1) | 
 |       .WillRepeatedly([&retransmitted_rtp_sequence_numbers]( | 
 |                           rtc::ArrayView<const uint8_t> packet, | 
 |                           const PacketOptions& options) { | 
 |         RtpPacket rtp_packet; | 
 |         EXPECT_TRUE(rtp_packet.Parse(packet)); | 
 |         EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); | 
 |         // Capture the retransmitted sequence number from the RTX header. | 
 |         rtc::ArrayView<const uint8_t> payload = rtp_packet.payload(); | 
 |         retransmitted_rtp_sequence_numbers.push_back( | 
 |             ByteReader<uint16_t>::ReadBigEndian(payload.data())); | 
 |         return true; | 
 |       }); | 
 |   test.router()->DeliverRtcp(nack_buffer.data(), nack_buffer.size()); | 
 |   test.AdvanceTime(TimeDelta::Millis(33)); | 
 |  | 
 |   // Verify that only base layer packet was retransmitted. | 
 |   std::vector<uint16_t> base_rtp_sequence_numbers(rtp_sequence_numbers.begin(), | 
 |                                rtp_sequence_numbers.begin() + 1); | 
 |   EXPECT_EQ(retransmitted_rtp_sequence_numbers, base_rtp_sequence_numbers); | 
 | } | 
 |  | 
 | }  // namespace webrtc |