diff --git a/trunk/src/app/srs_app_stream_bridge.cpp b/trunk/src/app/srs_app_stream_bridge.cpp index eefcd50f1..8afbd81a1 100644 --- a/trunk/src/app/srs_app_stream_bridge.cpp +++ b/trunk/src/app/srs_app_stream_bridge.cpp @@ -372,6 +372,7 @@ SrsRtcBridge::~SrsRtcBridge() srs_freep(frame_builder_); #endif rtmp_target_ = NULL; + initialized_ = false; } void SrsRtcBridge::enable_rtc2rtmp(SrsSharedPtr rtmp_target) @@ -388,6 +389,11 @@ srs_error_t SrsRtcBridge::initialize(ISrsRequest *r) { srs_error_t err = srs_success; + if (initialized_) { + return err; + } + initialized_ = true; + srs_freep(req_); req_ = r->copy(); diff --git a/trunk/src/app/srs_app_stream_bridge.hpp b/trunk/src/app/srs_app_stream_bridge.hpp index 7257e7c52..16a626c11 100644 --- a/trunk/src/app/srs_app_stream_bridge.hpp +++ b/trunk/src/app/srs_app_stream_bridge.hpp @@ -195,6 +195,8 @@ SRS_DECLARE_PRIVATE: // clang-format on #endif // The Source bridge, bridge stream to other source. SrsSharedPtr rtmp_target_; + // To avoid initialize multiple times, we use this flag. + bool initialized_; public: SrsRtcBridge(); diff --git a/trunk/src/utest/srs_utest_ai08.cpp b/trunk/src/utest/srs_utest_ai08.cpp index 130381dae..467d0fa96 100644 --- a/trunk/src/utest/srs_utest_ai08.cpp +++ b/trunk/src/utest/srs_utest_ai08.cpp @@ -3833,7 +3833,7 @@ VOID TEST(RtcFrameBuilderTest, TranscodeAudio_Success) HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC)); // Replace the audio transcoder with our mock - MockAudioTranscoder *mock_transcoder = new MockAudioTranscoder(); + MockAudioTranscoderForUtest *mock_transcoder = new MockAudioTranscoderForUtest(); mock_transcoder->set_output_packets(2); // Mock transcoder will output 2 packets // Access private member through friendship (utests have access to private members) @@ -3880,7 +3880,7 @@ VOID TEST(RtcFrameBuilderTest, TranscodeAudio_TranscoderError) HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC)); // Replace the audio transcoder with our mock - MockAudioTranscoder *mock_transcoder = new MockAudioTranscoder(); + MockAudioTranscoderForUtest *mock_transcoder = new MockAudioTranscoderForUtest(); srs_error_t mock_error = srs_error_new(ERROR_RTC_RTP_MUXER, "mock transcoder error"); mock_transcoder->set_transcode_error(mock_error); @@ -3925,7 +3925,7 @@ VOID TEST(RtcFrameBuilderTest, TranscodeAudio_FrameTargetError) HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC)); // Replace the audio transcoder with our mock - MockAudioTranscoder *mock_transcoder = new MockAudioTranscoder(); + MockAudioTranscoderForUtest *mock_transcoder = new MockAudioTranscoderForUtest(); mock_transcoder->set_output_packets(1); // Mock transcoder will output 1 packet builder.audio_transcoder_ = mock_transcoder; @@ -3972,7 +3972,7 @@ VOID TEST(RtcFrameBuilderTest, TranscodeAudio_MultipleOutputPackets) HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC)); // Replace the audio transcoder with our mock - MockAudioTranscoder *mock_transcoder = new MockAudioTranscoder(); + MockAudioTranscoderForUtest *mock_transcoder = new MockAudioTranscoderForUtest(); mock_transcoder->set_output_packets(5); // Mock transcoder will output 5 packets builder.audio_transcoder_ = mock_transcoder; @@ -4018,7 +4018,7 @@ VOID TEST(RtcFrameBuilderTest, TranscodeAudio_NoOutputPackets) HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC)); // Replace the audio transcoder with our mock - MockAudioTranscoder *mock_transcoder = new MockAudioTranscoder(); + MockAudioTranscoderForUtest *mock_transcoder = new MockAudioTranscoderForUtest(); mock_transcoder->set_output_packets(0); // Mock transcoder will output 0 packets builder.audio_transcoder_ = mock_transcoder; @@ -4062,7 +4062,7 @@ VOID TEST(RtcFrameBuilderTest, TranscodeAudio_FrameTargetErrorOnTranscodedFrame) HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC)); // Replace the audio transcoder with our mock - MockAudioTranscoder *mock_transcoder = new MockAudioTranscoder(); + MockAudioTranscoderForUtest *mock_transcoder = new MockAudioTranscoderForUtest(); mock_transcoder->set_output_packets(3); // Mock transcoder will output 3 packets builder.audio_transcoder_ = mock_transcoder; @@ -4133,7 +4133,7 @@ VOID TEST(RtcFrameBuilderTest, TranscodeAudio_SpecificCodePath) HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC)); // Replace the audio transcoder with our mock - MockAudioTranscoder *mock_transcoder = new MockAudioTranscoder(); + MockAudioTranscoderForUtest *mock_transcoder = new MockAudioTranscoderForUtest(); // Set up mock transcoder to output packets with specific timestamps and sample data const char sample_data[] = {0x21, 0x10, 0x04, 0x60, (char)0x8C}; // Mock AAC data @@ -4194,7 +4194,7 @@ VOID TEST(RtcFrameBuilderTest, TranscodeAudio_ErrorInTranscoderLoop) HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdAAC, SrsVideoCodecIdAVC)); // Replace the audio transcoder with our mock - MockAudioTranscoder *mock_transcoder = new MockAudioTranscoder(); + MockAudioTranscoderForUtest *mock_transcoder = new MockAudioTranscoderForUtest(); mock_transcoder->set_output_packets(3); // Mock transcoder will output 3 packets builder.audio_transcoder_ = mock_transcoder; diff --git a/trunk/src/utest/srs_utest_ai09.cpp b/trunk/src/utest/srs_utest_ai09.cpp index 03c5498ab..5542d06a0 100644 --- a/trunk/src/utest/srs_utest_ai09.cpp +++ b/trunk/src/utest/srs_utest_ai09.cpp @@ -50,7 +50,7 @@ void MockRtcFrameTarget::reset() srs_freep(frame_error_); } -MockAudioTranscoder::MockAudioTranscoder() +MockAudioTranscoderForUtest::MockAudioTranscoderForUtest() { transcode_error_ = srs_success; should_output_packets_ = false; @@ -58,13 +58,13 @@ MockAudioTranscoder::MockAudioTranscoder() aac_header_len_ = 0; } -MockAudioTranscoder::~MockAudioTranscoder() +MockAudioTranscoderForUtest::~MockAudioTranscoderForUtest() { reset(); srs_freepa(aac_header_data_); } -srs_error_t MockAudioTranscoder::initialize(SrsAudioCodecId from, SrsAudioCodecId to, int channels, int sample_rate, int bit_rate) +srs_error_t MockAudioTranscoderForUtest::initialize(SrsAudioCodecId from, SrsAudioCodecId to, int channels, int sample_rate, int bit_rate) { // Create default AAC header for testing if (!aac_header_data_) { @@ -76,7 +76,7 @@ srs_error_t MockAudioTranscoder::initialize(SrsAudioCodecId from, SrsAudioCodecI return srs_success; } -srs_error_t MockAudioTranscoder::transcode(SrsParsedAudioPacket *in, std::vector &outs) +srs_error_t MockAudioTranscoderForUtest::transcode(SrsParsedAudioPacket *in, std::vector &outs) { if (transcode_error_ != srs_success) { return srs_error_copy(transcode_error_); @@ -105,7 +105,7 @@ srs_error_t MockAudioTranscoder::transcode(SrsParsedAudioPacket *in, std::vector return srs_success; } -void MockAudioTranscoder::free_frames(std::vector &frames) +void MockAudioTranscoderForUtest::free_frames(std::vector &frames) { for (std::vector::iterator it = frames.begin(); it != frames.end(); ++it) { SrsParsedAudioPacket *p = *it; @@ -119,13 +119,13 @@ void MockAudioTranscoder::free_frames(std::vector &frame } } -void MockAudioTranscoder::aac_codec_header(uint8_t **data, int *len) +void MockAudioTranscoderForUtest::aac_codec_header(uint8_t **data, int *len) { *data = aac_header_data_; *len = aac_header_len_; } -void MockAudioTranscoder::reset() +void MockAudioTranscoderForUtest::reset() { srs_freep(transcode_error_); @@ -141,7 +141,7 @@ void MockAudioTranscoder::reset() should_output_packets_ = false; } -void MockAudioTranscoder::set_output_packets(int count, const char *sample_data, int sample_size) +void MockAudioTranscoderForUtest::set_output_packets(int count, const char *sample_data, int sample_size) { reset(); should_output_packets_ = true; @@ -167,7 +167,7 @@ void MockAudioTranscoder::set_output_packets(int count, const char *sample_data, } } -void MockAudioTranscoder::set_transcode_error(srs_error_t err) +void MockAudioTranscoderForUtest::set_transcode_error(srs_error_t err) { srs_freep(transcode_error_); transcode_error_ = srs_error_copy(err); diff --git a/trunk/src/utest/srs_utest_ai09.hpp b/trunk/src/utest/srs_utest_ai09.hpp index df79788ec..e61905081 100644 --- a/trunk/src/utest/srs_utest_ai09.hpp +++ b/trunk/src/utest/srs_utest_ai09.hpp @@ -39,7 +39,7 @@ public: }; // Mock audio transcoder for testing SrsRtcFrameBuilder::transcode_audio -class MockAudioTranscoder : public ISrsAudioTranscoder +class MockAudioTranscoderForUtest : public ISrsAudioTranscoder { public: // Control behavior @@ -50,8 +50,8 @@ public: int aac_header_len_; public: - MockAudioTranscoder(); - virtual ~MockAudioTranscoder(); + MockAudioTranscoderForUtest(); + virtual ~MockAudioTranscoderForUtest(); public: // ISrsAudioTranscoder interface diff --git a/trunk/src/utest/srs_utest_manual_app_rtc2rtmp.cpp b/trunk/src/utest/srs_utest_manual_app_rtc2rtmp.cpp index 615afd157..f996b5586 100644 --- a/trunk/src/utest/srs_utest_manual_app_rtc2rtmp.cpp +++ b/trunk/src/utest/srs_utest_manual_app_rtc2rtmp.cpp @@ -1409,7 +1409,7 @@ VOID TEST(Rtc2RtmpConvertTest, PacketAudio_ThreeAudioPackets) HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdOpus, SrsVideoCodecIdAVC)); // Replace the audio transcoder with our mock to avoid FFmpeg issues - MockAudioTranscoder *mock_transcoder = new MockAudioTranscoder(); + MockAudioTranscoderForUtest *mock_transcoder = new MockAudioTranscoderForUtest(); mock_transcoder->set_output_packets(1); // Each input packet produces 1 output packet // Access private member through friendship (utests have access to private members) @@ -1459,7 +1459,7 @@ VOID TEST(Rtc2RtmpConvertTest, PacketAudio_ReorderingAudioPackets) HELPER_EXPECT_SUCCESS(builder.initialize(req.get(), SrsAudioCodecIdOpus, SrsVideoCodecIdAVC)); // Replace the audio transcoder with our mock to avoid FFmpeg issues - MockAudioTranscoder *mock_transcoder = new MockAudioTranscoder(); + MockAudioTranscoderForUtest *mock_transcoder = new MockAudioTranscoderForUtest(); mock_transcoder->set_output_packets(1); // Each input packet produces 1 output packet // Access private member through friendship (utests have access to private members) diff --git a/trunk/src/utest/srs_utest_manual_mock.cpp b/trunk/src/utest/srs_utest_manual_mock.cpp index a52ac05e1..fb3f4a9e4 100644 --- a/trunk/src/utest/srs_utest_manual_mock.cpp +++ b/trunk/src/utest/srs_utest_manual_mock.cpp @@ -2483,6 +2483,31 @@ void MockOriginHub::set_on_forwarder_start_error(srs_error_t err) on_forwarder_start_error_ = srs_error_copy(err); } +// MockAudioCache implementation +MockAudioCache::MockAudioCache() +{ + process_packet_count_ = 0; +} + +MockAudioCache::~MockAudioCache() +{ +} + +srs_error_t MockAudioCache::process_packet(SrsRtpPacket *src, std::vector &ready_packets) +{ + process_packet_count_++; + + // Copy the packet. + SrsRtpPacket *copy = src->copy(); + ready_packets.push_back(copy); + + return srs_success; +} + +void MockAudioCache::clear_all() +{ +} + // Mock ISrsBasicRtmpClient implementation MockRtmpClient::MockRtmpClient() { @@ -2644,3 +2669,45 @@ void MockRtmpClient::set_url(std::string url) { url_ = url; } + +MockAudioTranscoder::MockAudioTranscoder() +{ + transcode_count_ = 0; +} + +MockAudioTranscoder::~MockAudioTranscoder() +{ +} + +srs_error_t MockAudioTranscoder::initialize(SrsAudioCodecId from, SrsAudioCodecId to, int channels, int sample_rate, int bit_rate) +{ + return srs_success; +} + +srs_error_t MockAudioTranscoder::transcode(SrsParsedAudioPacket *in, std::vector &outs) +{ + transcode_count_++; + + SrsParsedAudioPacket *out = in->copy(); + output_packets_.push_back(out); + outs.push_back(out); + + return srs_success; +} + +void MockAudioTranscoder::free_frames(std::vector &frames) +{ + for (std::vector::iterator it = frames.begin(); it != frames.end(); ++it) { + SrsParsedAudioPacket *p = *it; + srs_freep(p); + } +} + +void MockAudioTranscoder::aac_codec_header(uint8_t **data, int *len) +{ + int size = aac_header_.size(); + uint8_t *copy = new uint8_t[size]; + memcpy(copy, aac_header_.data(), size); + *data = copy; + *len = size; +} diff --git a/trunk/src/utest/srs_utest_manual_mock.hpp b/trunk/src/utest/srs_utest_manual_mock.hpp index d99e2b5c8..20a9736a2 100644 --- a/trunk/src/utest/srs_utest_manual_mock.hpp +++ b/trunk/src/utest/srs_utest_manual_mock.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1111,6 +1112,21 @@ public: void set_on_dvr_request_sh_error(srs_error_t err); }; +// Mock audio cache for ISrsRtcFrameBuilderAudioPacketCache +class MockAudioCache : public ISrsRtcFrameBuilderAudioPacketCache +{ +public: + int process_packet_count_; + +public: + MockAudioCache(); + virtual ~MockAudioCache(); + +public: + virtual srs_error_t process_packet(SrsRtpPacket *src, std::vector &ready_packets); + virtual void clear_all(); +}; + // Mock ISrsBasicRtmpClient for testing SrsForwarder class MockRtmpClient : public ISrsBasicRtmpClient { @@ -1164,4 +1180,23 @@ public: virtual void set_url(std::string url); }; +// Mock the audio transcoder ISrsAudioTranscoder. +class MockAudioTranscoder : public ISrsAudioTranscoder +{ +public: + int transcode_count_; + std::vector output_packets_; + std::string aac_header_; + +public: + MockAudioTranscoder(); + virtual ~MockAudioTranscoder(); + +public: + virtual srs_error_t initialize(SrsAudioCodecId from, SrsAudioCodecId to, int channels, int sample_rate, int bit_rate); + virtual srs_error_t transcode(SrsParsedAudioPacket *in, std::vector &outs); + virtual void free_frames(std::vector &frames); + virtual void aac_codec_header(uint8_t **data, int *len); +}; + #endif diff --git a/trunk/src/utest/srs_utest_workflow_rtc2rtmp.cpp b/trunk/src/utest/srs_utest_workflow_rtc2rtmp.cpp index 6e34ed6e5..1e0629cec 100644 --- a/trunk/src/utest/srs_utest_workflow_rtc2rtmp.cpp +++ b/trunk/src/utest/srs_utest_workflow_rtc2rtmp.cpp @@ -36,102 +36,6 @@ #include #include -// Create a mock audio cache ISrsRtcFrameBuilderAudioPacketCache -class MockAudioCache : public ISrsRtcFrameBuilderAudioPacketCache -{ -public: - int process_packet_count_; - -public: - MockAudioCache(); - virtual ~MockAudioCache(); - -public: - virtual srs_error_t process_packet(SrsRtpPacket *src, std::vector &ready_packets); - virtual void clear_all(); -}; - -MockAudioCache::MockAudioCache() -{ - process_packet_count_ = 0; -} - -MockAudioCache::~MockAudioCache() -{ -} - -srs_error_t MockAudioCache::process_packet(SrsRtpPacket *src, std::vector &ready_packets) -{ - process_packet_count_++; - - // Copy the packet. - SrsRtpPacket *copy = src->copy(); - ready_packets.push_back(copy); - - return srs_success; -} - -void MockAudioCache::clear_all() -{ -} - -// Mock the audio transcoder ISrsAudioTranscoder. -class MockAudioTranscoderForRtc2Rtmp : public ISrsAudioTranscoder -{ -public: - int transcode_count_; - std::vector output_packets_; - std::string aac_header_; - -public: - MockAudioTranscoderForRtc2Rtmp(); - virtual ~MockAudioTranscoderForRtc2Rtmp(); - -public: - virtual srs_error_t initialize(SrsAudioCodecId from, SrsAudioCodecId to, int channels, int sample_rate, int bit_rate); - virtual srs_error_t transcode(SrsParsedAudioPacket *in, std::vector &outs); - virtual void free_frames(std::vector &frames); - virtual void aac_codec_header(uint8_t **data, int *len); -}; - -MockAudioTranscoderForRtc2Rtmp::MockAudioTranscoderForRtc2Rtmp() -{ - transcode_count_ = 0; -} - -MockAudioTranscoderForRtc2Rtmp::~MockAudioTranscoderForRtc2Rtmp() -{ -} - -srs_error_t MockAudioTranscoderForRtc2Rtmp::initialize(SrsAudioCodecId from, SrsAudioCodecId to, int channels, int sample_rate, int bit_rate) -{ - return srs_success; -} - -srs_error_t MockAudioTranscoderForRtc2Rtmp::transcode(SrsParsedAudioPacket *in, std::vector &outs) -{ - transcode_count_++; - - SrsParsedAudioPacket *out = in->copy(); - output_packets_.push_back(out); - outs.push_back(out); - - return srs_success; -} - -void MockAudioTranscoderForRtc2Rtmp::free_frames(std::vector &frames) -{ -} - -void MockAudioTranscoderForRtc2Rtmp::aac_codec_header(uint8_t **data, int *len) -{ - int size = aac_header_.size(); - uint8_t *copy = new uint8_t[size]; - memcpy(copy, aac_header_.data(), size); - *data = copy; - *len = size; -} - // 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. @@ -150,7 +54,7 @@ VOID TEST(BasicWorkflowRtc2RtmpTest, ManuallyVerifyTypicalScenario) SrsUniquePtr track_factory(new MockRtcTrackDescriptionFactory()); SrsUniquePtr mock_sources(new MockLiveSourceManager()); MockAudioCache *mock_audio_cache = new MockAudioCache(); - MockAudioTranscoderForRtc2Rtmp *mock_audio_transcoder = new MockAudioTranscoderForRtc2Rtmp(); + MockAudioTranscoder *mock_audio_transcoder = new MockAudioTranscoder(); mock_audio_transcoder->aac_header_ = std::string("\xAF\x00\x12\x10", 4); // AAC sequence header. mock_config->rtc_to_rtmp_ = true; @@ -193,6 +97,10 @@ VOID TEST(BasicWorkflowRtc2RtmpTest, ManuallyVerifyTypicalScenario) frame_builder = bridge->frame_builder_; EXPECT_TRUE(frame_builder != NULL); + + // Mock the frame builder object + srs_freep(frame_builder->audio_cache_); + frame_builder->audio_cache_ = mock_audio_cache; } // Start the publish stream. @@ -200,17 +108,17 @@ VOID TEST(BasicWorkflowRtc2RtmpTest, ManuallyVerifyTypicalScenario) // Test: First call to start() should succeed HELPER_EXPECT_SUCCESS(publish_stream->start()); + // Wait for coroutine to start. + srs_usleep(1 * SRS_UTIME_MILLISECONDS); + // Verify is_sender_started_ flag is set EXPECT_TRUE(publish_stream->is_sender_started_); - // When starting the publish stream, the frame builder should be recreated - EXPECT_TRUE(frame_builder != bridge->frame_builder_); - frame_builder = bridge->frame_builder_; + // When starting the publish stream, the frame builder should not be recreated + EXPECT_TRUE(frame_builder == bridge->frame_builder_); EXPECT_TRUE(frame_builder != NULL); - // Mock the frame builder object - srs_freep(frame_builder->audio_cache_); - frame_builder->audio_cache_ = mock_audio_cache; + // Mock the frame builder object. When publish, the transcoder will be recreated. srs_freep(frame_builder->audio_transcoder_); frame_builder->audio_transcoder_ = mock_audio_transcoder; } @@ -224,6 +132,12 @@ VOID TEST(BasicWorkflowRtc2RtmpTest, ManuallyVerifyTypicalScenario) pkt.header_.set_timestamp(1000); pkt.header_.set_payload_type(track_factory->audio_pt_); + // Create fake audio payload. + SrsRtpRawPayload *raw = new SrsRtpRawPayload(); + pkt.set_payload(raw, SrsRtpPacketPayloadTypeRaw); + raw->payload_ = pkt.wrap(100); + raw->nn_payload_ = 100; + SrsUniquePtr data(new char[1500]); SrsBuffer buf(data.get(), 1500); HELPER_EXPECT_SUCCESS(pkt.encode(&buf));