@@ -823,3 +823,218 @@ VOID TEST(BasicWorkflowRtcConnTest, ManuallyVerifyForPublisherWithVP9)
823823 // Stop the publisher
824824 publisher->stop ();
825825}
826+
827+ // This test is used to verify the basic workflow of the RTC connection with G.711 PCMU codec.
828+ // It's finished with the help of AI, but each step is manually designed
829+ // and verified. So this is not dominated by AI, but by humanbeing.
830+ VOID TEST (BasicWorkflowRtcConnTest, ManuallyVerifyForPublisherWithG711Pcmu)
831+ {
832+ srs_error_t err;
833+
834+ // Create mock dependencies FIRST (they must outlive the connection)
835+ SrsUniquePtr<MockCircuitBreaker> mock_circuit_breaker (new MockCircuitBreaker ());
836+ SrsUniquePtr<MockConnectionManager> mock_conn_manager (new MockConnectionManager ());
837+ SrsUniquePtr<MockRtcSourceManager> mock_rtc_sources (new MockRtcSourceManager ());
838+ SrsUniquePtr<MockAppConfig> mock_config (new MockAppConfig ());
839+ SrsUniquePtr<MockDtlsCertificate> mock_dtls_certificate (new MockDtlsCertificate ());
840+ SrsUniquePtr<MockSdpFactory> mock_sdp_factory (new MockSdpFactory ());
841+ SrsUniquePtr<MockAppFactoryForRtcConn> mock_app_factory (new MockAppFactoryForRtcConn ());
842+ SrsStreamPublishTokenManager token_manager;
843+
844+ mock_config->rtc_dtls_role_ = " passive" ;
845+ mock_dtls_certificate->fingerprint_ = " test-fingerprint" ;
846+ mock_app_factory->rtc_sources_ = mock_rtc_sources.get ();
847+ mock_app_factory->mock_protocol_utility_ = new MockProtocolUtility (" 192.168.1.100" );
848+ MockRtcSource *mock_rtc_source = new MockRtcSource ();
849+ mock_rtc_sources->mock_source_ = SrsSharedPtr<SrsRtcSource>(mock_rtc_source);
850+
851+ // Create a real ISrsRtcConnection using _srs_app_factory_
852+ MockRtcAsyncTaskExecutor mock_exec;
853+ SrsContextId cid;
854+ cid.set_value (" test-rtc-conn-publisher-g711-pcmu-workflow" );
855+
856+ SrsUniquePtr<ISrsRtcConnection> conn_ptr (_srs_app_factory->create_rtc_connection (&mock_exec, cid));
857+ SrsRtcConnection *conn = dynamic_cast <SrsRtcConnection *>(conn_ptr.get ());
858+ EXPECT_TRUE (conn != NULL );
859+
860+ // Mock the RTC conn, also mock the config in publisher_negotiator_ and player_negotiator_
861+ conn->circuit_breaker_ = mock_circuit_breaker.get ();
862+ conn->conn_manager_ = mock_conn_manager.get ();
863+ conn->rtc_sources_ = mock_rtc_sources.get ();
864+ conn->config_ = mock_config.get ();
865+ conn->dtls_certificate_ = mock_dtls_certificate.get ();
866+ conn->app_factory_ = mock_app_factory.get ();
867+
868+ SrsRtcPublisherNegotiator *pub_neg = dynamic_cast <SrsRtcPublisherNegotiator *>(conn->publisher_negotiator_ );
869+ pub_neg->config_ = mock_config.get ();
870+ SrsRtcPlayerNegotiator *play_neg = dynamic_cast <SrsRtcPlayerNegotiator *>(conn->player_negotiator_ );
871+ play_neg->config_ = mock_config.get ();
872+ play_neg->rtc_sources_ = mock_rtc_sources.get ();
873+
874+ // Create RTC user config for add_publisher with G.711 PCMU codec
875+ SrsUniquePtr<SrsRtcUserConfig> ruc (new SrsRtcUserConfig ());
876+ if (true ) {
877+ srs_freep (ruc->req_ );
878+ ruc->req_ = new MockRtcAsyncCallRequest (" test.vhost" , " live" , " stream1" );
879+ ruc->publish_ = true ;
880+ ruc->dtls_ = true ;
881+ ruc->srtp_ = true ;
882+ ruc->audio_before_video_ = false ;
883+ ruc->acodec_ = " pcmu" ; // Specify PCMU codec
884+
885+ ruc->remote_sdp_str_ = mock_sdp_factory->create_chrome_publisher_offer_with_g711_pcmu ();
886+ HELPER_EXPECT_SUCCESS (ruc->remote_sdp_ .parse (ruc->remote_sdp_str_ ));
887+ }
888+
889+ // Add publisher, which negotiate the SDP and generate local SDP
890+ SrsSdp local_sdp;
891+ local_sdp.session_config_ .dtls_role_ = mock_config->get_rtc_dtls_role (ruc->req_ ->vhost_ );
892+
893+ if (true ) {
894+ HELPER_EXPECT_SUCCESS (conn->add_publisher (ruc.get (), local_sdp));
895+
896+ // Verify publishers and SSRC mappings
897+ EXPECT_TRUE (conn->publishers_ .size () == 1 );
898+ EXPECT_TRUE (conn->publishers_ssrc_map_ .size () == 2 );
899+ EXPECT_TRUE (conn->publishers_ssrc_map_ .find (mock_sdp_factory->audio_ssrc_ ) != conn->publishers_ssrc_map_ .end ());
900+ EXPECT_TRUE (conn->publishers_ssrc_map_ .find (mock_sdp_factory->video_ssrc_ ) != conn->publishers_ssrc_map_ .end ());
901+
902+ // Verify the source stream desription, should have two tracks.
903+ SrsRtcSourceDescription *stream_desc = mock_rtc_sources->mock_source_ ->stream_desc_ ;
904+ EXPECT_TRUE (stream_desc->audio_track_desc_ != NULL );
905+ EXPECT_TRUE (stream_desc->video_track_descs_ .size () == 1 );
906+
907+ // Verify the audio track ssrc and payload type.
908+ EXPECT_TRUE (stream_desc->audio_track_desc_ ->ssrc_ == mock_sdp_factory->audio_ssrc_ );
909+ // PCMU uses payload type 0
910+ EXPECT_TRUE (stream_desc->audio_track_desc_ ->media_ ->pt_ == 0 );
911+
912+ // Verify the codec is PCMU
913+ EXPECT_TRUE (stream_desc->audio_track_desc_ ->media_ ->name_ == " PCMU" );
914+
915+ // Verify the video track ssrc and payload type.
916+ EXPECT_TRUE (stream_desc->video_track_descs_ [0 ]->ssrc_ == mock_sdp_factory->video_ssrc_ );
917+ EXPECT_TRUE (stream_desc->video_track_descs_ [0 ]->media_ ->pt_ == mock_sdp_factory->video_pt_ );
918+
919+ // Verify the local SDP was generated with media information
920+ EXPECT_TRUE (local_sdp.version_ == " 0" );
921+ EXPECT_TRUE (local_sdp.group_policy_ == " BUNDLE" );
922+ EXPECT_TRUE (local_sdp.msids_ .size () == 1 );
923+ EXPECT_TRUE (local_sdp.msids_ [0 ] == " live/stream1" );
924+ EXPECT_TRUE (local_sdp.media_descs_ .size () == 2 );
925+
926+ // First should be audio media desc with PCMU
927+ SrsMediaDesc *audio_desc = &local_sdp.media_descs_ [0 ];
928+ EXPECT_TRUE (audio_desc->type_ == " audio" );
929+ EXPECT_TRUE (audio_desc->recvonly_ );
930+ EXPECT_TRUE (audio_desc->payload_types_ .size () == 1 );
931+ EXPECT_TRUE (audio_desc->payload_types_ [0 ].payload_type_ == 0 );
932+ EXPECT_TRUE (audio_desc->payload_types_ [0 ].encoding_name_ == " PCMU" );
933+ EXPECT_TRUE (audio_desc->payload_types_ [0 ].clock_rate_ == 8000 );
934+
935+ // Second should be video media desc
936+ SrsMediaDesc *video_desc = &local_sdp.media_descs_ [1 ];
937+ EXPECT_TRUE (video_desc->type_ == " video" );
938+ EXPECT_TRUE (video_desc->recvonly_ );
939+ EXPECT_TRUE (video_desc->payload_types_ .size () == 1 );
940+ EXPECT_TRUE (video_desc->payload_types_ [0 ].payload_type_ == mock_sdp_factory->video_pt_ );
941+ EXPECT_TRUE (video_desc->payload_types_ [0 ].encoding_name_ == " H264" );
942+ EXPECT_TRUE (video_desc->payload_types_ [0 ].clock_rate_ == 90000 );
943+ }
944+
945+ // Generate local SDP and setup SDP.
946+ std::string username;
947+ if (true ) {
948+ bool status = true ;
949+ conn->set_all_tracks_status (ruc->req_ ->get_stream_url (), ruc->publish_ , status);
950+
951+ HELPER_EXPECT_SUCCESS (conn->generate_local_sdp (ruc.get (), local_sdp, username));
952+ conn->set_remote_sdp (ruc->remote_sdp_ );
953+ conn->set_local_sdp (local_sdp);
954+ conn->set_state_as_waiting_stun ();
955+
956+ // Verify the local SDP was generated ice pwd
957+ SrsMediaDesc *audio_desc = &local_sdp.media_descs_ [0 ];
958+ EXPECT_TRUE (!audio_desc->session_info_ .ice_pwd_ .empty ());
959+ EXPECT_TRUE (!audio_desc->session_info_ .fingerprint_ .empty ());
960+ EXPECT_TRUE (audio_desc->candidates_ .size () == 1 );
961+ EXPECT_TRUE (audio_desc->candidates_ [0 ].ip_ == " 192.168.1.100" );
962+ EXPECT_TRUE (audio_desc->session_info_ .setup_ == " passive" );
963+
964+ SrsMediaDesc *video_desc = &local_sdp.media_descs_ [1 ];
965+ EXPECT_TRUE (!video_desc->session_info_ .ice_pwd_ .empty ());
966+ EXPECT_TRUE (!video_desc->session_info_ .fingerprint_ .empty ());
967+ EXPECT_TRUE (video_desc->candidates_ .size () == 1 );
968+ EXPECT_TRUE (video_desc->candidates_ [0 ].ip_ == " 192.168.1.100" );
969+ EXPECT_TRUE (video_desc->session_info_ .setup_ == " passive" );
970+
971+ EXPECT_TRUE (local_sdp.session_negotiate_ .dtls_role_ == " passive" );
972+ }
973+
974+ // Initialize the connection
975+ if (true ) {
976+ HELPER_EXPECT_SUCCESS (conn->initialize (ruc->req_ , ruc->dtls_ , ruc->srtp_ , username));
977+ EXPECT_TRUE (conn->nack_enabled_ );
978+
979+ // Create and set publish token
980+ SrsStreamPublishToken *publish_token_raw = NULL ;
981+ HELPER_EXPECT_SUCCESS (token_manager.acquire_token (ruc->req_ , publish_token_raw));
982+ SrsSharedPtr<ISrsStreamPublishToken> publish_token (publish_token_raw);
983+
984+ conn->set_publish_token (publish_token);
985+ EXPECT_TRUE (conn->publish_token_ ->is_acquired ());
986+ }
987+
988+ // DTLS done, start publisher
989+ SrsRtcPublishStream *publisher = NULL ;
990+ if (true ) {
991+ HELPER_EXPECT_SUCCESS (conn->on_dtls_handshake_done ());
992+
993+ // Wait for coroutine to start. Normally it should be ready wait for PLI requests.
994+ srs_usleep (1 * SRS_UTIME_MILLISECONDS);
995+
996+ // Verify the publisher is created and started
997+ EXPECT_TRUE (conn->publishers_ .size () == 1 );
998+ publisher = dynamic_cast <SrsRtcPublishStream *>(conn->publishers_ .begin ()->second );
999+ EXPECT_TRUE (publisher->is_sender_started_ );
1000+ }
1001+
1002+ // Got a RTP audio packet with PCMU payload type.
1003+ for (int i = 0 ; i < 3 ; i++) {
1004+ SrsRtpPacket pkt;
1005+ pkt.header_ .set_ssrc (mock_sdp_factory->audio_ssrc_ );
1006+ pkt.header_ .set_sequence (100 );
1007+ pkt.header_ .set_timestamp (1000 );
1008+ pkt.header_ .set_payload_type (0 ); // PCMU payload type
1009+
1010+ SrsUniquePtr<char []> data (new char [1500 ]);
1011+ SrsBuffer buf (data.get (), 1500 );
1012+ HELPER_EXPECT_SUCCESS (pkt.encode (&buf));
1013+
1014+ HELPER_EXPECT_SUCCESS (conn->on_rtp_cipher (data.get (), buf.pos ()));
1015+ HELPER_EXPECT_SUCCESS (conn->on_rtp_plaintext (data.get (), buf.pos ()));
1016+
1017+ EXPECT_EQ (mock_rtc_source->rtp_audio_count_ , i + 1 );
1018+ }
1019+
1020+ // Got a RTP video packet.
1021+ for (int i = 0 ; i < 3 ; i++) {
1022+ SrsRtpPacket pkt;
1023+ pkt.header_ .set_ssrc (mock_sdp_factory->video_ssrc_ );
1024+ pkt.header_ .set_sequence (100 );
1025+ pkt.header_ .set_timestamp (1000 );
1026+ pkt.header_ .set_payload_type (mock_sdp_factory->video_pt_ );
1027+
1028+ SrsUniquePtr<char []> data (new char [1500 ]);
1029+ SrsBuffer buf (data.get (), 1500 );
1030+ HELPER_EXPECT_SUCCESS (pkt.encode (&buf));
1031+
1032+ HELPER_EXPECT_SUCCESS (conn->on_rtp_cipher (data.get (), buf.pos ()));
1033+ HELPER_EXPECT_SUCCESS (conn->on_rtp_plaintext (data.get (), buf.pos ()));
1034+
1035+ EXPECT_EQ (mock_rtc_source->rtp_video_count_ , i + 1 );
1036+ }
1037+
1038+ // Stop the publisher
1039+ publisher->stop ();
1040+ }
0 commit comments