| /* | 
 |  *  Copyright 2016 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 "pc/track_media_info_map.h" | 
 |  | 
 | #include <stddef.h> | 
 |  | 
 | #include <cstdint> | 
 | #include <initializer_list> | 
 | #include <optional> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "api/make_ref_counted.h" | 
 | #include "api/media_stream_interface.h" | 
 | #include "api/media_types.h" | 
 | #include "api/rtp_parameters.h" | 
 | #include "api/scoped_refptr.h" | 
 | #include "api/test/mock_video_track.h" | 
 | #include "media/base/media_channel.h" | 
 | #include "pc/audio_track.h" | 
 | #include "pc/rtp_receiver.h" | 
 | #include "pc/rtp_sender.h" | 
 | #include "pc/test/fake_video_track_source.h" | 
 | #include "pc/test/mock_rtp_receiver_internal.h" | 
 | #include "pc/test/mock_rtp_sender_internal.h" | 
 | #include "pc/video_track.h" | 
 | #include "rtc_base/thread.h" | 
 | #include "test/gmock.h" | 
 | #include "test/gtest.h" | 
 |  | 
 | using ::testing::ElementsAre; | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | namespace { | 
 |  | 
 | RtpParameters CreateRtpParametersWithSsrcs( | 
 |     std::initializer_list<uint32_t> ssrcs) { | 
 |   RtpParameters params; | 
 |   for (uint32_t ssrc : ssrcs) { | 
 |     RtpEncodingParameters encoding_params; | 
 |     encoding_params.ssrc = ssrc; | 
 |     params.encodings.push_back(encoding_params); | 
 |   } | 
 |   return params; | 
 | } | 
 |  | 
 | rtc::scoped_refptr<MockRtpSenderInternal> CreateMockRtpSender( | 
 |     webrtc::MediaType media_type, | 
 |     std::initializer_list<uint32_t> ssrcs, | 
 |     rtc::scoped_refptr<MediaStreamTrackInterface> track) { | 
 |   uint32_t first_ssrc; | 
 |   if (ssrcs.size()) { | 
 |     first_ssrc = *ssrcs.begin(); | 
 |   } else { | 
 |     first_ssrc = 0; | 
 |   } | 
 |   auto sender = rtc::make_ref_counted<MockRtpSenderInternal>(); | 
 |   EXPECT_CALL(*sender, track()) | 
 |       .WillRepeatedly(::testing::Return(std::move(track))); | 
 |   EXPECT_CALL(*sender, ssrc()).WillRepeatedly(::testing::Return(first_ssrc)); | 
 |   EXPECT_CALL(*sender, media_type()) | 
 |       .WillRepeatedly(::testing::Return(media_type)); | 
 |   EXPECT_CALL(*sender, GetParameters()) | 
 |       .WillRepeatedly(::testing::Return(CreateRtpParametersWithSsrcs(ssrcs))); | 
 |   EXPECT_CALL(*sender, AttachmentId()).WillRepeatedly(::testing::Return(1)); | 
 |   return sender; | 
 | } | 
 |  | 
 | rtc::scoped_refptr<MockRtpReceiverInternal> CreateMockRtpReceiver( | 
 |     webrtc::MediaType media_type, | 
 |     std::initializer_list<uint32_t> ssrcs, | 
 |     rtc::scoped_refptr<MediaStreamTrackInterface> track) { | 
 |   auto receiver = rtc::make_ref_counted<MockRtpReceiverInternal>(); | 
 |   EXPECT_CALL(*receiver, track()) | 
 |       .WillRepeatedly(::testing::Return(std::move(track))); | 
 |   EXPECT_CALL(*receiver, media_type()) | 
 |       .WillRepeatedly(::testing::Return(media_type)); | 
 |   EXPECT_CALL(*receiver, GetParameters()) | 
 |       .WillRepeatedly(::testing::Return(CreateRtpParametersWithSsrcs(ssrcs))); | 
 |   EXPECT_CALL(*receiver, AttachmentId()).WillRepeatedly(::testing::Return(1)); | 
 |   return receiver; | 
 | } | 
 |  | 
 | rtc::scoped_refptr<VideoTrackInterface> CreateVideoTrack( | 
 |     const std::string& id) { | 
 |   return VideoTrack::Create(id, FakeVideoTrackSource::Create(false), | 
 |                             Thread::Current()); | 
 | } | 
 |  | 
 | rtc::scoped_refptr<VideoTrackInterface> CreateMockVideoTrack( | 
 |     const std::string& id) { | 
 |   auto track = MockVideoTrack::Create(); | 
 |   EXPECT_CALL(*track, kind()) | 
 |       .WillRepeatedly(::testing::Return(VideoTrack::kVideoKind)); | 
 |   return track; | 
 | } | 
 |  | 
 | class TrackMediaInfoMapTest : public ::testing::Test { | 
 |  public: | 
 |   TrackMediaInfoMapTest() : TrackMediaInfoMapTest(true) {} | 
 |  | 
 |   explicit TrackMediaInfoMapTest(bool use_real_video_track) | 
 |       : local_audio_track_(AudioTrack::Create("LocalAudioTrack", nullptr)), | 
 |         remote_audio_track_(AudioTrack::Create("RemoteAudioTrack", nullptr)), | 
 |         local_video_track_(use_real_video_track | 
 |                                ? CreateVideoTrack("LocalVideoTrack") | 
 |                                : CreateMockVideoTrack("LocalVideoTrack")), | 
 |         remote_video_track_(use_real_video_track | 
 |                                 ? CreateVideoTrack("RemoteVideoTrack") | 
 |                                 : CreateMockVideoTrack("LocalVideoTrack")) {} | 
 |  | 
 |   void AddRtpSenderWithSsrcs(std::initializer_list<uint32_t> ssrcs, | 
 |                              MediaStreamTrackInterface* local_track) { | 
 |     rtc::scoped_refptr<MockRtpSenderInternal> rtp_sender = CreateMockRtpSender( | 
 |         local_track->kind() == MediaStreamTrackInterface::kAudioKind | 
 |             ? webrtc::MediaType::AUDIO | 
 |             : webrtc::MediaType::VIDEO, | 
 |         ssrcs, rtc::scoped_refptr<MediaStreamTrackInterface>(local_track)); | 
 |     rtp_senders_.push_back(rtp_sender); | 
 |  | 
 |     if (local_track->kind() == MediaStreamTrackInterface::kAudioKind) { | 
 |       cricket::VoiceSenderInfo voice_sender_info; | 
 |       size_t i = 0; | 
 |       for (uint32_t ssrc : ssrcs) { | 
 |         voice_sender_info.local_stats.push_back(cricket::SsrcSenderInfo()); | 
 |         voice_sender_info.local_stats[i++].ssrc = ssrc; | 
 |       } | 
 |       voice_media_info_.senders.push_back(voice_sender_info); | 
 |     } else { | 
 |       cricket::VideoSenderInfo video_sender_info; | 
 |       size_t i = 0; | 
 |       for (uint32_t ssrc : ssrcs) { | 
 |         video_sender_info.local_stats.push_back(cricket::SsrcSenderInfo()); | 
 |         video_sender_info.local_stats[i++].ssrc = ssrc; | 
 |       } | 
 |       video_media_info_.senders.push_back(video_sender_info); | 
 |       video_media_info_.aggregated_senders.push_back(video_sender_info); | 
 |     } | 
 |   } | 
 |  | 
 |   void AddRtpReceiverWithSsrcs(std::initializer_list<uint32_t> ssrcs, | 
 |                                MediaStreamTrackInterface* remote_track) { | 
 |     auto rtp_receiver = CreateMockRtpReceiver( | 
 |         remote_track->kind() == MediaStreamTrackInterface::kAudioKind | 
 |             ? webrtc::MediaType::AUDIO | 
 |             : webrtc::MediaType::VIDEO, | 
 |         ssrcs, rtc::scoped_refptr<MediaStreamTrackInterface>(remote_track)); | 
 |     rtp_receivers_.push_back(rtp_receiver); | 
 |  | 
 |     if (remote_track->kind() == MediaStreamTrackInterface::kAudioKind) { | 
 |       cricket::VoiceReceiverInfo voice_receiver_info; | 
 |       size_t i = 0; | 
 |       for (uint32_t ssrc : ssrcs) { | 
 |         voice_receiver_info.local_stats.push_back(cricket::SsrcReceiverInfo()); | 
 |         voice_receiver_info.local_stats[i++].ssrc = ssrc; | 
 |       } | 
 |       voice_media_info_.receivers.push_back(voice_receiver_info); | 
 |     } else { | 
 |       cricket::VideoReceiverInfo video_receiver_info; | 
 |       size_t i = 0; | 
 |       for (uint32_t ssrc : ssrcs) { | 
 |         video_receiver_info.local_stats.push_back(cricket::SsrcReceiverInfo()); | 
 |         video_receiver_info.local_stats[i++].ssrc = ssrc; | 
 |       } | 
 |       video_media_info_.receivers.push_back(video_receiver_info); | 
 |     } | 
 |   } | 
 |  | 
 |   // Copies the current state of `voice_media_info_` and `video_media_info_` | 
 |   // into the map. | 
 |   void InitializeMap() { | 
 |     map_.Initialize(voice_media_info_, video_media_info_, rtp_senders_, | 
 |                     rtp_receivers_); | 
 |   } | 
 |  | 
 |  private: | 
 |   AutoThread main_thread_; | 
 |   cricket::VoiceMediaInfo voice_media_info_; | 
 |   cricket::VideoMediaInfo video_media_info_; | 
 |  | 
 |  protected: | 
 |   std::vector<rtc::scoped_refptr<RtpSenderInternal>> rtp_senders_; | 
 |   std::vector<rtc::scoped_refptr<RtpReceiverInternal>> rtp_receivers_; | 
 |   TrackMediaInfoMap map_; | 
 |   rtc::scoped_refptr<AudioTrack> local_audio_track_; | 
 |   rtc::scoped_refptr<AudioTrack> remote_audio_track_; | 
 |   rtc::scoped_refptr<VideoTrackInterface> local_video_track_; | 
 |   rtc::scoped_refptr<VideoTrackInterface> remote_video_track_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | TEST_F(TrackMediaInfoMapTest, SingleSenderReceiverPerTrackWithOneSsrc) { | 
 |   AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); | 
 |   AddRtpReceiverWithSsrcs({2}, remote_audio_track_.get()); | 
 |   AddRtpSenderWithSsrcs({3}, local_video_track_.get()); | 
 |   AddRtpReceiverWithSsrcs({4}, remote_video_track_.get()); | 
 |   InitializeMap(); | 
 |   // RTP audio sender -> local audio track | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->senders[0]), | 
 |             local_audio_track_.get()); | 
 |   // RTP audio receiver -> remote audio track | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->receivers[0]), | 
 |             remote_audio_track_.get()); | 
 |   // RTP video sender -> local video track | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->senders[0]), | 
 |             local_video_track_.get()); | 
 |   // RTP video receiver -> remote video track | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->receivers[0]), | 
 |             remote_video_track_.get()); | 
 | } | 
 |  | 
 | TEST_F(TrackMediaInfoMapTest, | 
 |        SingleSenderReceiverPerTrackWithAudioAndVideoUseSameSsrc) { | 
 |   AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); | 
 |   AddRtpReceiverWithSsrcs({2}, remote_audio_track_.get()); | 
 |   AddRtpSenderWithSsrcs({1}, local_video_track_.get()); | 
 |   AddRtpReceiverWithSsrcs({2}, remote_video_track_.get()); | 
 |   InitializeMap(); | 
 |   // RTP audio sender -> local audio track | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->senders[0]), | 
 |             local_audio_track_.get()); | 
 |   // RTP audio receiver -> remote audio track | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->receivers[0]), | 
 |             remote_audio_track_.get()); | 
 |   // RTP video sender -> local video track | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->senders[0]), | 
 |             local_video_track_.get()); | 
 |   // RTP video receiver -> remote video track | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->receivers[0]), | 
 |             remote_video_track_.get()); | 
 | } | 
 |  | 
 | TEST_F(TrackMediaInfoMapTest, SingleMultiSsrcSenderPerTrack) { | 
 |   AddRtpSenderWithSsrcs({1, 2}, local_audio_track_.get()); | 
 |   AddRtpSenderWithSsrcs({3, 4}, local_video_track_.get()); | 
 |   InitializeMap(); | 
 |   // RTP audio senders -> local audio track | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->senders[0]), | 
 |             local_audio_track_.get()); | 
 |   // RTP video senders -> local video track | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->senders[0]), | 
 |             local_video_track_.get()); | 
 | } | 
 |  | 
 | TEST_F(TrackMediaInfoMapTest, MultipleOneSsrcSendersPerTrack) { | 
 |   AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); | 
 |   AddRtpSenderWithSsrcs({2}, local_audio_track_.get()); | 
 |   AddRtpSenderWithSsrcs({3}, local_video_track_.get()); | 
 |   AddRtpSenderWithSsrcs({4}, local_video_track_.get()); | 
 |   InitializeMap(); | 
 |   // RTP audio senders -> local audio track | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->senders[0]), | 
 |             local_audio_track_.get()); | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->senders[1]), | 
 |             local_audio_track_.get()); | 
 |   // RTP video senders -> local video track | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->senders[0]), | 
 |             local_video_track_.get()); | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->senders[1]), | 
 |             local_video_track_.get()); | 
 | } | 
 |  | 
 | TEST_F(TrackMediaInfoMapTest, MultipleMultiSsrcSendersPerTrack) { | 
 |   AddRtpSenderWithSsrcs({1, 2}, local_audio_track_.get()); | 
 |   AddRtpSenderWithSsrcs({3, 4}, local_audio_track_.get()); | 
 |   AddRtpSenderWithSsrcs({5, 6}, local_video_track_.get()); | 
 |   AddRtpSenderWithSsrcs({7, 8}, local_video_track_.get()); | 
 |   InitializeMap(); | 
 |   // RTP audio senders -> local audio track | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->senders[0]), | 
 |             local_audio_track_.get()); | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->senders[1]), | 
 |             local_audio_track_.get()); | 
 |   // RTP video senders -> local video track | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->senders[0]), | 
 |             local_video_track_.get()); | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->senders[1]), | 
 |             local_video_track_.get()); | 
 | } | 
 |  | 
 | // SSRCs can be reused for send and receive in loopback. | 
 | TEST_F(TrackMediaInfoMapTest, SingleSenderReceiverPerTrackWithSsrcNotUnique) { | 
 |   AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); | 
 |   AddRtpReceiverWithSsrcs({1}, remote_audio_track_.get()); | 
 |   AddRtpSenderWithSsrcs({2}, local_video_track_.get()); | 
 |   AddRtpReceiverWithSsrcs({2}, remote_video_track_.get()); | 
 |   InitializeMap(); | 
 |   // RTP audio senders -> local audio track | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->senders[0]), | 
 |             local_audio_track_.get()); | 
 |   // RTP audio receiver -> remote audio track | 
 |   EXPECT_EQ(map_.GetAudioTrack(map_.voice_media_info()->receivers[0]), | 
 |             remote_audio_track_.get()); | 
 |   // RTP video senders -> local video track | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->senders[0]), | 
 |             local_video_track_.get()); | 
 |   // RTP video receiver -> remote video track | 
 |   EXPECT_EQ(map_.GetVideoTrack(map_.video_media_info()->receivers[0]), | 
 |             remote_video_track_.get()); | 
 | } | 
 |  | 
 | TEST_F(TrackMediaInfoMapTest, SsrcLookupFunction) { | 
 |   AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); | 
 |   AddRtpReceiverWithSsrcs({2}, remote_audio_track_.get()); | 
 |   AddRtpSenderWithSsrcs({3}, local_video_track_.get()); | 
 |   AddRtpReceiverWithSsrcs({4}, remote_video_track_.get()); | 
 |   InitializeMap(); | 
 |   EXPECT_TRUE(map_.GetVoiceSenderInfoBySsrc(1)); | 
 |   EXPECT_TRUE(map_.GetVoiceReceiverInfoBySsrc(2)); | 
 |   EXPECT_TRUE(map_.GetVideoSenderInfoBySsrc(3)); | 
 |   EXPECT_TRUE(map_.GetVideoReceiverInfoBySsrc(4)); | 
 |   EXPECT_FALSE(map_.GetVoiceSenderInfoBySsrc(2)); | 
 |   EXPECT_FALSE(map_.GetVoiceSenderInfoBySsrc(1024)); | 
 | } | 
 |  | 
 | TEST_F(TrackMediaInfoMapTest, GetAttachmentIdByTrack) { | 
 |   AddRtpSenderWithSsrcs({1}, local_audio_track_.get()); | 
 |   InitializeMap(); | 
 |   EXPECT_EQ(rtp_senders_[0]->AttachmentId(), | 
 |             map_.GetAttachmentIdByTrack(local_audio_track_.get())); | 
 |   EXPECT_EQ(std::nullopt, | 
 |             map_.GetAttachmentIdByTrack(local_video_track_.get())); | 
 | } | 
 |  | 
 | }  // namespace webrtc |