/* * Copyright 2017 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 #include "api/audio_codecs/builtin_audio_decoder_factory.h" #include "api/audio_codecs/builtin_audio_encoder_factory.h" #include "media/base/fakemediaengine.h" #include "ortc/ortcfactory.h" #include "ortc/testrtpparameters.h" #include "p2p/base/fakepackettransport.h" #include "pc/test/fakevideotracksource.h" #include "rtc_base/gunit.h" namespace webrtc { // This test uses an individual RtpSender using only the public interface, and // verifies that its behaves as designed at an API level. Also tests that // parameters are applied to the audio/video engines as expected. Network and // media interfaces are faked to isolate what's being tested. // // This test shouldn't result any any actual media being sent. That sort of // test should go in ortcfactory_integrationtest.cc. class OrtcRtpSenderTest : public testing::Test { public: OrtcRtpSenderTest() : fake_packet_transport_("fake") { // Need to set the fake packet transport to writable, in order to test that // the "send" flag is applied to the media engine based on the encoding // |active| flag. fake_packet_transport_.SetWritable(true); fake_media_engine_ = new cricket::FakeMediaEngine(); // Note: This doesn't need to use fake network classes, since we already // use FakePacketTransport. auto ortc_factory_result = OrtcFactory::Create( nullptr, nullptr, nullptr, nullptr, nullptr, std::unique_ptr(fake_media_engine_), CreateBuiltinAudioEncoderFactory(), CreateBuiltinAudioDecoderFactory()); ortc_factory_ = ortc_factory_result.MoveValue(); RtpTransportParameters parameters; parameters.rtcp.mux = true; auto rtp_transport_result = ortc_factory_->CreateRtpTransport( parameters, &fake_packet_transport_, nullptr, nullptr); rtp_transport_ = rtp_transport_result.MoveValue(); } protected: rtc::scoped_refptr CreateAudioTrack( const std::string& id) { return ortc_factory_->CreateAudioTrack(id, nullptr); } rtc::scoped_refptr CreateVideoTrack( const std::string& id) { return rtc::scoped_refptr( ortc_factory_->CreateVideoTrack(id, FakeVideoTrackSource::Create())); } // Owned by |ortc_factory_|. cricket::FakeMediaEngine* fake_media_engine_; rtc::FakePacketTransport fake_packet_transport_; std::unique_ptr ortc_factory_; std::unique_ptr rtp_transport_; }; TEST_F(OrtcRtpSenderTest, GetAndSetTrack) { // Test GetTrack with a sender constructed with a track. auto audio_track = CreateAudioTrack("audio"); auto audio_sender_result = ortc_factory_->CreateRtpSender(audio_track, rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); EXPECT_EQ(audio_track, audio_sender->GetTrack()); // Test GetTrack after SetTrack. auto video_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); auto video_track = CreateVideoTrack("video1"); EXPECT_TRUE(video_sender->SetTrack(video_track).ok()); EXPECT_EQ(video_track, video_sender->GetTrack()); video_track = CreateVideoTrack("video2"); EXPECT_TRUE(video_sender->SetTrack(video_track).ok()); EXPECT_EQ(video_track, video_sender->GetTrack()); } // Test that track can be set when previously unset, even after Send has been // called. TEST_F(OrtcRtpSenderTest, SetTrackWhileSending) { auto audio_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); EXPECT_TRUE(audio_sender->Send(MakeMinimalOpusParameters()).ok()); EXPECT_TRUE(audio_sender->SetTrack(CreateAudioTrack("audio")).ok()); auto video_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); EXPECT_TRUE(video_sender->Send(MakeMinimalVp8Parameters()).ok()); EXPECT_TRUE(video_sender->SetTrack(CreateVideoTrack("video")).ok()); } // Test that track can be changed mid-sending. Differs from the above test in // that the track is set and being changed, rather than unset and being set for // the first time. TEST_F(OrtcRtpSenderTest, ChangeTrackWhileSending) { auto audio_sender_result = ortc_factory_->CreateRtpSender( CreateAudioTrack("audio1"), rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); EXPECT_TRUE(audio_sender->Send(MakeMinimalOpusParameters()).ok()); EXPECT_TRUE(audio_sender->SetTrack(CreateAudioTrack("audio2")).ok()); auto video_sender_result = ortc_factory_->CreateRtpSender( CreateVideoTrack("video1"), rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); EXPECT_TRUE(video_sender->Send(MakeMinimalVp8Parameters()).ok()); EXPECT_TRUE(video_sender->SetTrack(CreateVideoTrack("video2")).ok()); } // Test that track can be set to null while sending. TEST_F(OrtcRtpSenderTest, UnsetTrackWhileSending) { auto audio_sender_result = ortc_factory_->CreateRtpSender( CreateAudioTrack("audio"), rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); EXPECT_TRUE(audio_sender->Send(MakeMinimalOpusParameters()).ok()); EXPECT_TRUE(audio_sender->SetTrack(nullptr).ok()); auto video_sender_result = ortc_factory_->CreateRtpSender( CreateVideoTrack("video"), rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); EXPECT_TRUE(video_sender->Send(MakeMinimalVp8Parameters()).ok()); EXPECT_TRUE(video_sender->SetTrack(nullptr).ok()); } // Shouldn't be able to set an audio track on a video sender or vice versa. TEST_F(OrtcRtpSenderTest, SetTrackOfWrongKindFails) { auto audio_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, audio_sender->SetTrack(CreateVideoTrack("video")).type()); auto video_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, video_sender->SetTrack(CreateAudioTrack("audio")).type()); } // Currently SetTransport isn't supported. When it is, replace this test with a // test/tests for it. TEST_F(OrtcRtpSenderTest, SetTransportFails) { rtc::FakePacketTransport fake_packet_transport("another_transport"); RtpTransportParameters parameters; parameters.rtcp.mux = true; auto rtp_transport_result = ortc_factory_->CreateRtpTransport( parameters, &fake_packet_transport, nullptr, nullptr); auto rtp_transport = rtp_transport_result.MoveValue(); auto sender_result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto sender = sender_result.MoveValue(); EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION, sender->SetTransport(rtp_transport.get()).type()); } TEST_F(OrtcRtpSenderTest, GetTransport) { auto result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); EXPECT_EQ(rtp_transport_.get(), result.value()->GetTransport()); } // Test that "Send" causes the expected parameters to be applied to the media // engine level, for an audio sender. TEST_F(OrtcRtpSenderTest, SendAppliesAudioParametersToMediaEngine) { auto audio_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); // First, create parameters with all the bells and whistles. RtpParameters parameters; RtpCodecParameters opus_codec; opus_codec.name = "opus"; opus_codec.kind = cricket::MEDIA_TYPE_AUDIO; opus_codec.payload_type = 120; opus_codec.clock_rate.emplace(48000); opus_codec.num_channels.emplace(2); opus_codec.parameters["minptime"] = "10"; opus_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::TRANSPORT_CC); parameters.codecs.push_back(std::move(opus_codec)); // Add two codecs, expecting the first to be used. // TODO(deadbeef): Once "codec_payload_type" is supported, use it to select a // codec that's not at the top of the list. RtpCodecParameters isac_codec; isac_codec.name = "ISAC"; isac_codec.kind = cricket::MEDIA_TYPE_AUDIO; isac_codec.payload_type = 110; isac_codec.clock_rate.emplace(16000); parameters.codecs.push_back(std::move(isac_codec)); RtpEncodingParameters encoding; encoding.ssrc.emplace(0xdeadbeef); encoding.max_bitrate_bps.emplace(20000); parameters.encodings.push_back(std::move(encoding)); parameters.header_extensions.emplace_back( "urn:ietf:params:rtp-hdrext:ssrc-audio-level", 3); EXPECT_TRUE(audio_sender->Send(parameters).ok()); // Now verify that the parameters were applied to the fake media engine layer // that exists below BaseChannel. cricket::FakeVoiceMediaChannel* fake_voice_channel = fake_media_engine_->GetVoiceChannel(0); ASSERT_NE(nullptr, fake_voice_channel); EXPECT_TRUE(fake_voice_channel->sending()); // Verify codec parameters. ASSERT_GT(fake_voice_channel->send_codecs().size(), 0u); const cricket::AudioCodec& top_codec = fake_voice_channel->send_codecs()[0]; EXPECT_EQ("opus", top_codec.name); EXPECT_EQ(120, top_codec.id); EXPECT_EQ(48000, top_codec.clockrate); EXPECT_EQ(2u, top_codec.channels); ASSERT_NE(top_codec.params.end(), top_codec.params.find("minptime")); EXPECT_EQ("10", top_codec.params.at("minptime")); // Verify encoding parameters. EXPECT_EQ(20000, fake_voice_channel->max_bps()); ASSERT_EQ(1u, fake_voice_channel->send_streams().size()); const cricket::StreamParams& send_stream = fake_voice_channel->send_streams()[0]; EXPECT_EQ(1u, send_stream.ssrcs.size()); EXPECT_EQ(0xdeadbeef, send_stream.first_ssrc()); // Verify header extensions. ASSERT_EQ(1u, fake_voice_channel->send_extensions().size()); const RtpExtension& extension = fake_voice_channel->send_extensions()[0]; EXPECT_EQ("urn:ietf:params:rtp-hdrext:ssrc-audio-level", extension.uri); EXPECT_EQ(3, extension.id); } // Test that "Send" causes the expected parameters to be applied to the media // engine level, for a video sender. TEST_F(OrtcRtpSenderTest, SendAppliesVideoParametersToMediaEngine) { auto video_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); // First, create parameters with all the bells and whistles. RtpParameters parameters; RtpCodecParameters vp8_codec; vp8_codec.name = "VP8"; vp8_codec.kind = cricket::MEDIA_TYPE_VIDEO; vp8_codec.payload_type = 99; // Try a couple types of feedback params. "Generic NACK" is a bit of a // special case, so test it here. vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR); vp8_codec.rtcp_feedback.emplace_back(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::GENERIC_NACK); parameters.codecs.push_back(std::move(vp8_codec)); RtpCodecParameters vp8_rtx_codec; vp8_rtx_codec.name = "rtx"; vp8_rtx_codec.kind = cricket::MEDIA_TYPE_VIDEO; vp8_rtx_codec.payload_type = 100; vp8_rtx_codec.parameters["apt"] = "99"; parameters.codecs.push_back(std::move(vp8_rtx_codec)); // Add two codecs, expecting the first to be used. // TODO(deadbeef): Once "codec_payload_type" is supported, use it to select a // codec that's not at the top of the list. RtpCodecParameters vp9_codec; vp9_codec.name = "VP9"; vp9_codec.kind = cricket::MEDIA_TYPE_VIDEO; vp9_codec.payload_type = 102; parameters.codecs.push_back(std::move(vp9_codec)); RtpCodecParameters vp9_rtx_codec; vp9_rtx_codec.name = "rtx"; vp9_rtx_codec.kind = cricket::MEDIA_TYPE_VIDEO; vp9_rtx_codec.payload_type = 103; vp9_rtx_codec.parameters["apt"] = "102"; parameters.codecs.push_back(std::move(vp9_rtx_codec)); RtpEncodingParameters encoding; encoding.ssrc.emplace(0xdeadbeef); encoding.rtx.emplace(0xbaadfeed); encoding.max_bitrate_bps.emplace(99999); parameters.encodings.push_back(std::move(encoding)); parameters.header_extensions.emplace_back("urn:3gpp:video-orientation", 4); parameters.header_extensions.emplace_back( "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay", 6); EXPECT_TRUE(video_sender->Send(parameters).ok()); // Now verify that the parameters were applied to the fake media engine layer // that exists below BaseChannel. cricket::FakeVideoMediaChannel* fake_video_channel = fake_media_engine_->GetVideoChannel(0); ASSERT_NE(nullptr, fake_video_channel); EXPECT_TRUE(fake_video_channel->sending()); // Verify codec parameters. ASSERT_GE(fake_video_channel->send_codecs().size(), 2u); const cricket::VideoCodec& top_codec = fake_video_channel->send_codecs()[0]; EXPECT_EQ("VP8", top_codec.name); EXPECT_EQ(99, top_codec.id); EXPECT_TRUE(top_codec.feedback_params.Has({"ccm", "fir"})); EXPECT_TRUE(top_codec.feedback_params.Has(cricket::FeedbackParam("nack"))); const cricket::VideoCodec& rtx_codec = fake_video_channel->send_codecs()[1]; EXPECT_EQ("rtx", rtx_codec.name); EXPECT_EQ(100, rtx_codec.id); ASSERT_NE(rtx_codec.params.end(), rtx_codec.params.find("apt")); EXPECT_EQ("99", rtx_codec.params.at("apt")); // Verify encoding parameters. EXPECT_EQ(99999, fake_video_channel->max_bps()); ASSERT_EQ(1u, fake_video_channel->send_streams().size()); const cricket::StreamParams& send_stream = fake_video_channel->send_streams()[0]; EXPECT_EQ(2u, send_stream.ssrcs.size()); EXPECT_EQ(0xdeadbeef, send_stream.first_ssrc()); uint32_t rtx_ssrc = 0u; EXPECT_TRUE(send_stream.GetFidSsrc(send_stream.first_ssrc(), &rtx_ssrc)); EXPECT_EQ(0xbaadfeed, rtx_ssrc); // Verify header extensions. ASSERT_EQ(2u, fake_video_channel->send_extensions().size()); const RtpExtension& extension1 = fake_video_channel->send_extensions()[0]; EXPECT_EQ("urn:3gpp:video-orientation", extension1.uri); EXPECT_EQ(4, extension1.id); const RtpExtension& extension2 = fake_video_channel->send_extensions()[1]; EXPECT_EQ("http://www.webrtc.org/experiments/rtp-hdrext/playout-delay", extension2.uri); EXPECT_EQ(6, extension2.id); } // Ensure that when primary or RTX SSRCs are left unset, they're generated // automatically. TEST_F(OrtcRtpSenderTest, SendGeneratesSsrcsWhenEmpty) { auto audio_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); RtpParameters parameters = MakeMinimalOpusParametersWithNoSsrc(); // Default RTX parameters, with no SSRC. parameters.encodings[0].rtx.emplace(); EXPECT_TRUE(audio_sender->Send(parameters).ok()); cricket::FakeVoiceMediaChannel* fake_voice_channel = fake_media_engine_->GetVoiceChannel(0); ASSERT_NE(nullptr, fake_voice_channel); ASSERT_EQ(1u, fake_voice_channel->send_streams().size()); const cricket::StreamParams& audio_send_stream = fake_voice_channel->send_streams()[0]; EXPECT_NE(0u, audio_send_stream.first_ssrc()); uint32_t rtx_ssrc = 0u; EXPECT_TRUE( audio_send_stream.GetFidSsrc(audio_send_stream.first_ssrc(), &rtx_ssrc)); EXPECT_NE(0u, rtx_ssrc); EXPECT_NE(audio_send_stream.first_ssrc(), rtx_ssrc); auto video_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); parameters = MakeMinimalVp8ParametersWithNoSsrc(); // Default RTX parameters, with no SSRC. parameters.encodings[0].rtx.emplace(); EXPECT_TRUE(video_sender->Send(parameters).ok()); cricket::FakeVideoMediaChannel* fake_video_channel = fake_media_engine_->GetVideoChannel(0); ASSERT_NE(nullptr, fake_video_channel); ASSERT_EQ(1u, fake_video_channel->send_streams().size()); const cricket::StreamParams& video_send_stream = fake_video_channel->send_streams()[0]; EXPECT_NE(0u, video_send_stream.first_ssrc()); rtx_ssrc = 0u; EXPECT_TRUE( video_send_stream.GetFidSsrc(video_send_stream.first_ssrc(), &rtx_ssrc)); EXPECT_NE(0u, rtx_ssrc); EXPECT_NE(video_send_stream.first_ssrc(), rtx_ssrc); EXPECT_NE(video_send_stream.first_ssrc(), audio_send_stream.first_ssrc()); } // Test changing both the send codec and SSRC at the same time, and verify that // the new parameters are applied to the media engine level. TEST_F(OrtcRtpSenderTest, CallingSendTwiceChangesParameters) { auto audio_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); EXPECT_TRUE( audio_sender->Send(MakeMinimalOpusParametersWithSsrc(0x11111111)).ok()); EXPECT_TRUE( audio_sender->Send(MakeMinimalIsacParametersWithSsrc(0x22222222)).ok()); cricket::FakeVoiceMediaChannel* fake_voice_channel = fake_media_engine_->GetVoiceChannel(0); ASSERT_NE(nullptr, fake_voice_channel); ASSERT_GT(fake_voice_channel->send_codecs().size(), 0u); EXPECT_EQ("ISAC", fake_voice_channel->send_codecs()[0].name); ASSERT_EQ(1u, fake_voice_channel->send_streams().size()); EXPECT_EQ(0x22222222u, fake_voice_channel->send_streams()[0].first_ssrc()); auto video_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); EXPECT_TRUE( video_sender->Send(MakeMinimalVp8ParametersWithSsrc(0x33333333)).ok()); EXPECT_TRUE( video_sender->Send(MakeMinimalVp9ParametersWithSsrc(0x44444444)).ok()); cricket::FakeVideoMediaChannel* fake_video_channel = fake_media_engine_->GetVideoChannel(0); ASSERT_NE(nullptr, fake_video_channel); ASSERT_GT(fake_video_channel->send_codecs().size(), 0u); EXPECT_EQ("VP9", fake_video_channel->send_codecs()[0].name); ASSERT_EQ(1u, fake_video_channel->send_streams().size()); EXPECT_EQ(0x44444444u, fake_video_channel->send_streams()[0].first_ssrc()); } // Ensure that if the |active| flag of RtpEncodingParameters is set to false, // sending stops at the media engine level. TEST_F(OrtcRtpSenderTest, DeactivatingEncodingStopsSending) { auto audio_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); RtpParameters parameters = MakeMinimalOpusParameters(); EXPECT_TRUE(audio_sender->Send(parameters).ok()); // Expect "sending" flag to initially be true. cricket::FakeVoiceMediaChannel* fake_voice_channel = fake_media_engine_->GetVoiceChannel(0); ASSERT_NE(nullptr, fake_voice_channel); EXPECT_TRUE(fake_voice_channel->sending()); // Deactivate encoding and expect it to change to false. parameters.encodings[0].active = false; EXPECT_TRUE(audio_sender->Send(parameters).ok()); EXPECT_FALSE(fake_voice_channel->sending()); // Try the same thing for video now. auto video_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); parameters = MakeMinimalVp8Parameters(); EXPECT_TRUE(video_sender->Send(parameters).ok()); cricket::FakeVideoMediaChannel* fake_video_channel = fake_media_engine_->GetVideoChannel(0); ASSERT_NE(nullptr, fake_video_channel); EXPECT_TRUE(fake_video_channel->sending()); parameters.encodings[0].active = false; EXPECT_TRUE(video_sender->Send(parameters).ok()); EXPECT_FALSE(fake_video_channel->sending()); } // Ensure that calling Send with an empty list of encodings causes send streams // at the media engine level to be cleared. TEST_F(OrtcRtpSenderTest, CallingSendWithEmptyEncodingsClearsSendStreams) { auto audio_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); RtpParameters parameters = MakeMinimalOpusParameters(); EXPECT_TRUE(audio_sender->Send(parameters).ok()); parameters.encodings.clear(); EXPECT_TRUE(audio_sender->Send(parameters).ok()); cricket::FakeVoiceMediaChannel* fake_voice_channel = fake_media_engine_->GetVoiceChannel(0); ASSERT_NE(nullptr, fake_voice_channel); EXPECT_TRUE(fake_voice_channel->send_streams().empty()); auto video_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); parameters = MakeMinimalVp8Parameters(); EXPECT_TRUE(video_sender->Send(parameters).ok()); parameters.encodings.clear(); EXPECT_TRUE(video_sender->Send(parameters).ok()); cricket::FakeVideoMediaChannel* fake_video_channel = fake_media_engine_->GetVideoChannel(0); ASSERT_NE(nullptr, fake_video_channel); EXPECT_TRUE(fake_video_channel->send_streams().empty()); } // These errors should be covered by rtpparametersconversion_unittest.cc, but // we should at least test that those errors are propogated from calls to Send, // with a few examples. TEST_F(OrtcRtpSenderTest, SendReturnsErrorOnInvalidParameters) { auto result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto sender = result.MoveValue(); // NACK feedback missing message type. RtpParameters invalid_feedback = MakeMinimalVp8Parameters(); invalid_feedback.codecs[0].rtcp_feedback.emplace_back(RtcpFeedbackType::NACK); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, sender->Send(invalid_feedback).type()); // Negative payload type. RtpParameters invalid_pt = MakeMinimalVp8Parameters(); invalid_pt.codecs[0].payload_type = -1; EXPECT_EQ(RTCErrorType::INVALID_RANGE, sender->Send(invalid_pt).type()); // Duplicate codec payload types. RtpParameters duplicate_payload_types = MakeMinimalVp8Parameters(); duplicate_payload_types.codecs.push_back(duplicate_payload_types.codecs[0]); duplicate_payload_types.codecs.back().name = "VP9"; EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, sender->Send(duplicate_payload_types).type()); } // Two senders using the same transport shouldn't be able to use the same // payload type to refer to different codecs, same header extension IDs to // refer to different extensions, or same SSRC. TEST_F(OrtcRtpSenderTest, SendReturnsErrorOnIdConflicts) { auto audio_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto video_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); auto video_sender = video_sender_result.MoveValue(); // First test payload type conflict. RtpParameters audio_parameters = MakeMinimalOpusParameters(); RtpParameters video_parameters = MakeMinimalVp8Parameters(); audio_parameters.codecs[0].payload_type = 100; video_parameters.codecs[0].payload_type = 100; EXPECT_TRUE(audio_sender->Send(audio_parameters).ok()); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, video_sender->Send(video_parameters).type()); // Test header extension ID conflict. video_parameters.codecs[0].payload_type = 110; audio_parameters.header_extensions.emplace_back("foo", 4); video_parameters.header_extensions.emplace_back("bar", 4); EXPECT_TRUE(audio_sender->Send(audio_parameters).ok()); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, video_sender->Send(video_parameters).type()); // Test SSRC conflict. Have an RTX SSRC that conflicts with a primary SSRC // for extra challenge. video_parameters.header_extensions[0].uri = "foo"; audio_parameters.encodings[0].ssrc.emplace(0xdeadbeef); video_parameters.encodings[0].ssrc.emplace(0xabbaabba); video_parameters.encodings[0].rtx.emplace(0xdeadbeef); EXPECT_TRUE(audio_sender->Send(audio_parameters).ok()); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, video_sender->Send(video_parameters).type()); // Sanity check that parameters can be set if the conflicts are all resolved. video_parameters.encodings[0].rtx->ssrc.emplace(0xbaadf00d); EXPECT_TRUE(video_sender->Send(video_parameters).ok()); } // Ensure that deleting a sender causes send streams at the media engine level // to be cleared. TEST_F(OrtcRtpSenderTest, DeletingSenderClearsSendStreams) { auto audio_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); EXPECT_TRUE(audio_sender->Send(MakeMinimalOpusParameters()).ok()); // Also create an audio receiver, to prevent the voice channel from being // completely deleted. auto audio_receiver_result = ortc_factory_->CreateRtpReceiver( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto audio_receiver = audio_receiver_result.MoveValue(); EXPECT_TRUE(audio_receiver->Receive(MakeMinimalOpusParameters()).ok()); audio_sender.reset(nullptr); cricket::FakeVoiceMediaChannel* fake_voice_channel = fake_media_engine_->GetVoiceChannel(0); ASSERT_NE(nullptr, fake_voice_channel); EXPECT_TRUE(fake_voice_channel->send_streams().empty()); auto video_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); EXPECT_TRUE(video_sender->Send(MakeMinimalVp8Parameters()).ok()); // Also create an video receiver, to prevent the video channel from being // completely deleted. auto video_receiver_result = ortc_factory_->CreateRtpReceiver( cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); auto video_receiver = video_receiver_result.MoveValue(); EXPECT_TRUE(video_receiver->Receive(MakeMinimalVp8Parameters()).ok()); video_sender.reset(nullptr); cricket::FakeVideoMediaChannel* fake_video_channel = fake_media_engine_->GetVideoChannel(0); ASSERT_NE(nullptr, fake_video_channel); EXPECT_TRUE(fake_video_channel->send_streams().empty()); } // If Send hasn't been called, GetParameters should return empty parameters. TEST_F(OrtcRtpSenderTest, GetDefaultParameters) { auto result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); EXPECT_EQ(RtpParameters(), result.value()->GetParameters()); result = ortc_factory_->CreateRtpSender(cricket::MEDIA_TYPE_VIDEO, rtp_transport_.get()); EXPECT_EQ(RtpParameters(), result.value()->GetParameters()); } // Test that GetParameters returns the last parameters passed into Send, along // with the implementation-default values filled in where they were left unset. TEST_F(OrtcRtpSenderTest, GetParametersReturnsLastSetParametersWithDefaultsFilled) { auto audio_sender_result = ortc_factory_->CreateRtpSender( CreateAudioTrack("audio"), rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); RtpParameters opus_parameters = MakeMinimalOpusParameters(); EXPECT_TRUE(audio_sender->Send(opus_parameters).ok()); EXPECT_EQ(opus_parameters, audio_sender->GetParameters()); RtpParameters isac_parameters = MakeMinimalIsacParameters(); // Sanity check that num_channels actually is left unset. ASSERT_FALSE(isac_parameters.codecs[0].num_channels); EXPECT_TRUE(audio_sender->Send(isac_parameters).ok()); // Should be filled with a default "num channels" of 1. // TODO(deadbeef): This should actually default to 2 for some codecs. Update // this test once that's implemented. isac_parameters.codecs[0].num_channels.emplace(1); EXPECT_EQ(isac_parameters, audio_sender->GetParameters()); auto video_sender_result = ortc_factory_->CreateRtpSender( CreateVideoTrack("video"), rtp_transport_.get()); auto video_sender = video_sender_result.MoveValue(); RtpParameters vp8_parameters = MakeMinimalVp8Parameters(); // Sanity check that clock_rate actually is left unset. EXPECT_TRUE(video_sender->Send(vp8_parameters).ok()); // Should be filled with a default clock rate of 90000. vp8_parameters.codecs[0].clock_rate.emplace(90000); EXPECT_EQ(vp8_parameters, video_sender->GetParameters()); RtpParameters vp9_parameters = MakeMinimalVp9Parameters(); // Sanity check that clock_rate actually is left unset. EXPECT_TRUE(video_sender->Send(vp9_parameters).ok()); // Should be filled with a default clock rate of 90000. vp9_parameters.codecs[0].clock_rate.emplace(90000); EXPECT_EQ(vp9_parameters, video_sender->GetParameters()); } TEST_F(OrtcRtpSenderTest, GetKind) { // Construct one sender from the "kind" enum and another from a track. auto audio_sender_result = ortc_factory_->CreateRtpSender( cricket::MEDIA_TYPE_AUDIO, rtp_transport_.get()); auto video_sender_result = ortc_factory_->CreateRtpSender( CreateVideoTrack("video"), rtp_transport_.get()); auto audio_sender = audio_sender_result.MoveValue(); auto video_sender = video_sender_result.MoveValue(); EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, audio_sender->GetKind()); EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, video_sender->GetKind()); } } // namespace webrtc