AI: Add RTMP2RTC workflow test.
This commit is contained in:
parent
72f723b016
commit
2604d0a239
|
|
@ -23,9 +23,9 @@
|
||||||
#ifdef SRS_RTSP
|
#ifdef SRS_RTSP
|
||||||
#include <srs_app_rtsp_source.hpp>
|
#include <srs_app_rtsp_source.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
#include <srs_app_rtc_codec.hpp>
|
||||||
#include <srs_app_rtc_conn.hpp>
|
#include <srs_app_rtc_conn.hpp>
|
||||||
#include <srs_app_rtc_source.hpp>
|
#include <srs_app_rtc_source.hpp>
|
||||||
#include <srs_app_rtc_codec.hpp>
|
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_kernel_file.hpp>
|
#include <srs_kernel_file.hpp>
|
||||||
#include <srs_kernel_flv.hpp>
|
#include <srs_kernel_flv.hpp>
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,10 @@
|
||||||
#ifdef SRS_FFMPEG_FIT
|
#ifdef SRS_FFMPEG_FIT
|
||||||
#include <srs_app_rtc_codec.hpp>
|
#include <srs_app_rtc_codec.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
#include <srs_app_factory.hpp>
|
||||||
#include <srs_kernel_kbps.hpp>
|
#include <srs_kernel_kbps.hpp>
|
||||||
#include <srs_protocol_raw_avc.hpp>
|
#include <srs_protocol_raw_avc.hpp>
|
||||||
#include <srs_protocol_rtp.hpp>
|
#include <srs_protocol_rtp.hpp>
|
||||||
#include <srs_app_factory.hpp>
|
|
||||||
|
|
||||||
// The NACK sent by us(SFU).
|
// The NACK sent by us(SFU).
|
||||||
SrsPps *_srs_pps_snack = NULL;
|
SrsPps *_srs_pps_snack = NULL;
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,7 @@ SrsRtmpConn::SrsRtmpConn(ISrsRtmpTransport *transport, string cip, int cport)
|
||||||
publish_1stpkt_timeout_ = 0;
|
publish_1stpkt_timeout_ = 0;
|
||||||
publish_normal_timeout_ = 0;
|
publish_normal_timeout_ = 0;
|
||||||
|
|
||||||
|
app_factory_ = _srs_app_factory;
|
||||||
config_ = _srs_config;
|
config_ = _srs_config;
|
||||||
manager_ = _srs_conn_manager;
|
manager_ = _srs_conn_manager;
|
||||||
stream_publish_tokens_ = _srs_stream_publish_tokens;
|
stream_publish_tokens_ = _srs_stream_publish_tokens;
|
||||||
|
|
@ -270,6 +271,7 @@ SrsRtmpConn::~SrsRtmpConn()
|
||||||
srs_freep(refer_);
|
srs_freep(refer_);
|
||||||
srs_freep(security_);
|
srs_freep(security_);
|
||||||
|
|
||||||
|
app_factory_ = NULL;
|
||||||
config_ = NULL;
|
config_ = NULL;
|
||||||
manager_ = NULL;
|
manager_ = NULL;
|
||||||
stream_publish_tokens_ = NULL;
|
stream_publish_tokens_ = NULL;
|
||||||
|
|
@ -1065,7 +1067,7 @@ srs_error_t SrsRtmpConn::acquire_publish(SrsSharedPtr<SrsLiveSource> source)
|
||||||
|
|
||||||
// Bridge to RTC streaming.
|
// Bridge to RTC streaming.
|
||||||
// TODO: FIXME: Need to convert RTMP to SRT.
|
// TODO: FIXME: Need to convert RTMP to SRT.
|
||||||
SrsRtmpBridge *bridge = new SrsRtmpBridge(_srs_app_factory);
|
SrsRtmpBridge *bridge = new SrsRtmpBridge(app_factory_);
|
||||||
|
|
||||||
#if defined(SRS_FFMPEG_FIT)
|
#if defined(SRS_FFMPEG_FIT)
|
||||||
bool rtmp_to_rtc = config_->get_rtc_from_rtmp(req->vhost_);
|
bool rtmp_to_rtc = config_->get_rtc_from_rtmp(req->vhost_);
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ class ISrsStreamPublishTokenManager;
|
||||||
class ISrsLiveSourceManager;
|
class ISrsLiveSourceManager;
|
||||||
class ISrsStatistic;
|
class ISrsStatistic;
|
||||||
class ISrsHttpHooks;
|
class ISrsHttpHooks;
|
||||||
|
class ISrsAppFactory;
|
||||||
class ISrsRtcSourceManager;
|
class ISrsRtcSourceManager;
|
||||||
class ISrsSrtSourceManager;
|
class ISrsSrtSourceManager;
|
||||||
class ISrsRtspSourceManager;
|
class ISrsRtspSourceManager;
|
||||||
|
|
@ -174,6 +175,7 @@ class SrsRtmpConn : public ISrsConnection, // It's a resource.
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
SRS_DECLARE_PRIVATE: // clang-format on
|
SRS_DECLARE_PRIVATE: // clang-format on
|
||||||
|
ISrsAppFactory *app_factory_;
|
||||||
ISrsResourceManager *manager_;
|
ISrsResourceManager *manager_;
|
||||||
ISrsAppConfig *config_;
|
ISrsAppConfig *config_;
|
||||||
ISrsStreamPublishTokenManager *stream_publish_tokens_;
|
ISrsStreamPublishTokenManager *stream_publish_tokens_;
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,21 @@ ISrsRequest *MockRtcAsyncCallRequest::as_http()
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MockRtcSource::MockRtcSource()
|
||||||
|
{
|
||||||
|
on_rtp_count_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockRtcSource::~MockRtcSource()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t MockRtcSource::on_rtp(SrsRtpPacket *pkt)
|
||||||
|
{
|
||||||
|
on_rtp_count_++;
|
||||||
|
return SrsRtcSource::on_rtp(pkt);
|
||||||
|
}
|
||||||
|
|
||||||
// MockRtcSourceManager implementation
|
// MockRtcSourceManager implementation
|
||||||
MockRtcSourceManager::MockRtcSourceManager()
|
MockRtcSourceManager::MockRtcSourceManager()
|
||||||
{
|
{
|
||||||
|
|
@ -310,7 +325,7 @@ MockRtcSourceManager::MockRtcSourceManager()
|
||||||
fetch_or_create_error_ = srs_success;
|
fetch_or_create_error_ = srs_success;
|
||||||
initialize_count_ = 0;
|
initialize_count_ = 0;
|
||||||
fetch_or_create_count_ = 0;
|
fetch_or_create_count_ = 0;
|
||||||
mock_source_ = SrsSharedPtr<SrsRtcSource>(new SrsRtcSource());
|
mock_source_ = SrsSharedPtr<SrsRtcSource>(new MockRtcSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
MockRtcSourceManager::~MockRtcSourceManager()
|
MockRtcSourceManager::~MockRtcSourceManager()
|
||||||
|
|
@ -2726,7 +2741,7 @@ void MockAudioTranscoder::aac_codec_header(uint8_t **data, int *len)
|
||||||
if (size <= 0) {
|
if (size <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *copy = new uint8_t[size];
|
uint8_t *copy = new uint8_t[size];
|
||||||
memcpy(copy, aac_header_.data(), size);
|
memcpy(copy, aac_header_.data(), size);
|
||||||
*data = copy;
|
*data = copy;
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,20 @@ public:
|
||||||
virtual ISrsRequest *as_http();
|
virtual ISrsRequest *as_http();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Mock RTC source for testing
|
||||||
|
class MockRtcSource : public SrsRtcSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int on_rtp_count_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MockRtcSource();
|
||||||
|
virtual ~MockRtcSource();
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual srs_error_t on_rtp(SrsRtpPacket *pkt);
|
||||||
|
};
|
||||||
|
|
||||||
// Mock RTC source manager for testing
|
// Mock RTC source manager for testing
|
||||||
class MockRtcSourceManager : public ISrsRtcSourceManager
|
class MockRtcSourceManager : public ISrsRtcSourceManager
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@
|
||||||
|
|
||||||
#include <srs_utest_workflow_rtmp2rtc.hpp>
|
#include <srs_utest_workflow_rtmp2rtc.hpp>
|
||||||
|
|
||||||
|
#include <srs_app_factory.hpp>
|
||||||
|
#include <srs_app_rtc_codec.hpp>
|
||||||
#include <srs_app_rtmp_conn.hpp>
|
#include <srs_app_rtmp_conn.hpp>
|
||||||
#include <srs_protocol_conn.hpp>
|
#include <srs_protocol_conn.hpp>
|
||||||
#include <srs_protocol_io.hpp>
|
#include <srs_protocol_io.hpp>
|
||||||
|
|
@ -38,6 +40,35 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Mock app factory for RTMP to RTC workflow testing
|
||||||
|
class MockAppFactoryForRtmp2Rtc : public SrsAppFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MockAudioTranscoder *last_created_audio_transcoder_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MockAppFactoryForRtmp2Rtc()
|
||||||
|
{
|
||||||
|
last_created_audio_transcoder_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~MockAppFactoryForRtmp2Rtc()
|
||||||
|
{
|
||||||
|
// Don't delete last_created_audio_transcoder_ here,
|
||||||
|
// it is managed by RTP builder
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SRS_FFMPEG_FIT
|
||||||
|
virtual ISrsAudioTranscoder *create_audio_transcoder()
|
||||||
|
{
|
||||||
|
// Create a new mock audio transcoder each time
|
||||||
|
MockAudioTranscoder *transcoder = new MockAudioTranscoder();
|
||||||
|
last_created_audio_transcoder_ = transcoder;
|
||||||
|
return transcoder;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
// This test is used to verify the basic workflow of the RTMP connection.
|
// This test is used to verify the basic workflow of the RTMP connection.
|
||||||
// It's finished with the help of AI, but each step is manually designed
|
// 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.
|
// and verified. So this is not dominated by AI, but by humanbeing.
|
||||||
|
|
@ -46,6 +77,7 @@ VOID TEST(BasicWorkflowRtmp2RtcTest, ManuallyVerifyTypicalScenario)
|
||||||
srs_error_t err;
|
srs_error_t err;
|
||||||
|
|
||||||
// Mock all interface dependencies
|
// Mock all interface dependencies
|
||||||
|
SrsUniquePtr<MockAppFactoryForRtmp2Rtc> mock_factory(new MockAppFactoryForRtmp2Rtc());
|
||||||
SrsUniquePtr<MockAppConfig> mock_config(new MockAppConfig());
|
SrsUniquePtr<MockAppConfig> mock_config(new MockAppConfig());
|
||||||
SrsUniquePtr<MockConnectionManager> mock_manager(new MockConnectionManager());
|
SrsUniquePtr<MockConnectionManager> mock_manager(new MockConnectionManager());
|
||||||
SrsUniquePtr<MockLiveSourceManager> mock_sources(new MockLiveSourceManager());
|
SrsUniquePtr<MockLiveSourceManager> mock_sources(new MockLiveSourceManager());
|
||||||
|
|
@ -77,6 +109,7 @@ VOID TEST(BasicWorkflowRtmp2RtcTest, ManuallyVerifyTypicalScenario)
|
||||||
ISrsRtmpTransport *transport = new MockRtmpTransport();
|
ISrsRtmpTransport *transport = new MockRtmpTransport();
|
||||||
SrsUniquePtr<SrsRtmpConn> conn(new SrsRtmpConn(transport, "192.168.1.100", 1935));
|
SrsUniquePtr<SrsRtmpConn> conn(new SrsRtmpConn(transport, "192.168.1.100", 1935));
|
||||||
|
|
||||||
|
conn->app_factory_ = mock_factory.get();
|
||||||
conn->config_ = mock_config.get();
|
conn->config_ = mock_config.get();
|
||||||
conn->manager_ = mock_manager.get();
|
conn->manager_ = mock_manager.get();
|
||||||
conn->live_sources_ = mock_sources.get();
|
conn->live_sources_ = mock_sources.get();
|
||||||
|
|
@ -124,42 +157,83 @@ VOID TEST(BasicWorkflowRtmp2RtcTest, ManuallyVerifyTypicalScenario)
|
||||||
EXPECT_TRUE(bridge != NULL);
|
EXPECT_TRUE(bridge != NULL);
|
||||||
builder = bridge->rtp_builder_;
|
builder = bridge->rtp_builder_;
|
||||||
EXPECT_TRUE(builder != NULL);
|
EXPECT_TRUE(builder != NULL);
|
||||||
|
|
||||||
|
#ifdef SRS_FFMPEG_FIT
|
||||||
|
// Verify that the mock factory created the audio transcoder
|
||||||
|
EXPECT_TRUE(mock_factory->last_created_audio_transcoder_ != NULL);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an RTMP audio message to feed consumer.
|
// Send AAC sequence header first (required before raw data).
|
||||||
|
MockRtcSource *mock_rtc_source = dynamic_cast<MockRtcSource *>(mock_rtc_sources->mock_source_.get());
|
||||||
if (true) {
|
if (true) {
|
||||||
// Create a real AAC audio message with proper format.
|
// Create AAC sequence header message.
|
||||||
// AAC audio format in RTMP/FLV:
|
// AAC audio format in RTMP/FLV:
|
||||||
// Byte 0: (SoundFormat << 4) | (SoundRate << 2) | (SoundSize << 1) | SoundType
|
// Byte 0: (SoundFormat << 4) | (SoundRate << 2) | (SoundSize << 1) | SoundType
|
||||||
// SoundFormat=10 (AAC), SoundRate=3 (44kHz), SoundSize=1 (16-bit), SoundType=1 (stereo)
|
// SoundFormat=10 (AAC), SoundRate=3 (44kHz), SoundSize=1 (16-bit), SoundType=1 (stereo)
|
||||||
// = 0xAF
|
// = 0xAF
|
||||||
// Byte 1: AACPacketType (0=sequence header, 1=raw data)
|
// Byte 1: AACPacketType (0=sequence header, 1=raw data)
|
||||||
// Remaining bytes: AAC data
|
// Remaining bytes: AudioSpecificConfig (ASC) data
|
||||||
int payload_size = 10;
|
int payload_size = 4;
|
||||||
SrsRtmpCommonMessage *msg = new SrsRtmpCommonMessage();
|
SrsRtmpCommonMessage *msg = new SrsRtmpCommonMessage();
|
||||||
msg->header_.initialize_audio(payload_size, 0, 1);
|
msg->header_.initialize_audio(payload_size, 0, 1);
|
||||||
msg->create_payload(payload_size);
|
msg->create_payload(payload_size);
|
||||||
|
|
||||||
// Fill in AAC audio data
|
// Fill in AAC sequence header
|
||||||
SrsBuffer stream(msg->payload(), payload_size);
|
SrsBuffer stream(msg->payload(), payload_size);
|
||||||
// Audio format byte: AAC(10), 44kHz(3), 16-bit(1), stereo(1) = 0xAF
|
// Audio format byte: AAC(10), 44kHz(3), 16-bit(1), stereo(1) = 0xAF
|
||||||
stream.write_1bytes(0xAF);
|
stream.write_1bytes(0xAF);
|
||||||
// AAC packet type: 1 = AAC raw data
|
// AAC packet type: 0 = AAC sequence header
|
||||||
stream.write_1bytes(0x01);
|
stream.write_1bytes(0x00);
|
||||||
// AAC raw data (8 bytes of dummy audio data)
|
// AudioSpecificConfig (ASC) data: 0x12 0x10
|
||||||
for (int i = 0; i < 8; i++) {
|
// This represents AAC-LC profile, 44.1kHz sample rate, 2 channels
|
||||||
stream.write_1bytes(0x00);
|
stream.write_1bytes(0x12);
|
||||||
}
|
stream.write_1bytes(0x10);
|
||||||
|
|
||||||
// Feed audio to rtmp server.
|
// Feed sequence header to rtmp server.
|
||||||
mock_rtmp_server->recv_msgs_.push_back(msg);
|
mock_rtmp_server->recv_msgs_.push_back(msg);
|
||||||
mock_rtmp_server->cond_->signal();
|
mock_rtmp_server->cond_->signal();
|
||||||
|
|
||||||
// Wait for consumer to process the message.
|
// Wait for consumer to process the message.
|
||||||
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
|
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
|
||||||
|
|
||||||
// Verify that the message is sent to the client.
|
// Verify that the sequence header is sent to the RTC source.
|
||||||
EXPECT_EQ(1, mock_source->on_audio_count_);
|
EXPECT_EQ(1, mock_source->on_audio_count_);
|
||||||
|
EXPECT_EQ(0, mock_rtc_source->on_rtp_count_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send AAC raw data frame.
|
||||||
|
if (true) {
|
||||||
|
// Create AAC raw data message.
|
||||||
|
// Byte 0: Audio format byte (0xAF)
|
||||||
|
// Byte 1: AACPacketType (1=raw data)
|
||||||
|
// Remaining bytes: AAC raw frame data
|
||||||
|
int payload_size = 10;
|
||||||
|
SrsRtmpCommonMessage *msg = new SrsRtmpCommonMessage();
|
||||||
|
msg->header_.initialize_audio(payload_size, 23, 1); // 23ms timestamp for second frame
|
||||||
|
msg->create_payload(payload_size);
|
||||||
|
|
||||||
|
// Fill in AAC raw data
|
||||||
|
SrsBuffer stream(msg->payload(), payload_size);
|
||||||
|
// Audio format byte: AAC(10), 44kHz(3), 16-bit(1), stereo(1) = 0xAF
|
||||||
|
stream.write_1bytes(0xAF);
|
||||||
|
// AAC packet type: 1 = AAC raw data
|
||||||
|
stream.write_1bytes(0x01);
|
||||||
|
// AAC raw frame data (8 bytes of dummy audio data)
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
stream.write_1bytes(0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed raw data to rtmp server.
|
||||||
|
mock_rtmp_server->recv_msgs_.push_back(msg);
|
||||||
|
mock_rtmp_server->cond_->signal();
|
||||||
|
|
||||||
|
// Wait for consumer to process the message.
|
||||||
|
srs_usleep(1 * SRS_UTIME_MILLISECONDS);
|
||||||
|
|
||||||
|
// Verify that the raw data is sent to the client.
|
||||||
|
EXPECT_EQ(2, mock_source->on_audio_count_);
|
||||||
|
EXPECT_EQ(1, mock_rtc_source->on_rtp_count_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simulate client quit event, the receive thread will get this error.
|
// Simulate client quit event, the receive thread will get this error.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user