/** * The MIT License (MIT) * * Copyright (c) 2013-2025 Winlin * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include // This test is used to verify the basic workflow of the RTC connection. // It's finished with the help of AI, but each step is manually designed // and verified. So this is not dominated by AI, but by humanbeing. VOID TEST(BasicWorkflowRtcConnTest, ManuallyVerifyForPlayer) { srs_error_t err; // Create mock dependencies FIRST (they must outlive the connection) SrsUniquePtr mock_circuit_breaker(new MockCircuitBreaker()); SrsUniquePtr mock_conn_manager(new MockConnectionManager()); SrsUniquePtr mock_rtc_sources(new MockRtcSourceManager()); SrsUniquePtr mock_config(new MockAppConfig()); SrsUniquePtr mock_dtls_certificate(new MockDtlsCertificate()); SrsUniquePtr mock_sdp_factory(new MockSdpFactory()); SrsUniquePtr mock_app_factory(new MockAppFactoryForRtcConn()); mock_config->rtc_dtls_role_ = "passive"; mock_dtls_certificate->fingerprint_ = "test-fingerprint"; mock_app_factory->rtc_sources_ = mock_rtc_sources.get(); mock_app_factory->mock_protocol_utility_ = new MockProtocolUtility("192.168.1.100"); MockRtcSource *mock_rtc_source = new MockRtcSource(); mock_rtc_sources->mock_source_ = SrsSharedPtr(mock_rtc_source); // Create a real ISrsRtcConnection using _srs_app_factory_ MockRtcAsyncTaskExecutor mock_exec; SrsContextId cid; cid.set_value("test-rtc-conn-player-workflow"); SrsUniquePtr conn_ptr(_srs_app_factory->create_rtc_connection(&mock_exec, cid)); SrsRtcConnection *conn = dynamic_cast(conn_ptr.get()); EXPECT_TRUE(conn != NULL); // Mock the RTC conn, also mock the config in publisher_negotiator_ and player_negotiator_ conn->circuit_breaker_ = mock_circuit_breaker.get(); conn->conn_manager_ = mock_conn_manager.get(); conn->rtc_sources_ = mock_rtc_sources.get(); conn->config_ = mock_config.get(); conn->dtls_certificate_ = mock_dtls_certificate.get(); conn->app_factory_ = mock_app_factory.get(); SrsRtcPublisherNegotiator *pub_neg = dynamic_cast(conn->publisher_negotiator_); pub_neg->config_ = mock_config.get(); SrsRtcPlayerNegotiator *play_neg = dynamic_cast(conn->player_negotiator_); play_neg->config_ = mock_config.get(); play_neg->rtc_sources_ = mock_rtc_sources.get(); // Create RTC user config for add_player SrsUniquePtr ruc(new SrsRtcUserConfig()); if (true) { srs_freep(ruc->req_); ruc->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream1"); ruc->publish_ = false; ruc->dtls_ = true; ruc->srtp_ = true; ruc->audio_before_video_ = false; ruc->remote_sdp_str_ = mock_sdp_factory->create_chrome_player_offer_with_h264(); HELPER_EXPECT_SUCCESS(ruc->remote_sdp_.parse(ruc->remote_sdp_str_)); } // Add player, which negotiate the SDP and generate local SDP SrsSdp local_sdp; local_sdp.session_config_.dtls_role_ = mock_config->get_rtc_dtls_role(ruc->req_->vhost_); if (true) { HELPER_EXPECT_SUCCESS(conn->add_player(ruc.get(), local_sdp)); // Verify publishers and SSRC mappings EXPECT_TRUE(conn->players_.size() == 1); EXPECT_TRUE(conn->players_ssrc_map_.size() == 2); // Verify the local SDP was generated with media information EXPECT_TRUE(local_sdp.version_ == "0"); EXPECT_TRUE(local_sdp.group_policy_ == "BUNDLE"); EXPECT_TRUE(local_sdp.msids_.size() == 1); EXPECT_TRUE(local_sdp.msids_[0] == "live/stream1"); EXPECT_TRUE(local_sdp.media_descs_.size() == 2); // First should be audio media desc SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0]; EXPECT_TRUE(audio_desc->type_ == "audio"); EXPECT_FALSE(audio_desc->recvonly_); EXPECT_TRUE(audio_desc->payload_types_.size() == 1); EXPECT_TRUE(audio_desc->payload_types_[0].payload_type_ == mock_sdp_factory->audio_pt_); EXPECT_TRUE(audio_desc->payload_types_[0].encoding_name_ == "opus"); EXPECT_TRUE(audio_desc->payload_types_[0].clock_rate_ == 48000); // Second should be video media desc SrsMediaDesc *video_desc = &local_sdp.media_descs_[1]; EXPECT_TRUE(video_desc->type_ == "video"); EXPECT_FALSE(video_desc->recvonly_); EXPECT_TRUE(video_desc->payload_types_.size() == 1); EXPECT_TRUE(video_desc->payload_types_[0].payload_type_ == mock_sdp_factory->video_pt_); EXPECT_TRUE(video_desc->payload_types_[0].encoding_name_ == "H264"); EXPECT_TRUE(video_desc->payload_types_[0].clock_rate_ == 90000); } // Generate local SDP and setup SDP. std::string username; if (true) { bool status = true; conn->set_all_tracks_status(ruc->req_->get_stream_url(), ruc->publish_, status); HELPER_EXPECT_SUCCESS(conn->generate_local_sdp(ruc.get(), local_sdp, username)); conn->set_remote_sdp(ruc->remote_sdp_); conn->set_local_sdp(local_sdp); conn->set_state_as_waiting_stun(); // Verify the local SDP was generated ice pwd SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0]; EXPECT_TRUE(!audio_desc->session_info_.ice_pwd_.empty()); EXPECT_TRUE(!audio_desc->session_info_.fingerprint_.empty()); EXPECT_TRUE(audio_desc->candidates_.size() == 1); EXPECT_TRUE(audio_desc->candidates_[0].ip_ == "192.168.1.100"); EXPECT_TRUE(audio_desc->session_info_.setup_ == "passive"); SrsMediaDesc *video_desc = &local_sdp.media_descs_[1]; EXPECT_TRUE(!video_desc->session_info_.ice_pwd_.empty()); EXPECT_TRUE(!video_desc->session_info_.fingerprint_.empty()); EXPECT_TRUE(video_desc->candidates_.size() == 1); EXPECT_TRUE(video_desc->candidates_[0].ip_ == "192.168.1.100"); EXPECT_TRUE(video_desc->session_info_.setup_ == "passive"); EXPECT_TRUE(local_sdp.session_negotiate_.dtls_role_ == "passive"); } // Initialize the connection if (true) { HELPER_EXPECT_SUCCESS(conn->initialize(ruc->req_, ruc->dtls_, ruc->srtp_, username)); EXPECT_TRUE(conn->nack_enabled_); } // DTLS done, start player consumer if (true) { HELPER_EXPECT_SUCCESS(conn->on_dtls_handshake_done()); // Wait for coroutine to start. Normally it should be ready and stopped at wait for // RTP packets from consumer. srs_usleep(1 * SRS_UTIME_MILLISECONDS); // Verify the consumer is created and started EXPECT_TRUE(conn->players_.size() == 1); SrsRtcPlayStream *player = dynamic_cast(conn->players_.begin()->second); EXPECT_TRUE(player->is_started_); // Stop the player player->stop(); } } // This test is used to verify the basic workflow of the RTC connection. // It's finished with the help of AI, but each step is manually designed // and verified. So this is not dominated by AI, but by humanbeing. VOID TEST(BasicWorkflowRtcConnTest, ManuallyVerifyForPublisher) { srs_error_t err; // Create mock dependencies FIRST (they must outlive the connection) SrsUniquePtr mock_circuit_breaker(new MockCircuitBreaker()); SrsUniquePtr mock_conn_manager(new MockConnectionManager()); SrsUniquePtr mock_rtc_sources(new MockRtcSourceManager()); SrsUniquePtr mock_config(new MockAppConfig()); SrsUniquePtr mock_dtls_certificate(new MockDtlsCertificate()); SrsUniquePtr mock_sdp_factory(new MockSdpFactory()); SrsUniquePtr mock_app_factory(new MockAppFactoryForRtcConn()); SrsStreamPublishTokenManager token_manager; mock_config->rtc_dtls_role_ = "passive"; mock_dtls_certificate->fingerprint_ = "test-fingerprint"; mock_app_factory->rtc_sources_ = mock_rtc_sources.get(); mock_app_factory->mock_protocol_utility_ = new MockProtocolUtility("192.168.1.100"); MockRtcSource *mock_rtc_source = new MockRtcSource(); mock_rtc_sources->mock_source_ = SrsSharedPtr(mock_rtc_source); // Create a real ISrsRtcConnection using _srs_app_factory_ MockRtcAsyncTaskExecutor mock_exec; SrsContextId cid; cid.set_value("test-rtc-conn-publisher-workflow"); SrsUniquePtr conn_ptr(_srs_app_factory->create_rtc_connection(&mock_exec, cid)); SrsRtcConnection *conn = dynamic_cast(conn_ptr.get()); EXPECT_TRUE(conn != NULL); // Mock the RTC conn, also mock the config in publisher_negotiator_ and player_negotiator_ conn->circuit_breaker_ = mock_circuit_breaker.get(); conn->conn_manager_ = mock_conn_manager.get(); conn->rtc_sources_ = mock_rtc_sources.get(); conn->config_ = mock_config.get(); conn->dtls_certificate_ = mock_dtls_certificate.get(); conn->app_factory_ = mock_app_factory.get(); SrsRtcPublisherNegotiator *pub_neg = dynamic_cast(conn->publisher_negotiator_); pub_neg->config_ = mock_config.get(); SrsRtcPlayerNegotiator *play_neg = dynamic_cast(conn->player_negotiator_); play_neg->config_ = mock_config.get(); play_neg->rtc_sources_ = mock_rtc_sources.get(); // Create RTC user config for add_publisher SrsUniquePtr ruc(new SrsRtcUserConfig()); if (true) { srs_freep(ruc->req_); ruc->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream1"); ruc->publish_ = true; ruc->dtls_ = true; ruc->srtp_ = true; ruc->audio_before_video_ = false; ruc->remote_sdp_str_ = mock_sdp_factory->create_chrome_publisher_offer_with_h264(); HELPER_EXPECT_SUCCESS(ruc->remote_sdp_.parse(ruc->remote_sdp_str_)); } // Add publisher, which negotiate the SDP and generate local SDP SrsSdp local_sdp; local_sdp.session_config_.dtls_role_ = mock_config->get_rtc_dtls_role(ruc->req_->vhost_); if (true) { HELPER_EXPECT_SUCCESS(conn->add_publisher(ruc.get(), local_sdp)); // Verify publishers and SSRC mappings EXPECT_TRUE(conn->publishers_.size() == 1); EXPECT_TRUE(conn->publishers_ssrc_map_.size() == 2); EXPECT_TRUE(conn->publishers_ssrc_map_.find(mock_sdp_factory->audio_ssrc_) != conn->publishers_ssrc_map_.end()); EXPECT_TRUE(conn->publishers_ssrc_map_.find(mock_sdp_factory->video_ssrc_) != conn->publishers_ssrc_map_.end()); // Verify the source stream desription, should have two tracks. SrsRtcSourceDescription *stream_desc = mock_rtc_sources->mock_source_->stream_desc_; EXPECT_TRUE(stream_desc->audio_track_desc_ != NULL); EXPECT_TRUE(stream_desc->video_track_descs_.size() == 1); // Verify the audio track ssrc and payload type. EXPECT_TRUE(stream_desc->audio_track_desc_->ssrc_ == mock_sdp_factory->audio_ssrc_); EXPECT_TRUE(stream_desc->audio_track_desc_->media_->pt_ == mock_sdp_factory->audio_pt_); // Verify the video track ssrc and payload type. EXPECT_TRUE(stream_desc->video_track_descs_[0]->ssrc_ == mock_sdp_factory->video_ssrc_); EXPECT_TRUE(stream_desc->video_track_descs_[0]->media_->pt_ == mock_sdp_factory->video_pt_); // Verify the local SDP was generated with media information EXPECT_TRUE(local_sdp.version_ == "0"); EXPECT_TRUE(local_sdp.group_policy_ == "BUNDLE"); EXPECT_TRUE(local_sdp.msids_.size() == 1); EXPECT_TRUE(local_sdp.msids_[0] == "live/stream1"); EXPECT_TRUE(local_sdp.media_descs_.size() == 2); // First should be audio media desc SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0]; EXPECT_TRUE(audio_desc->type_ == "audio"); EXPECT_TRUE(audio_desc->recvonly_); EXPECT_TRUE(audio_desc->payload_types_.size() == 1); EXPECT_TRUE(audio_desc->payload_types_[0].payload_type_ == mock_sdp_factory->audio_pt_); EXPECT_TRUE(audio_desc->payload_types_[0].encoding_name_ == "opus"); EXPECT_TRUE(audio_desc->payload_types_[0].clock_rate_ == 48000); // Second should be video media desc SrsMediaDesc *video_desc = &local_sdp.media_descs_[1]; EXPECT_TRUE(video_desc->type_ == "video"); EXPECT_TRUE(video_desc->recvonly_); EXPECT_TRUE(video_desc->payload_types_.size() == 1); EXPECT_TRUE(video_desc->payload_types_[0].payload_type_ == mock_sdp_factory->video_pt_); EXPECT_TRUE(video_desc->payload_types_[0].encoding_name_ == "H264"); EXPECT_TRUE(video_desc->payload_types_[0].clock_rate_ == 90000); } // Generate local SDP and setup SDP. std::string username; if (true) { bool status = true; conn->set_all_tracks_status(ruc->req_->get_stream_url(), ruc->publish_, status); HELPER_EXPECT_SUCCESS(conn->generate_local_sdp(ruc.get(), local_sdp, username)); conn->set_remote_sdp(ruc->remote_sdp_); conn->set_local_sdp(local_sdp); conn->set_state_as_waiting_stun(); // Verify the local SDP was generated ice pwd SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0]; EXPECT_TRUE(!audio_desc->session_info_.ice_pwd_.empty()); EXPECT_TRUE(!audio_desc->session_info_.fingerprint_.empty()); EXPECT_TRUE(audio_desc->candidates_.size() == 1); EXPECT_TRUE(audio_desc->candidates_[0].ip_ == "192.168.1.100"); EXPECT_TRUE(audio_desc->session_info_.setup_ == "passive"); SrsMediaDesc *video_desc = &local_sdp.media_descs_[1]; EXPECT_TRUE(!video_desc->session_info_.ice_pwd_.empty()); EXPECT_TRUE(!video_desc->session_info_.fingerprint_.empty()); EXPECT_TRUE(video_desc->candidates_.size() == 1); EXPECT_TRUE(video_desc->candidates_[0].ip_ == "192.168.1.100"); EXPECT_TRUE(video_desc->session_info_.setup_ == "passive"); EXPECT_TRUE(local_sdp.session_negotiate_.dtls_role_ == "passive"); } // Initialize the connection if (true) { HELPER_EXPECT_SUCCESS(conn->initialize(ruc->req_, ruc->dtls_, ruc->srtp_, username)); EXPECT_TRUE(conn->nack_enabled_); // Create and set publish token SrsStreamPublishToken *publish_token_raw = NULL; HELPER_EXPECT_SUCCESS(token_manager.acquire_token(ruc->req_, publish_token_raw)); SrsSharedPtr publish_token(publish_token_raw); conn->set_publish_token(publish_token); EXPECT_TRUE(conn->publish_token_->is_acquired()); } // DTLS done, start publisher SrsRtcPublishStream *publisher = NULL; if (true) { HELPER_EXPECT_SUCCESS(conn->on_dtls_handshake_done()); // Wait for coroutine to start. Normally it should be ready wait for PLI requests. srs_usleep(1 * SRS_UTIME_MILLISECONDS); // Verify the publisher is created and started EXPECT_TRUE(conn->publishers_.size() == 1); publisher = dynamic_cast(conn->publishers_.begin()->second); EXPECT_TRUE(publisher->is_sender_started_); } // Got a RTP audio packet. for (int i = 0; i < 3; i++) { SrsRtpPacket pkt; pkt.header_.set_ssrc(mock_sdp_factory->audio_ssrc_); pkt.header_.set_sequence(100); pkt.header_.set_timestamp(1000); pkt.header_.set_payload_type(mock_sdp_factory->audio_pt_); SrsUniquePtr data(new char[1500]); SrsBuffer buf(data.get(), 1500); HELPER_EXPECT_SUCCESS(pkt.encode(&buf)); HELPER_EXPECT_SUCCESS(conn->on_rtp_cipher(data.get(), buf.pos())); HELPER_EXPECT_SUCCESS(conn->on_rtp_plaintext(data.get(), buf.pos())); EXPECT_EQ(mock_rtc_source->rtp_audio_count_, i + 1); } // Got a RTP video packet. for (int i = 0; i < 3; i++) { SrsRtpPacket pkt; pkt.header_.set_ssrc(mock_sdp_factory->video_ssrc_); pkt.header_.set_sequence(100); pkt.header_.set_timestamp(1000); pkt.header_.set_payload_type(mock_sdp_factory->video_pt_); SrsUniquePtr data(new char[1500]); SrsBuffer buf(data.get(), 1500); HELPER_EXPECT_SUCCESS(pkt.encode(&buf)); HELPER_EXPECT_SUCCESS(conn->on_rtp_cipher(data.get(), buf.pos())); HELPER_EXPECT_SUCCESS(conn->on_rtp_plaintext(data.get(), buf.pos())); EXPECT_EQ(mock_rtc_source->rtp_video_count_, i + 1); } // Stop the publisher publisher->stop(); } // This test is used to verify the basic workflow of the RTC connection with AV1 codec. // It's finished with the help of AI, but each step is manually designed // and verified. So this is not dominated by AI, but by humanbeing. VOID TEST(BasicWorkflowRtcConnTest, ManuallyVerifyForPublisherWithAV1) { srs_error_t err; // Create mock dependencies FIRST (they must outlive the connection) SrsUniquePtr mock_circuit_breaker(new MockCircuitBreaker()); SrsUniquePtr mock_conn_manager(new MockConnectionManager()); SrsUniquePtr mock_rtc_sources(new MockRtcSourceManager()); SrsUniquePtr mock_config(new MockAppConfig()); SrsUniquePtr mock_dtls_certificate(new MockDtlsCertificate()); SrsUniquePtr mock_sdp_factory(new MockSdpFactory()); SrsUniquePtr mock_app_factory(new MockAppFactoryForRtcConn()); SrsStreamPublishTokenManager token_manager; mock_config->rtc_dtls_role_ = "passive"; mock_dtls_certificate->fingerprint_ = "test-fingerprint"; mock_app_factory->rtc_sources_ = mock_rtc_sources.get(); mock_app_factory->mock_protocol_utility_ = new MockProtocolUtility("192.168.1.100"); MockRtcSource *mock_rtc_source = new MockRtcSource(); mock_rtc_sources->mock_source_ = SrsSharedPtr(mock_rtc_source); // Create a real ISrsRtcConnection using _srs_app_factory_ MockRtcAsyncTaskExecutor mock_exec; SrsContextId cid; cid.set_value("test-rtc-conn-publisher-av1-workflow"); SrsUniquePtr conn_ptr(_srs_app_factory->create_rtc_connection(&mock_exec, cid)); SrsRtcConnection *conn = dynamic_cast(conn_ptr.get()); EXPECT_TRUE(conn != NULL); // Mock the RTC conn, also mock the config in publisher_negotiator_ and player_negotiator_ conn->circuit_breaker_ = mock_circuit_breaker.get(); conn->conn_manager_ = mock_conn_manager.get(); conn->rtc_sources_ = mock_rtc_sources.get(); conn->config_ = mock_config.get(); conn->dtls_certificate_ = mock_dtls_certificate.get(); conn->app_factory_ = mock_app_factory.get(); SrsRtcPublisherNegotiator *pub_neg = dynamic_cast(conn->publisher_negotiator_); pub_neg->config_ = mock_config.get(); SrsRtcPlayerNegotiator *play_neg = dynamic_cast(conn->player_negotiator_); play_neg->config_ = mock_config.get(); play_neg->rtc_sources_ = mock_rtc_sources.get(); // Create RTC user config for add_publisher with AV1 codec SrsUniquePtr ruc(new SrsRtcUserConfig()); if (true) { srs_freep(ruc->req_); ruc->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream1"); ruc->publish_ = true; ruc->dtls_ = true; ruc->srtp_ = true; ruc->audio_before_video_ = false; ruc->vcodec_ = "av1"; // Specify AV1 codec ruc->remote_sdp_str_ = mock_sdp_factory->create_chrome_publisher_offer_with_av1(); HELPER_EXPECT_SUCCESS(ruc->remote_sdp_.parse(ruc->remote_sdp_str_)); } // Add publisher, which negotiate the SDP and generate local SDP SrsSdp local_sdp; local_sdp.session_config_.dtls_role_ = mock_config->get_rtc_dtls_role(ruc->req_->vhost_); if (true) { HELPER_EXPECT_SUCCESS(conn->add_publisher(ruc.get(), local_sdp)); // Verify publishers and SSRC mappings EXPECT_TRUE(conn->publishers_.size() == 1); EXPECT_TRUE(conn->publishers_ssrc_map_.size() == 2); EXPECT_TRUE(conn->publishers_ssrc_map_.find(mock_sdp_factory->audio_ssrc_) != conn->publishers_ssrc_map_.end()); EXPECT_TRUE(conn->publishers_ssrc_map_.find(mock_sdp_factory->video_ssrc_) != conn->publishers_ssrc_map_.end()); // Verify the source stream desription, should have two tracks. SrsRtcSourceDescription *stream_desc = mock_rtc_sources->mock_source_->stream_desc_; EXPECT_TRUE(stream_desc->audio_track_desc_ != NULL); EXPECT_TRUE(stream_desc->video_track_descs_.size() == 1); // Verify the audio track ssrc and payload type. EXPECT_TRUE(stream_desc->audio_track_desc_->ssrc_ == mock_sdp_factory->audio_ssrc_); EXPECT_TRUE(stream_desc->audio_track_desc_->media_->pt_ == mock_sdp_factory->audio_pt_); // Verify the video track ssrc and payload type. EXPECT_TRUE(stream_desc->video_track_descs_[0]->ssrc_ == mock_sdp_factory->video_ssrc_); // AV1 uses payload type 45 EXPECT_TRUE(stream_desc->video_track_descs_[0]->media_->pt_ == 45); // Verify the codec is AV1 EXPECT_TRUE(stream_desc->video_track_descs_[0]->media_->name_ == "AV1"); // Verify the local SDP was generated with media information EXPECT_TRUE(local_sdp.version_ == "0"); EXPECT_TRUE(local_sdp.group_policy_ == "BUNDLE"); EXPECT_TRUE(local_sdp.msids_.size() == 1); EXPECT_TRUE(local_sdp.msids_[0] == "live/stream1"); EXPECT_TRUE(local_sdp.media_descs_.size() == 2); // First should be audio media desc SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0]; EXPECT_TRUE(audio_desc->type_ == "audio"); EXPECT_TRUE(audio_desc->recvonly_); EXPECT_TRUE(audio_desc->payload_types_.size() == 1); EXPECT_TRUE(audio_desc->payload_types_[0].payload_type_ == mock_sdp_factory->audio_pt_); EXPECT_TRUE(audio_desc->payload_types_[0].encoding_name_ == "opus"); EXPECT_TRUE(audio_desc->payload_types_[0].clock_rate_ == 48000); // Second should be video media desc with AV1 SrsMediaDesc *video_desc = &local_sdp.media_descs_[1]; EXPECT_TRUE(video_desc->type_ == "video"); EXPECT_TRUE(video_desc->recvonly_); EXPECT_TRUE(video_desc->payload_types_.size() == 1); EXPECT_TRUE(video_desc->payload_types_[0].payload_type_ == 45); EXPECT_TRUE(video_desc->payload_types_[0].encoding_name_ == "AV1"); EXPECT_TRUE(video_desc->payload_types_[0].clock_rate_ == 90000); } // Generate local SDP and setup SDP. std::string username; if (true) { bool status = true; conn->set_all_tracks_status(ruc->req_->get_stream_url(), ruc->publish_, status); HELPER_EXPECT_SUCCESS(conn->generate_local_sdp(ruc.get(), local_sdp, username)); conn->set_remote_sdp(ruc->remote_sdp_); conn->set_local_sdp(local_sdp); conn->set_state_as_waiting_stun(); // Verify the local SDP was generated ice pwd SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0]; EXPECT_TRUE(!audio_desc->session_info_.ice_pwd_.empty()); EXPECT_TRUE(!audio_desc->session_info_.fingerprint_.empty()); EXPECT_TRUE(audio_desc->candidates_.size() == 1); EXPECT_TRUE(audio_desc->candidates_[0].ip_ == "192.168.1.100"); EXPECT_TRUE(audio_desc->session_info_.setup_ == "passive"); SrsMediaDesc *video_desc = &local_sdp.media_descs_[1]; EXPECT_TRUE(!video_desc->session_info_.ice_pwd_.empty()); EXPECT_TRUE(!video_desc->session_info_.fingerprint_.empty()); EXPECT_TRUE(video_desc->candidates_.size() == 1); EXPECT_TRUE(video_desc->candidates_[0].ip_ == "192.168.1.100"); EXPECT_TRUE(video_desc->session_info_.setup_ == "passive"); EXPECT_TRUE(local_sdp.session_negotiate_.dtls_role_ == "passive"); } // Initialize the connection if (true) { HELPER_EXPECT_SUCCESS(conn->initialize(ruc->req_, ruc->dtls_, ruc->srtp_, username)); EXPECT_TRUE(conn->nack_enabled_); // Create and set publish token SrsStreamPublishToken *publish_token_raw = NULL; HELPER_EXPECT_SUCCESS(token_manager.acquire_token(ruc->req_, publish_token_raw)); SrsSharedPtr publish_token(publish_token_raw); conn->set_publish_token(publish_token); EXPECT_TRUE(conn->publish_token_->is_acquired()); } // DTLS done, start publisher SrsRtcPublishStream *publisher = NULL; if (true) { HELPER_EXPECT_SUCCESS(conn->on_dtls_handshake_done()); // Wait for coroutine to start. Normally it should be ready wait for PLI requests. srs_usleep(1 * SRS_UTIME_MILLISECONDS); // Verify the publisher is created and started EXPECT_TRUE(conn->publishers_.size() == 1); publisher = dynamic_cast(conn->publishers_.begin()->second); EXPECT_TRUE(publisher->is_sender_started_); } // Got a RTP audio packet. for (int i = 0; i < 3; i++) { SrsRtpPacket pkt; pkt.header_.set_ssrc(mock_sdp_factory->audio_ssrc_); pkt.header_.set_sequence(100); pkt.header_.set_timestamp(1000); pkt.header_.set_payload_type(mock_sdp_factory->audio_pt_); SrsUniquePtr data(new char[1500]); SrsBuffer buf(data.get(), 1500); HELPER_EXPECT_SUCCESS(pkt.encode(&buf)); HELPER_EXPECT_SUCCESS(conn->on_rtp_cipher(data.get(), buf.pos())); HELPER_EXPECT_SUCCESS(conn->on_rtp_plaintext(data.get(), buf.pos())); EXPECT_EQ(mock_rtc_source->rtp_audio_count_, i + 1); } // Got a RTP video packet with AV1 payload type. for (int i = 0; i < 3; i++) { SrsRtpPacket pkt; pkt.header_.set_ssrc(mock_sdp_factory->video_ssrc_); pkt.header_.set_sequence(100); pkt.header_.set_timestamp(1000); pkt.header_.set_payload_type(45); // AV1 payload type SrsUniquePtr data(new char[1500]); SrsBuffer buf(data.get(), 1500); HELPER_EXPECT_SUCCESS(pkt.encode(&buf)); HELPER_EXPECT_SUCCESS(conn->on_rtp_cipher(data.get(), buf.pos())); HELPER_EXPECT_SUCCESS(conn->on_rtp_plaintext(data.get(), buf.pos())); EXPECT_EQ(mock_rtc_source->rtp_video_count_, i + 1); } // Stop the publisher publisher->stop(); } // This test is used to verify the basic workflow of the RTC connection with VP9 codec. // It's finished with the help of AI, but each step is manually designed // and verified. So this is not dominated by AI, but by humanbeing. VOID TEST(BasicWorkflowRtcConnTest, ManuallyVerifyForPublisherWithVP9) { srs_error_t err; // Create mock dependencies FIRST (they must outlive the connection) SrsUniquePtr mock_circuit_breaker(new MockCircuitBreaker()); SrsUniquePtr mock_conn_manager(new MockConnectionManager()); SrsUniquePtr mock_rtc_sources(new MockRtcSourceManager()); SrsUniquePtr mock_config(new MockAppConfig()); SrsUniquePtr mock_dtls_certificate(new MockDtlsCertificate()); SrsUniquePtr mock_sdp_factory(new MockSdpFactory()); SrsUniquePtr mock_app_factory(new MockAppFactoryForRtcConn()); SrsStreamPublishTokenManager token_manager; mock_config->rtc_dtls_role_ = "passive"; mock_dtls_certificate->fingerprint_ = "test-fingerprint"; mock_app_factory->rtc_sources_ = mock_rtc_sources.get(); mock_app_factory->mock_protocol_utility_ = new MockProtocolUtility("192.168.1.100"); MockRtcSource *mock_rtc_source = new MockRtcSource(); mock_rtc_sources->mock_source_ = SrsSharedPtr(mock_rtc_source); // Create a real ISrsRtcConnection using _srs_app_factory_ MockRtcAsyncTaskExecutor mock_exec; SrsContextId cid; cid.set_value("test-rtc-conn-publisher-vp9-workflow"); SrsUniquePtr conn_ptr(_srs_app_factory->create_rtc_connection(&mock_exec, cid)); SrsRtcConnection *conn = dynamic_cast(conn_ptr.get()); EXPECT_TRUE(conn != NULL); // Mock the RTC conn, also mock the config in publisher_negotiator_ and player_negotiator_ conn->circuit_breaker_ = mock_circuit_breaker.get(); conn->conn_manager_ = mock_conn_manager.get(); conn->rtc_sources_ = mock_rtc_sources.get(); conn->config_ = mock_config.get(); conn->dtls_certificate_ = mock_dtls_certificate.get(); conn->app_factory_ = mock_app_factory.get(); SrsRtcPublisherNegotiator *pub_neg = dynamic_cast(conn->publisher_negotiator_); pub_neg->config_ = mock_config.get(); SrsRtcPlayerNegotiator *play_neg = dynamic_cast(conn->player_negotiator_); play_neg->config_ = mock_config.get(); play_neg->rtc_sources_ = mock_rtc_sources.get(); // Create RTC user config for add_publisher with VP9 codec SrsUniquePtr ruc(new SrsRtcUserConfig()); if (true) { srs_freep(ruc->req_); ruc->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream1"); ruc->publish_ = true; ruc->dtls_ = true; ruc->srtp_ = true; ruc->audio_before_video_ = false; ruc->vcodec_ = "vp9"; // Specify VP9 codec ruc->remote_sdp_str_ = mock_sdp_factory->create_chrome_publisher_offer_with_vp9(); HELPER_EXPECT_SUCCESS(ruc->remote_sdp_.parse(ruc->remote_sdp_str_)); } // Add publisher, which negotiate the SDP and generate local SDP SrsSdp local_sdp; local_sdp.session_config_.dtls_role_ = mock_config->get_rtc_dtls_role(ruc->req_->vhost_); if (true) { HELPER_EXPECT_SUCCESS(conn->add_publisher(ruc.get(), local_sdp)); // Verify publishers and SSRC mappings EXPECT_TRUE(conn->publishers_.size() == 1); EXPECT_TRUE(conn->publishers_ssrc_map_.size() == 2); EXPECT_TRUE(conn->publishers_ssrc_map_.find(mock_sdp_factory->audio_ssrc_) != conn->publishers_ssrc_map_.end()); EXPECT_TRUE(conn->publishers_ssrc_map_.find(mock_sdp_factory->video_ssrc_) != conn->publishers_ssrc_map_.end()); // Verify the source stream desription, should have two tracks. SrsRtcSourceDescription *stream_desc = mock_rtc_sources->mock_source_->stream_desc_; EXPECT_TRUE(stream_desc->audio_track_desc_ != NULL); EXPECT_TRUE(stream_desc->video_track_descs_.size() == 1); // Verify the audio track ssrc and payload type. EXPECT_TRUE(stream_desc->audio_track_desc_->ssrc_ == mock_sdp_factory->audio_ssrc_); EXPECT_TRUE(stream_desc->audio_track_desc_->media_->pt_ == mock_sdp_factory->audio_pt_); // Verify the video track ssrc and payload type. EXPECT_TRUE(stream_desc->video_track_descs_[0]->ssrc_ == mock_sdp_factory->video_ssrc_); // VP9 uses payload type 98 EXPECT_TRUE(stream_desc->video_track_descs_[0]->media_->pt_ == 98); // Verify the codec is VP9 EXPECT_TRUE(stream_desc->video_track_descs_[0]->media_->name_ == "VP9"); // Verify the local SDP was generated with media information EXPECT_TRUE(local_sdp.version_ == "0"); EXPECT_TRUE(local_sdp.group_policy_ == "BUNDLE"); EXPECT_TRUE(local_sdp.msids_.size() == 1); EXPECT_TRUE(local_sdp.msids_[0] == "live/stream1"); EXPECT_TRUE(local_sdp.media_descs_.size() == 2); // First should be audio media desc SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0]; EXPECT_TRUE(audio_desc->type_ == "audio"); EXPECT_TRUE(audio_desc->recvonly_); EXPECT_TRUE(audio_desc->payload_types_.size() == 1); EXPECT_TRUE(audio_desc->payload_types_[0].payload_type_ == mock_sdp_factory->audio_pt_); EXPECT_TRUE(audio_desc->payload_types_[0].encoding_name_ == "opus"); EXPECT_TRUE(audio_desc->payload_types_[0].clock_rate_ == 48000); // Second should be video media desc with VP9 SrsMediaDesc *video_desc = &local_sdp.media_descs_[1]; EXPECT_TRUE(video_desc->type_ == "video"); EXPECT_TRUE(video_desc->recvonly_); EXPECT_TRUE(video_desc->payload_types_.size() == 1); EXPECT_TRUE(video_desc->payload_types_[0].payload_type_ == 98); EXPECT_TRUE(video_desc->payload_types_[0].encoding_name_ == "VP9"); EXPECT_TRUE(video_desc->payload_types_[0].clock_rate_ == 90000); } // Generate local SDP and setup SDP. std::string username; if (true) { bool status = true; conn->set_all_tracks_status(ruc->req_->get_stream_url(), ruc->publish_, status); HELPER_EXPECT_SUCCESS(conn->generate_local_sdp(ruc.get(), local_sdp, username)); conn->set_remote_sdp(ruc->remote_sdp_); conn->set_local_sdp(local_sdp); conn->set_state_as_waiting_stun(); // Verify the local SDP was generated ice pwd SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0]; EXPECT_TRUE(!audio_desc->session_info_.ice_pwd_.empty()); EXPECT_TRUE(!audio_desc->session_info_.fingerprint_.empty()); EXPECT_TRUE(audio_desc->candidates_.size() == 1); EXPECT_TRUE(audio_desc->candidates_[0].ip_ == "192.168.1.100"); EXPECT_TRUE(audio_desc->session_info_.setup_ == "passive"); SrsMediaDesc *video_desc = &local_sdp.media_descs_[1]; EXPECT_TRUE(!video_desc->session_info_.ice_pwd_.empty()); EXPECT_TRUE(!video_desc->session_info_.fingerprint_.empty()); EXPECT_TRUE(video_desc->candidates_.size() == 1); EXPECT_TRUE(video_desc->candidates_[0].ip_ == "192.168.1.100"); EXPECT_TRUE(video_desc->session_info_.setup_ == "passive"); EXPECT_TRUE(local_sdp.session_negotiate_.dtls_role_ == "passive"); } // Initialize the connection if (true) { HELPER_EXPECT_SUCCESS(conn->initialize(ruc->req_, ruc->dtls_, ruc->srtp_, username)); EXPECT_TRUE(conn->nack_enabled_); // Create and set publish token SrsStreamPublishToken *publish_token_raw = NULL; HELPER_EXPECT_SUCCESS(token_manager.acquire_token(ruc->req_, publish_token_raw)); SrsSharedPtr publish_token(publish_token_raw); conn->set_publish_token(publish_token); EXPECT_TRUE(conn->publish_token_->is_acquired()); } // DTLS done, start publisher SrsRtcPublishStream *publisher = NULL; if (true) { HELPER_EXPECT_SUCCESS(conn->on_dtls_handshake_done()); // Wait for coroutine to start. Normally it should be ready wait for PLI requests. srs_usleep(1 * SRS_UTIME_MILLISECONDS); // Verify the publisher is created and started EXPECT_TRUE(conn->publishers_.size() == 1); publisher = dynamic_cast(conn->publishers_.begin()->second); EXPECT_TRUE(publisher->is_sender_started_); } // Got a RTP audio packet. for (int i = 0; i < 3; i++) { SrsRtpPacket pkt; pkt.header_.set_ssrc(mock_sdp_factory->audio_ssrc_); pkt.header_.set_sequence(100); pkt.header_.set_timestamp(1000); pkt.header_.set_payload_type(mock_sdp_factory->audio_pt_); SrsUniquePtr data(new char[1500]); SrsBuffer buf(data.get(), 1500); HELPER_EXPECT_SUCCESS(pkt.encode(&buf)); HELPER_EXPECT_SUCCESS(conn->on_rtp_cipher(data.get(), buf.pos())); HELPER_EXPECT_SUCCESS(conn->on_rtp_plaintext(data.get(), buf.pos())); EXPECT_EQ(mock_rtc_source->rtp_audio_count_, i + 1); } // Got a RTP video packet with VP9 payload type. for (int i = 0; i < 3; i++) { SrsRtpPacket pkt; pkt.header_.set_ssrc(mock_sdp_factory->video_ssrc_); pkt.header_.set_sequence(100); pkt.header_.set_timestamp(1000); pkt.header_.set_payload_type(98); // VP9 payload type SrsUniquePtr data(new char[1500]); SrsBuffer buf(data.get(), 1500); HELPER_EXPECT_SUCCESS(pkt.encode(&buf)); HELPER_EXPECT_SUCCESS(conn->on_rtp_cipher(data.get(), buf.pos())); HELPER_EXPECT_SUCCESS(conn->on_rtp_plaintext(data.get(), buf.pos())); EXPECT_EQ(mock_rtc_source->rtp_video_count_, i + 1); } // Stop the publisher publisher->stop(); } // This test is used to verify the basic workflow of the RTC connection with G.711 PCMU codec. // It's finished with the help of AI, but each step is manually designed // and verified. So this is not dominated by AI, but by humanbeing. VOID TEST(BasicWorkflowRtcConnTest, ManuallyVerifyForPublisherWithG711Pcmu) { srs_error_t err; // Create mock dependencies FIRST (they must outlive the connection) SrsUniquePtr mock_circuit_breaker(new MockCircuitBreaker()); SrsUniquePtr mock_conn_manager(new MockConnectionManager()); SrsUniquePtr mock_rtc_sources(new MockRtcSourceManager()); SrsUniquePtr mock_config(new MockAppConfig()); SrsUniquePtr mock_dtls_certificate(new MockDtlsCertificate()); SrsUniquePtr mock_sdp_factory(new MockSdpFactory()); SrsUniquePtr mock_app_factory(new MockAppFactoryForRtcConn()); SrsStreamPublishTokenManager token_manager; mock_config->rtc_dtls_role_ = "passive"; mock_dtls_certificate->fingerprint_ = "test-fingerprint"; mock_app_factory->rtc_sources_ = mock_rtc_sources.get(); mock_app_factory->mock_protocol_utility_ = new MockProtocolUtility("192.168.1.100"); MockRtcSource *mock_rtc_source = new MockRtcSource(); mock_rtc_sources->mock_source_ = SrsSharedPtr(mock_rtc_source); // Create a real ISrsRtcConnection using _srs_app_factory_ MockRtcAsyncTaskExecutor mock_exec; SrsContextId cid; cid.set_value("test-rtc-conn-publisher-g711-pcmu-workflow"); SrsUniquePtr conn_ptr(_srs_app_factory->create_rtc_connection(&mock_exec, cid)); SrsRtcConnection *conn = dynamic_cast(conn_ptr.get()); EXPECT_TRUE(conn != NULL); // Mock the RTC conn, also mock the config in publisher_negotiator_ and player_negotiator_ conn->circuit_breaker_ = mock_circuit_breaker.get(); conn->conn_manager_ = mock_conn_manager.get(); conn->rtc_sources_ = mock_rtc_sources.get(); conn->config_ = mock_config.get(); conn->dtls_certificate_ = mock_dtls_certificate.get(); conn->app_factory_ = mock_app_factory.get(); SrsRtcPublisherNegotiator *pub_neg = dynamic_cast(conn->publisher_negotiator_); pub_neg->config_ = mock_config.get(); SrsRtcPlayerNegotiator *play_neg = dynamic_cast(conn->player_negotiator_); play_neg->config_ = mock_config.get(); play_neg->rtc_sources_ = mock_rtc_sources.get(); // Create RTC user config for add_publisher with G.711 PCMU codec SrsUniquePtr ruc(new SrsRtcUserConfig()); if (true) { srs_freep(ruc->req_); ruc->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream1"); ruc->publish_ = true; ruc->dtls_ = true; ruc->srtp_ = true; ruc->audio_before_video_ = false; ruc->acodec_ = "pcmu"; // Specify PCMU codec ruc->remote_sdp_str_ = mock_sdp_factory->create_chrome_publisher_offer_with_g711_pcmu(); HELPER_EXPECT_SUCCESS(ruc->remote_sdp_.parse(ruc->remote_sdp_str_)); } // Add publisher, which negotiate the SDP and generate local SDP SrsSdp local_sdp; local_sdp.session_config_.dtls_role_ = mock_config->get_rtc_dtls_role(ruc->req_->vhost_); if (true) { HELPER_EXPECT_SUCCESS(conn->add_publisher(ruc.get(), local_sdp)); // Verify publishers and SSRC mappings EXPECT_TRUE(conn->publishers_.size() == 1); EXPECT_TRUE(conn->publishers_ssrc_map_.size() == 2); EXPECT_TRUE(conn->publishers_ssrc_map_.find(mock_sdp_factory->audio_ssrc_) != conn->publishers_ssrc_map_.end()); EXPECT_TRUE(conn->publishers_ssrc_map_.find(mock_sdp_factory->video_ssrc_) != conn->publishers_ssrc_map_.end()); // Verify the source stream desription, should have two tracks. SrsRtcSourceDescription *stream_desc = mock_rtc_sources->mock_source_->stream_desc_; EXPECT_TRUE(stream_desc->audio_track_desc_ != NULL); EXPECT_TRUE(stream_desc->video_track_descs_.size() == 1); // Verify the audio track ssrc and payload type. EXPECT_TRUE(stream_desc->audio_track_desc_->ssrc_ == mock_sdp_factory->audio_ssrc_); // PCMU uses payload type 0 EXPECT_TRUE(stream_desc->audio_track_desc_->media_->pt_ == 0); // Verify the codec is PCMU EXPECT_TRUE(stream_desc->audio_track_desc_->media_->name_ == "PCMU"); // Verify the video track ssrc and payload type. EXPECT_TRUE(stream_desc->video_track_descs_[0]->ssrc_ == mock_sdp_factory->video_ssrc_); EXPECT_TRUE(stream_desc->video_track_descs_[0]->media_->pt_ == mock_sdp_factory->video_pt_); // Verify the local SDP was generated with media information EXPECT_TRUE(local_sdp.version_ == "0"); EXPECT_TRUE(local_sdp.group_policy_ == "BUNDLE"); EXPECT_TRUE(local_sdp.msids_.size() == 1); EXPECT_TRUE(local_sdp.msids_[0] == "live/stream1"); EXPECT_TRUE(local_sdp.media_descs_.size() == 2); // First should be audio media desc with PCMU SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0]; EXPECT_TRUE(audio_desc->type_ == "audio"); EXPECT_TRUE(audio_desc->recvonly_); EXPECT_TRUE(audio_desc->payload_types_.size() == 1); EXPECT_TRUE(audio_desc->payload_types_[0].payload_type_ == 0); EXPECT_TRUE(audio_desc->payload_types_[0].encoding_name_ == "PCMU"); EXPECT_TRUE(audio_desc->payload_types_[0].clock_rate_ == 8000); // Second should be video media desc SrsMediaDesc *video_desc = &local_sdp.media_descs_[1]; EXPECT_TRUE(video_desc->type_ == "video"); EXPECT_TRUE(video_desc->recvonly_); EXPECT_TRUE(video_desc->payload_types_.size() == 1); EXPECT_TRUE(video_desc->payload_types_[0].payload_type_ == mock_sdp_factory->video_pt_); EXPECT_TRUE(video_desc->payload_types_[0].encoding_name_ == "H264"); EXPECT_TRUE(video_desc->payload_types_[0].clock_rate_ == 90000); } // Generate local SDP and setup SDP. std::string username; if (true) { bool status = true; conn->set_all_tracks_status(ruc->req_->get_stream_url(), ruc->publish_, status); HELPER_EXPECT_SUCCESS(conn->generate_local_sdp(ruc.get(), local_sdp, username)); conn->set_remote_sdp(ruc->remote_sdp_); conn->set_local_sdp(local_sdp); conn->set_state_as_waiting_stun(); // Verify the local SDP was generated ice pwd SrsMediaDesc *audio_desc = &local_sdp.media_descs_[0]; EXPECT_TRUE(!audio_desc->session_info_.ice_pwd_.empty()); EXPECT_TRUE(!audio_desc->session_info_.fingerprint_.empty()); EXPECT_TRUE(audio_desc->candidates_.size() == 1); EXPECT_TRUE(audio_desc->candidates_[0].ip_ == "192.168.1.100"); EXPECT_TRUE(audio_desc->session_info_.setup_ == "passive"); SrsMediaDesc *video_desc = &local_sdp.media_descs_[1]; EXPECT_TRUE(!video_desc->session_info_.ice_pwd_.empty()); EXPECT_TRUE(!video_desc->session_info_.fingerprint_.empty()); EXPECT_TRUE(video_desc->candidates_.size() == 1); EXPECT_TRUE(video_desc->candidates_[0].ip_ == "192.168.1.100"); EXPECT_TRUE(video_desc->session_info_.setup_ == "passive"); EXPECT_TRUE(local_sdp.session_negotiate_.dtls_role_ == "passive"); } // Initialize the connection if (true) { HELPER_EXPECT_SUCCESS(conn->initialize(ruc->req_, ruc->dtls_, ruc->srtp_, username)); EXPECT_TRUE(conn->nack_enabled_); // Create and set publish token SrsStreamPublishToken *publish_token_raw = NULL; HELPER_EXPECT_SUCCESS(token_manager.acquire_token(ruc->req_, publish_token_raw)); SrsSharedPtr publish_token(publish_token_raw); conn->set_publish_token(publish_token); EXPECT_TRUE(conn->publish_token_->is_acquired()); } // DTLS done, start publisher SrsRtcPublishStream *publisher = NULL; if (true) { HELPER_EXPECT_SUCCESS(conn->on_dtls_handshake_done()); // Wait for coroutine to start. Normally it should be ready wait for PLI requests. srs_usleep(1 * SRS_UTIME_MILLISECONDS); // Verify the publisher is created and started EXPECT_TRUE(conn->publishers_.size() == 1); publisher = dynamic_cast(conn->publishers_.begin()->second); EXPECT_TRUE(publisher->is_sender_started_); } // Got a RTP audio packet with PCMU payload type. for (int i = 0; i < 3; i++) { SrsRtpPacket pkt; pkt.header_.set_ssrc(mock_sdp_factory->audio_ssrc_); pkt.header_.set_sequence(100); pkt.header_.set_timestamp(1000); pkt.header_.set_payload_type(0); // PCMU payload type SrsUniquePtr data(new char[1500]); SrsBuffer buf(data.get(), 1500); HELPER_EXPECT_SUCCESS(pkt.encode(&buf)); HELPER_EXPECT_SUCCESS(conn->on_rtp_cipher(data.get(), buf.pos())); HELPER_EXPECT_SUCCESS(conn->on_rtp_plaintext(data.get(), buf.pos())); EXPECT_EQ(mock_rtc_source->rtp_audio_count_, i + 1); } // Got a RTP video packet. for (int i = 0; i < 3; i++) { SrsRtpPacket pkt; pkt.header_.set_ssrc(mock_sdp_factory->video_ssrc_); pkt.header_.set_sequence(100); pkt.header_.set_timestamp(1000); pkt.header_.set_payload_type(mock_sdp_factory->video_pt_); SrsUniquePtr data(new char[1500]); SrsBuffer buf(data.get(), 1500); HELPER_EXPECT_SUCCESS(pkt.encode(&buf)); HELPER_EXPECT_SUCCESS(conn->on_rtp_cipher(data.get(), buf.pos())); HELPER_EXPECT_SUCCESS(conn->on_rtp_plaintext(data.get(), buf.pos())); EXPECT_EQ(mock_rtc_source->rtp_video_count_, i + 1); } // Stop the publisher publisher->stop(); }