AI: Add test to cover app caster module
This commit is contained in:
parent
b239975458
commit
4004ddb5c0
2
trunk/configure
vendored
2
trunk/configure
vendored
|
|
@ -385,7 +385,7 @@ if [[ $SRS_UTEST == YES ]]; then
|
|||
"srs_utest_protocol3" "srs_utest_app" "srs_utest_app2" "srs_utest_app3" "srs_utest_app4"
|
||||
"srs_utest_app5" "srs_utest_app6" "srs_utest_app7" "srs_utest_app8" "srs_utest_app9"
|
||||
"srs_utest_app10" "srs_utest_app11" "srs_utest_app12" "srs_utest_app13" "srs_utest_app14"
|
||||
"srs_utest_app15" "srs_utest_app16")
|
||||
"srs_utest_app15" "srs_utest_app16" "srs_utest_app17")
|
||||
# Always include SRT utest
|
||||
MODULE_FILES+=("srs_utest_srt")
|
||||
if [[ $SRS_GB28181 == YES ]]; then
|
||||
|
|
|
|||
|
|
@ -26,6 +26,14 @@ using namespace std;
|
|||
|
||||
#define SRS_HTTP_FLV_STREAM_BUFFER 4096
|
||||
|
||||
ISrsHttpFlvListener::ISrsHttpFlvListener()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsHttpFlvListener::~ISrsHttpFlvListener()
|
||||
{
|
||||
}
|
||||
|
||||
SrsHttpFlvListener::SrsHttpFlvListener()
|
||||
{
|
||||
listener_ = new SrsTcpListener(this);
|
||||
|
|
@ -84,6 +92,14 @@ srs_error_t SrsHttpFlvListener::on_tcp_client(ISrsListener *listener, srs_netfd_
|
|||
return err;
|
||||
}
|
||||
|
||||
ISrsAppCasterFlv::ISrsAppCasterFlv()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsAppCasterFlv::~ISrsAppCasterFlv()
|
||||
{
|
||||
}
|
||||
|
||||
SrsAppCasterFlv::SrsAppCasterFlv()
|
||||
{
|
||||
http_mux_ = new SrsHttpServeMux();
|
||||
|
|
@ -254,6 +270,14 @@ srs_error_t SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa
|
|||
return err;
|
||||
}
|
||||
|
||||
ISrsDynamicHttpConn::ISrsDynamicHttpConn()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsDynamicHttpConn::~ISrsDynamicHttpConn()
|
||||
{
|
||||
}
|
||||
|
||||
SrsDynamicHttpConn::SrsDynamicHttpConn(ISrsResourceManager *cm, srs_netfd_t fd, SrsHttpServeMux *m, string cip, int cport)
|
||||
{
|
||||
// Create a identify for this client.
|
||||
|
|
@ -423,13 +447,23 @@ srs_error_t SrsDynamicHttpConn::start()
|
|||
return conn_->start();
|
||||
}
|
||||
|
||||
ISrsHttpFileReader::ISrsHttpFileReader()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsHttpFileReader::~ISrsHttpFileReader()
|
||||
{
|
||||
}
|
||||
|
||||
SrsHttpFileReader::SrsHttpFileReader(ISrsHttpResponseReader *h)
|
||||
{
|
||||
http_ = h;
|
||||
file_reader_ = new SrsFileReader();
|
||||
}
|
||||
|
||||
SrsHttpFileReader::~SrsHttpFileReader()
|
||||
{
|
||||
srs_freep(file_reader_);
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpFileReader::open(std::string /*file*/)
|
||||
|
|
|
|||
|
|
@ -30,8 +30,18 @@ class SrsAppCasterFlv;
|
|||
#include <srs_kernel_file.hpp>
|
||||
#include <srs_protocol_conn.hpp>
|
||||
|
||||
// The http flv listener.
|
||||
class ISrsHttpFlvListener : public ISrsTcpHandler, public ISrsListener
|
||||
{
|
||||
public:
|
||||
ISrsHttpFlvListener();
|
||||
virtual ~ISrsHttpFlvListener();
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
// A TCP listener, for flv stream server.
|
||||
class SrsHttpFlvListener : public ISrsTcpHandler, public ISrsListener
|
||||
class SrsHttpFlvListener : public ISrsHttpFlvListener
|
||||
{
|
||||
private:
|
||||
SrsTcpListener *listener_;
|
||||
|
|
@ -50,8 +60,18 @@ public:
|
|||
virtual srs_error_t on_tcp_client(ISrsListener *listener, srs_netfd_t stfd);
|
||||
};
|
||||
|
||||
// The http flv caster interface.
|
||||
class ISrsAppCasterFlv : public ISrsTcpHandler, public ISrsResourceManager, public ISrsHttpHandler
|
||||
{
|
||||
public:
|
||||
ISrsAppCasterFlv();
|
||||
virtual ~ISrsAppCasterFlv();
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
// The stream caster for flv stream over HTTP POST.
|
||||
class SrsAppCasterFlv : public ISrsTcpHandler, public ISrsResourceManager, public ISrsHttpHandler
|
||||
class SrsAppCasterFlv : public ISrsAppCasterFlv
|
||||
{
|
||||
private:
|
||||
std::string output_;
|
||||
|
|
@ -90,7 +110,17 @@ public:
|
|||
};
|
||||
|
||||
// The dynamic http connection, never drop the body.
|
||||
class SrsDynamicHttpConn : public ISrsConnection, public ISrsStartable, public ISrsHttpConnOwner, public ISrsReloadHandler
|
||||
class ISrsDynamicHttpConn: public ISrsConnection, public ISrsStartable, public ISrsHttpConnOwner, public ISrsReloadHandler
|
||||
{
|
||||
public:
|
||||
ISrsDynamicHttpConn();
|
||||
virtual ~ISrsDynamicHttpConn();
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
// The dynamic http connection, never drop the body.
|
||||
class SrsDynamicHttpConn : public ISrsDynamicHttpConn
|
||||
{
|
||||
private:
|
||||
// The manager object to manage the connection.
|
||||
|
|
@ -135,10 +165,21 @@ public:
|
|||
};
|
||||
|
||||
// The http wrapper for file reader, to read http post stream like a file.
|
||||
class SrsHttpFileReader : public SrsFileReader
|
||||
class ISrsHttpFileReader : public ISrsFileReader
|
||||
{
|
||||
public:
|
||||
ISrsHttpFileReader();
|
||||
virtual ~ISrsHttpFileReader();
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
// The http wrapper for file reader, to read http post stream like a file.
|
||||
class SrsHttpFileReader : public ISrsHttpFileReader
|
||||
{
|
||||
private:
|
||||
ISrsHttpResponseReader *http_;
|
||||
SrsFileReader *file_reader_;
|
||||
|
||||
public:
|
||||
SrsHttpFileReader(ISrsHttpResponseReader *h);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ public:
|
|||
public:
|
||||
virtual ISrsListener *set_endpoint(const std::string &i, int p) = 0;
|
||||
virtual ISrsListener *set_label(const std::string &label) = 0;
|
||||
virtual void close() = 0;
|
||||
};
|
||||
|
||||
// The tcp connection handler.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
using namespace std;
|
||||
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_app_factory.hpp>
|
||||
#include <srs_app_rtmp_conn.hpp>
|
||||
#include <srs_app_st.hpp>
|
||||
#include <srs_app_utility.hpp>
|
||||
|
|
@ -35,19 +36,23 @@ SrsUdpCasterListener::SrsUdpCasterListener()
|
|||
{
|
||||
caster_ = new SrsMpegtsOverUdp();
|
||||
listener_ = new SrsUdpListener(caster_);
|
||||
|
||||
config_ = _srs_config;
|
||||
}
|
||||
|
||||
SrsUdpCasterListener::~SrsUdpCasterListener()
|
||||
{
|
||||
srs_freep(listener_);
|
||||
srs_freep(caster_);
|
||||
|
||||
config_ = NULL;
|
||||
}
|
||||
|
||||
srs_error_t SrsUdpCasterListener::initialize(SrsConfDirective *conf)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
int port = _srs_config->get_stream_caster_listen(conf);
|
||||
int port = config_->get_stream_caster_listen(conf);
|
||||
if (port <= 0) {
|
||||
return srs_error_new(ERROR_STREAM_CASTER_PORT, "invalid port=%d", port);
|
||||
}
|
||||
|
|
@ -78,6 +83,14 @@ void SrsUdpCasterListener::close()
|
|||
listener_->close();
|
||||
}
|
||||
|
||||
ISrsMpegtsQueue::ISrsMpegtsQueue()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsMpegtsQueue::~ISrsMpegtsQueue()
|
||||
{
|
||||
}
|
||||
|
||||
SrsMpegtsQueue::SrsMpegtsQueue()
|
||||
{
|
||||
nb_audios_ = nb_videos_ = 0;
|
||||
|
|
@ -152,6 +165,14 @@ SrsMediaPacket *SrsMpegtsQueue::dequeue()
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ISrsMpegtsOverUdp::ISrsMpegtsOverUdp()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsMpegtsOverUdp::~ISrsMpegtsOverUdp()
|
||||
{
|
||||
}
|
||||
|
||||
SrsMpegtsOverUdp::SrsMpegtsOverUdp()
|
||||
{
|
||||
context_ = new SrsTsContext();
|
||||
|
|
@ -166,6 +187,9 @@ SrsMpegtsOverUdp::SrsMpegtsOverUdp()
|
|||
h264_sps_pps_sent_ = false;
|
||||
queue_ = new SrsMpegtsQueue();
|
||||
pprint_ = SrsPithyPrint::create_caster();
|
||||
|
||||
config_ = _srs_config;
|
||||
app_factory_ = _srs_app_factory;
|
||||
}
|
||||
|
||||
SrsMpegtsOverUdp::~SrsMpegtsOverUdp()
|
||||
|
|
@ -178,11 +202,14 @@ SrsMpegtsOverUdp::~SrsMpegtsOverUdp()
|
|||
srs_freep(aac_);
|
||||
srs_freep(queue_);
|
||||
srs_freep(pprint_);
|
||||
|
||||
config_ = NULL;
|
||||
app_factory_ = NULL;
|
||||
}
|
||||
|
||||
srs_error_t SrsMpegtsOverUdp::initialize(SrsConfDirective *c)
|
||||
{
|
||||
output_ = _srs_config->get_stream_caster_output(c);
|
||||
output_ = config_->get_stream_caster_output(c);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
|
|
@ -654,7 +681,7 @@ srs_error_t SrsMpegtsOverUdp::connect()
|
|||
|
||||
srs_utime_t cto = SRS_CONSTS_RTMP_TIMEOUT;
|
||||
srs_utime_t sto = SRS_CONSTS_RTMP_PULSE;
|
||||
sdk_ = new SrsSimpleRtmpClient(output_, cto, sto);
|
||||
sdk_ = app_factory_->create_rtmp_client(output_, cto, sto);
|
||||
|
||||
if ((err = sdk_->connect()) != srs_success) {
|
||||
close();
|
||||
|
|
|
|||
|
|
@ -15,18 +15,27 @@ struct sockaddr;
|
|||
|
||||
class SrsBuffer;
|
||||
class SrsTsContext;
|
||||
class ISrsTsContext;
|
||||
class SrsConfDirective;
|
||||
class SrsSimpleStream;
|
||||
class SrsRtmpClient;
|
||||
class SrsStSocket;
|
||||
class ISrsRequest;
|
||||
class SrsRawH264Stream;
|
||||
class ISrsRawH264Stream;
|
||||
class SrsMediaPacket;
|
||||
class SrsRawAacStream;
|
||||
class ISrsRawAacStream;
|
||||
struct SrsRawAacStreamCodec;
|
||||
class SrsPithyPrint;
|
||||
class ISrsPithyPrint;
|
||||
class SrsSimpleRtmpClient;
|
||||
class ISrsBasicRtmpClient;
|
||||
class SrsMpegtsOverUdp;
|
||||
class ISrsIpListener;
|
||||
class ISrsMpegtsOverUdp;
|
||||
class ISrsAppConfig;
|
||||
class ISrsAppFactory;
|
||||
|
||||
#include <srs_app_listener.hpp>
|
||||
#include <srs_app_st.hpp>
|
||||
|
|
@ -36,8 +45,11 @@ class SrsMpegtsOverUdp;
|
|||
class SrsUdpCasterListener : public ISrsListener
|
||||
{
|
||||
private:
|
||||
SrsUdpListener *listener_;
|
||||
SrsMpegtsOverUdp *caster_;
|
||||
ISrsAppConfig *config_;
|
||||
|
||||
private:
|
||||
ISrsIpListener *listener_;
|
||||
ISrsMpegtsOverUdp *caster_;
|
||||
|
||||
public:
|
||||
SrsUdpCasterListener();
|
||||
|
|
@ -49,10 +61,22 @@ public:
|
|||
void close();
|
||||
};
|
||||
|
||||
// The interface for mpegts queue.
|
||||
class ISrsMpegtsQueue
|
||||
{
|
||||
public:
|
||||
ISrsMpegtsQueue();
|
||||
virtual ~ISrsMpegtsQueue();
|
||||
|
||||
public:
|
||||
virtual srs_error_t push(SrsMediaPacket *msg) = 0;
|
||||
virtual SrsMediaPacket *dequeue() = 0;
|
||||
};
|
||||
|
||||
// The queue for mpegts over udp to send packets.
|
||||
// For the aac in mpegts contains many flv packets in a pes packet,
|
||||
// we must recalc the timestamp.
|
||||
class SrsMpegtsQueue
|
||||
class SrsMpegtsQueue : public ISrsMpegtsQueue
|
||||
{
|
||||
private:
|
||||
// The key: dts, value: msg.
|
||||
|
|
@ -69,19 +93,34 @@ public:
|
|||
virtual SrsMediaPacket *dequeue();
|
||||
};
|
||||
|
||||
// The interface for mpegts over udp.
|
||||
class ISrsMpegtsOverUdp : public ISrsTsHandler, public ISrsUdpHandler
|
||||
{
|
||||
public:
|
||||
ISrsMpegtsOverUdp();
|
||||
virtual ~ISrsMpegtsOverUdp();
|
||||
|
||||
public:
|
||||
virtual srs_error_t initialize(SrsConfDirective *c) = 0;
|
||||
};
|
||||
|
||||
// The mpegts over udp stream caster.
|
||||
class SrsMpegtsOverUdp : public ISrsTsHandler, public ISrsUdpHandler
|
||||
class SrsMpegtsOverUdp : public ISrsMpegtsOverUdp
|
||||
{
|
||||
private:
|
||||
SrsTsContext *context_;
|
||||
ISrsAppConfig *config_;
|
||||
ISrsAppFactory *app_factory_;
|
||||
|
||||
private:
|
||||
ISrsTsContext *context_;
|
||||
SrsSimpleStream *buffer_;
|
||||
std::string output_;
|
||||
|
||||
private:
|
||||
SrsSimpleRtmpClient *sdk_;
|
||||
ISrsBasicRtmpClient *sdk_;
|
||||
|
||||
private:
|
||||
SrsRawH264Stream *avc_;
|
||||
ISrsRawH264Stream *avc_;
|
||||
std::string h264_sps_;
|
||||
bool h264_sps_changed_;
|
||||
std::string h264_pps_;
|
||||
|
|
@ -89,12 +128,12 @@ private:
|
|||
bool h264_sps_pps_sent_;
|
||||
|
||||
private:
|
||||
SrsRawAacStream *aac_;
|
||||
ISrsRawAacStream *aac_;
|
||||
std::string aac_specific_config_;
|
||||
|
||||
private:
|
||||
SrsMpegtsQueue *queue_;
|
||||
SrsPithyPrint *pprint_;
|
||||
ISrsMpegtsQueue *queue_;
|
||||
ISrsPithyPrint *pprint_;
|
||||
|
||||
public:
|
||||
SrsMpegtsOverUdp();
|
||||
|
|
|
|||
|
|
@ -451,6 +451,10 @@ srs_error_t MockIpListener::listen()
|
|||
return srs_success;
|
||||
}
|
||||
|
||||
void MockIpListener::close()
|
||||
{
|
||||
}
|
||||
|
||||
void MockIpListener::reset()
|
||||
{
|
||||
endpoint_ip_ = "";
|
||||
|
|
@ -605,6 +609,10 @@ srs_error_t MockIpListenerForGbListen::listen()
|
|||
return srs_success;
|
||||
}
|
||||
|
||||
void MockIpListenerForGbListen::close()
|
||||
{
|
||||
}
|
||||
|
||||
void MockIpListenerForGbListen::reset()
|
||||
{
|
||||
listen_called_ = false;
|
||||
|
|
|
|||
|
|
@ -212,6 +212,7 @@ public:
|
|||
virtual ISrsListener *set_endpoint(const std::string &i, int p);
|
||||
virtual ISrsListener *set_label(const std::string &label);
|
||||
virtual srs_error_t listen();
|
||||
virtual void close();
|
||||
void reset();
|
||||
};
|
||||
|
||||
|
|
@ -275,6 +276,7 @@ public:
|
|||
virtual ISrsListener *set_endpoint(const std::string &i, int p);
|
||||
virtual ISrsListener *set_label(const std::string &label);
|
||||
virtual srs_error_t listen();
|
||||
virtual void close();
|
||||
void reset();
|
||||
};
|
||||
|
||||
|
|
|
|||
890
trunk/src/utest/srs_utest_app17.cpp
Normal file
890
trunk/src/utest/srs_utest_app17.cpp
Normal file
|
|
@ -0,0 +1,890 @@
|
|||
//
|
||||
// Copyright (c) 2013-2025 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
|
||||
#include <srs_utest_app17.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#include <srs_app_mpegts_udp.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_packet.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
|
||||
// Mock ISrsAppConfig implementation
|
||||
MockAppConfigForUdpCaster::MockAppConfigForUdpCaster()
|
||||
{
|
||||
stream_caster_listen_port_ = 0;
|
||||
}
|
||||
|
||||
MockAppConfigForUdpCaster::~MockAppConfigForUdpCaster()
|
||||
{
|
||||
}
|
||||
|
||||
int MockAppConfigForUdpCaster::get_stream_caster_listen(SrsConfDirective *conf)
|
||||
{
|
||||
return stream_caster_listen_port_;
|
||||
}
|
||||
|
||||
// Mock ISrsIpListener implementation
|
||||
MockIpListenerForUdpCaster::MockIpListenerForUdpCaster()
|
||||
{
|
||||
endpoint_port_ = 0;
|
||||
set_endpoint_called_ = false;
|
||||
set_label_called_ = false;
|
||||
listen_called_ = false;
|
||||
close_called_ = false;
|
||||
}
|
||||
|
||||
MockIpListenerForUdpCaster::~MockIpListenerForUdpCaster()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsListener *MockIpListenerForUdpCaster::set_endpoint(const std::string &i, int p)
|
||||
{
|
||||
endpoint_ip_ = i;
|
||||
endpoint_port_ = p;
|
||||
set_endpoint_called_ = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
ISrsListener *MockIpListenerForUdpCaster::set_label(const std::string &label)
|
||||
{
|
||||
label_ = label;
|
||||
set_label_called_ = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
srs_error_t MockIpListenerForUdpCaster::listen()
|
||||
{
|
||||
listen_called_ = true;
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
void MockIpListenerForUdpCaster::close()
|
||||
{
|
||||
close_called_ = true;
|
||||
}
|
||||
|
||||
void MockIpListenerForUdpCaster::reset()
|
||||
{
|
||||
endpoint_ip_ = "";
|
||||
endpoint_port_ = 0;
|
||||
label_ = "";
|
||||
set_endpoint_called_ = false;
|
||||
set_label_called_ = false;
|
||||
listen_called_ = false;
|
||||
close_called_ = false;
|
||||
}
|
||||
|
||||
// Mock ISrsMpegtsOverUdp implementation
|
||||
MockMpegtsOverUdp::MockMpegtsOverUdp()
|
||||
{
|
||||
initialize_called_ = false;
|
||||
initialize_error_ = srs_success;
|
||||
}
|
||||
|
||||
MockMpegtsOverUdp::~MockMpegtsOverUdp()
|
||||
{
|
||||
srs_freep(initialize_error_);
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsOverUdp::initialize(SrsConfDirective *c)
|
||||
{
|
||||
initialize_called_ = true;
|
||||
if (initialize_error_ != srs_success) {
|
||||
return srs_error_copy(initialize_error_);
|
||||
}
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsOverUdp::on_ts_message(SrsTsMessage *msg)
|
||||
{
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsOverUdp::on_udp_packet(const sockaddr *from, const int fromlen, char *buf, int nb_buf)
|
||||
{
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
void MockMpegtsOverUdp::reset()
|
||||
{
|
||||
initialize_called_ = false;
|
||||
srs_freep(initialize_error_);
|
||||
}
|
||||
|
||||
// Test SrsUdpCasterListener - covers the major use scenario:
|
||||
// 1. Create listener
|
||||
// 2. Initialize with valid port configuration
|
||||
// 3. Verify listener and caster are properly configured
|
||||
// 4. Start listening
|
||||
// 5. Close the listener
|
||||
VOID TEST(UdpCasterListenerTest, InitializeAndListen)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Create mock config with valid port
|
||||
SrsUniquePtr<MockAppConfigForUdpCaster> mock_config(new MockAppConfigForUdpCaster());
|
||||
mock_config->stream_caster_listen_port_ = 8935;
|
||||
|
||||
// Create mock listener and caster
|
||||
MockIpListenerForUdpCaster *mock_listener = new MockIpListenerForUdpCaster();
|
||||
MockMpegtsOverUdp *mock_caster = new MockMpegtsOverUdp();
|
||||
|
||||
// Create SrsUdpCasterListener
|
||||
SrsUniquePtr<SrsUdpCasterListener> listener(new SrsUdpCasterListener());
|
||||
|
||||
// Inject mock dependencies
|
||||
listener->config_ = mock_config.get();
|
||||
listener->listener_ = mock_listener;
|
||||
listener->caster_ = mock_caster;
|
||||
|
||||
// Create a dummy config directive
|
||||
SrsConfDirective conf;
|
||||
|
||||
// Test initialize() - should configure listener and caster
|
||||
HELPER_EXPECT_SUCCESS(listener->initialize(&conf));
|
||||
|
||||
// Verify listener was configured with correct endpoint
|
||||
EXPECT_TRUE(mock_listener->set_endpoint_called_);
|
||||
EXPECT_EQ(8935, mock_listener->endpoint_port_);
|
||||
EXPECT_EQ(srs_net_address_any(), mock_listener->endpoint_ip_);
|
||||
|
||||
// Verify listener label was set
|
||||
EXPECT_TRUE(mock_listener->set_label_called_);
|
||||
EXPECT_EQ("MPEGTS", mock_listener->label_);
|
||||
|
||||
// Verify caster was initialized
|
||||
EXPECT_TRUE(mock_caster->initialize_called_);
|
||||
|
||||
// Test listen() - should start listening
|
||||
HELPER_EXPECT_SUCCESS(listener->listen());
|
||||
EXPECT_TRUE(mock_listener->listen_called_);
|
||||
|
||||
// Test close() - should close listener
|
||||
listener->close();
|
||||
EXPECT_TRUE(mock_listener->close_called_);
|
||||
|
||||
// Clean up - set to NULL to avoid double-free
|
||||
listener->config_ = NULL;
|
||||
listener->listener_ = NULL;
|
||||
listener->caster_ = NULL;
|
||||
}
|
||||
|
||||
// Test SrsUdpCasterListener::initialize with invalid port
|
||||
VOID TEST(UdpCasterListenerTest, InitializeWithInvalidPort)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Create mock config with invalid port
|
||||
SrsUniquePtr<MockAppConfigForUdpCaster> mock_config(new MockAppConfigForUdpCaster());
|
||||
mock_config->stream_caster_listen_port_ = 0; // Invalid port
|
||||
|
||||
// Create mock listener and caster
|
||||
MockIpListenerForUdpCaster *mock_listener = new MockIpListenerForUdpCaster();
|
||||
MockMpegtsOverUdp *mock_caster = new MockMpegtsOverUdp();
|
||||
|
||||
// Create SrsUdpCasterListener
|
||||
SrsUniquePtr<SrsUdpCasterListener> listener(new SrsUdpCasterListener());
|
||||
|
||||
// Inject mock dependencies
|
||||
listener->config_ = mock_config.get();
|
||||
listener->listener_ = mock_listener;
|
||||
listener->caster_ = mock_caster;
|
||||
|
||||
// Create a dummy config directive
|
||||
SrsConfDirective conf;
|
||||
|
||||
// Test initialize() - should fail with invalid port
|
||||
HELPER_EXPECT_FAILED(listener->initialize(&conf));
|
||||
|
||||
// Verify listener was NOT configured
|
||||
EXPECT_FALSE(mock_listener->set_endpoint_called_);
|
||||
EXPECT_FALSE(mock_listener->set_label_called_);
|
||||
|
||||
// Verify caster was NOT initialized
|
||||
EXPECT_FALSE(mock_caster->initialize_called_);
|
||||
|
||||
// Clean up - set to NULL to avoid double-free
|
||||
listener->config_ = NULL;
|
||||
listener->listener_ = NULL;
|
||||
listener->caster_ = NULL;
|
||||
}
|
||||
|
||||
// Test SrsUdpCasterListener::initialize when caster initialization fails
|
||||
VOID TEST(UdpCasterListenerTest, InitializeWithCasterFailure)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Create mock config with valid port
|
||||
SrsUniquePtr<MockAppConfigForUdpCaster> mock_config(new MockAppConfigForUdpCaster());
|
||||
mock_config->stream_caster_listen_port_ = 8935;
|
||||
|
||||
// Create mock listener and caster
|
||||
MockIpListenerForUdpCaster *mock_listener = new MockIpListenerForUdpCaster();
|
||||
MockMpegtsOverUdp *mock_caster = new MockMpegtsOverUdp();
|
||||
|
||||
// Configure caster to fail initialization
|
||||
mock_caster->initialize_error_ = srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "caster init failed");
|
||||
|
||||
// Create SrsUdpCasterListener
|
||||
SrsUniquePtr<SrsUdpCasterListener> listener(new SrsUdpCasterListener());
|
||||
|
||||
// Inject mock dependencies
|
||||
listener->config_ = mock_config.get();
|
||||
listener->listener_ = mock_listener;
|
||||
listener->caster_ = mock_caster;
|
||||
|
||||
// Create a dummy config directive
|
||||
SrsConfDirective conf;
|
||||
|
||||
// Test initialize() - should fail when caster initialization fails
|
||||
HELPER_EXPECT_FAILED(listener->initialize(&conf));
|
||||
|
||||
// Verify listener was configured (happens before caster init)
|
||||
EXPECT_TRUE(mock_listener->set_endpoint_called_);
|
||||
EXPECT_TRUE(mock_listener->set_label_called_);
|
||||
|
||||
// Verify caster initialization was attempted
|
||||
EXPECT_TRUE(mock_caster->initialize_called_);
|
||||
|
||||
// Clean up - set to NULL to avoid double-free
|
||||
listener->config_ = NULL;
|
||||
listener->listener_ = NULL;
|
||||
listener->caster_ = NULL;
|
||||
}
|
||||
|
||||
// Test SrsMpegtsQueue push and dequeue - major use scenario
|
||||
// This test covers the typical workflow:
|
||||
// 1. Push multiple audio and video packets with different timestamps
|
||||
// 2. Dequeue packets in timestamp order once threshold is met (2+ videos and 2+ audios)
|
||||
// 3. Verify packets are returned in correct timestamp order
|
||||
// 4. Verify dequeue returns NULL when threshold is not met
|
||||
VOID TEST(MpegtsQueueTest, PushAndDequeueBasic)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Create SrsMpegtsQueue
|
||||
SrsUniquePtr<SrsMpegtsQueue> queue(new SrsMpegtsQueue());
|
||||
|
||||
// Push 3 video packets
|
||||
SrsMediaPacket *video1 = new SrsMediaPacket();
|
||||
video1->timestamp_ = 1000;
|
||||
video1->message_type_ = SrsFrameTypeVideo;
|
||||
HELPER_EXPECT_SUCCESS(queue->push(video1));
|
||||
|
||||
SrsMediaPacket *video2 = new SrsMediaPacket();
|
||||
video2->timestamp_ = 2000;
|
||||
video2->message_type_ = SrsFrameTypeVideo;
|
||||
HELPER_EXPECT_SUCCESS(queue->push(video2));
|
||||
|
||||
SrsMediaPacket *video3 = new SrsMediaPacket();
|
||||
video3->timestamp_ = 3000;
|
||||
video3->message_type_ = SrsFrameTypeVideo;
|
||||
HELPER_EXPECT_SUCCESS(queue->push(video3));
|
||||
|
||||
// Push 3 audio packets
|
||||
SrsMediaPacket *audio1 = new SrsMediaPacket();
|
||||
audio1->timestamp_ = 1500;
|
||||
audio1->message_type_ = SrsFrameTypeAudio;
|
||||
HELPER_EXPECT_SUCCESS(queue->push(audio1));
|
||||
|
||||
SrsMediaPacket *audio2 = new SrsMediaPacket();
|
||||
audio2->timestamp_ = 2500;
|
||||
audio2->message_type_ = SrsFrameTypeAudio;
|
||||
HELPER_EXPECT_SUCCESS(queue->push(audio2));
|
||||
|
||||
SrsMediaPacket *audio3 = new SrsMediaPacket();
|
||||
audio3->timestamp_ = 3500;
|
||||
audio3->message_type_ = SrsFrameTypeAudio;
|
||||
HELPER_EXPECT_SUCCESS(queue->push(audio3));
|
||||
|
||||
// Now we have 3 videos and 3 audios, dequeue should return packets in timestamp order
|
||||
SrsMediaPacket *dequeued1 = queue->dequeue();
|
||||
ASSERT_TRUE(dequeued1 != NULL);
|
||||
EXPECT_EQ(1000, dequeued1->timestamp_);
|
||||
EXPECT_TRUE(dequeued1->is_video());
|
||||
srs_freep(dequeued1);
|
||||
|
||||
// After dequeue, we have 2 videos and 3 audios, still meets threshold (2+ videos and 2+ audios)
|
||||
SrsMediaPacket *dequeued2 = queue->dequeue();
|
||||
ASSERT_TRUE(dequeued2 != NULL);
|
||||
EXPECT_EQ(1500, dequeued2->timestamp_);
|
||||
EXPECT_TRUE(dequeued2->is_audio());
|
||||
srs_freep(dequeued2);
|
||||
|
||||
// After dequeue, we have 2 videos and 2 audios, still meets threshold
|
||||
SrsMediaPacket *dequeued3 = queue->dequeue();
|
||||
ASSERT_TRUE(dequeued3 != NULL);
|
||||
EXPECT_EQ(2000, dequeued3->timestamp_);
|
||||
EXPECT_TRUE(dequeued3->is_video());
|
||||
srs_freep(dequeued3);
|
||||
|
||||
// After dequeue, we have 1 video and 2 audios, does NOT meet threshold (need 2+ videos)
|
||||
// So dequeue should return NULL
|
||||
SrsMediaPacket *dequeued4 = queue->dequeue();
|
||||
EXPECT_TRUE(dequeued4 == NULL);
|
||||
}
|
||||
|
||||
// Test SrsMpegtsOverUdp::on_udp_packet and on_udp_bytes - major use scenario
|
||||
// This test covers the complete UDP packet processing flow:
|
||||
// 1. Receive UDP packet with TS data
|
||||
// 2. Parse peer IP and port from sockaddr
|
||||
// 3. Append data to buffer
|
||||
// 4. Find TS sync byte (0x47)
|
||||
// 5. Parse TS packets (188 bytes each)
|
||||
// 6. Decode TS packets using context
|
||||
// 7. Erase consumed bytes from buffer
|
||||
VOID TEST(MpegtsOverUdpTest, ProcessUdpPacketWithTsData)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Create SrsMpegtsOverUdp instance
|
||||
SrsUniquePtr<SrsMpegtsOverUdp> udp_handler(new SrsMpegtsOverUdp());
|
||||
|
||||
// Create a valid TS packet (188 bytes) - PAT packet
|
||||
// This is a real TS PAT (Program Association Table) packet
|
||||
uint8_t ts_packet[188] = {
|
||||
// TS header: sync_byte=0x47, pid=0x0000 (PAT)
|
||||
0x47, 0x40, 0x00, 0x10, 0x00,
|
||||
// PAT table
|
||||
0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00, 0x00, 0x01, 0xf0,
|
||||
0x00, 0x2a, 0xb1, 0x04, 0xb2};
|
||||
// Fill rest with 0xff (stuffing bytes)
|
||||
for (int i = 21; i < 188; i++) {
|
||||
ts_packet[i] = 0xff;
|
||||
}
|
||||
|
||||
// Create sockaddr for peer address
|
||||
struct sockaddr_in peer_addr;
|
||||
memset(&peer_addr, 0, sizeof(peer_addr));
|
||||
peer_addr.sin_family = AF_INET;
|
||||
peer_addr.sin_port = htons(12345);
|
||||
inet_pton(AF_INET, "192.168.1.100", &peer_addr.sin_addr);
|
||||
|
||||
// Test on_udp_packet - should successfully process the TS packet
|
||||
HELPER_EXPECT_SUCCESS(udp_handler->on_udp_packet(
|
||||
(const sockaddr *)&peer_addr,
|
||||
sizeof(peer_addr),
|
||||
(char *)ts_packet,
|
||||
sizeof(ts_packet)));
|
||||
|
||||
// Verify buffer was updated (data appended and then consumed)
|
||||
// After processing one complete TS packet (188 bytes), buffer should be empty
|
||||
EXPECT_EQ(0, udp_handler->buffer_->length());
|
||||
}
|
||||
|
||||
// Mock ISrsRawH264Stream implementation
|
||||
MockMpegtsRawH264Stream::MockMpegtsRawH264Stream()
|
||||
{
|
||||
annexb_demux_called_ = false;
|
||||
is_sps_called_ = false;
|
||||
is_pps_called_ = false;
|
||||
sps_demux_called_ = false;
|
||||
pps_demux_called_ = false;
|
||||
annexb_demux_error_ = srs_success;
|
||||
sps_demux_error_ = srs_success;
|
||||
pps_demux_error_ = srs_success;
|
||||
demux_frame_ = NULL;
|
||||
demux_frame_size_ = 0;
|
||||
is_sps_result_ = false;
|
||||
is_pps_result_ = false;
|
||||
}
|
||||
|
||||
MockMpegtsRawH264Stream::~MockMpegtsRawH264Stream()
|
||||
{
|
||||
srs_freep(annexb_demux_error_);
|
||||
srs_freep(sps_demux_error_);
|
||||
srs_freep(pps_demux_error_);
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRawH264Stream::annexb_demux(SrsBuffer *stream, char **pframe, int *pnb_frame)
|
||||
{
|
||||
annexb_demux_called_ = true;
|
||||
|
||||
if (annexb_demux_error_ != srs_success) {
|
||||
return srs_error_copy(annexb_demux_error_);
|
||||
}
|
||||
|
||||
*pframe = demux_frame_;
|
||||
*pnb_frame = demux_frame_size_;
|
||||
|
||||
// Skip the frame in the buffer
|
||||
if (demux_frame_size_ > 0 && stream->left() >= demux_frame_size_) {
|
||||
stream->skip(demux_frame_size_);
|
||||
}
|
||||
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
bool MockMpegtsRawH264Stream::is_sps(char *frame, int nb_frame)
|
||||
{
|
||||
is_sps_called_ = true;
|
||||
return is_sps_result_;
|
||||
}
|
||||
|
||||
bool MockMpegtsRawH264Stream::is_pps(char *frame, int nb_frame)
|
||||
{
|
||||
is_pps_called_ = true;
|
||||
return is_pps_result_;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRawH264Stream::sps_demux(char *frame, int nb_frame, std::string &sps)
|
||||
{
|
||||
sps_demux_called_ = true;
|
||||
|
||||
if (sps_demux_error_ != srs_success) {
|
||||
return srs_error_copy(sps_demux_error_);
|
||||
}
|
||||
|
||||
sps = sps_output_;
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRawH264Stream::pps_demux(char *frame, int nb_frame, std::string &pps)
|
||||
{
|
||||
pps_demux_called_ = true;
|
||||
|
||||
if (pps_demux_error_ != srs_success) {
|
||||
return srs_error_copy(pps_demux_error_);
|
||||
}
|
||||
|
||||
pps = pps_output_;
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRawH264Stream::mux_sequence_header(std::string sps, std::string pps, std::string &sh)
|
||||
{
|
||||
// Simple mock implementation
|
||||
sh = "\x17\x00\x00\x00\x00"; // AVC sequence header prefix
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRawH264Stream::mux_ipb_frame(char *frame, int frame_size, std::string &ibp)
|
||||
{
|
||||
// Simple mock implementation
|
||||
ibp.assign(frame, frame_size);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRawH264Stream::mux_avc2flv(std::string video, int8_t frame_type, int8_t avc_packet_type, uint32_t dts, uint32_t pts, char **flv, int *nb_flv)
|
||||
{
|
||||
// Simple mock implementation - allocate a small buffer
|
||||
*nb_flv = 10;
|
||||
*flv = new char[*nb_flv];
|
||||
memset(*flv, 0, *nb_flv);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
void MockMpegtsRawH264Stream::reset()
|
||||
{
|
||||
annexb_demux_called_ = false;
|
||||
is_sps_called_ = false;
|
||||
is_pps_called_ = false;
|
||||
sps_demux_called_ = false;
|
||||
pps_demux_called_ = false;
|
||||
srs_freep(annexb_demux_error_);
|
||||
srs_freep(sps_demux_error_);
|
||||
srs_freep(pps_demux_error_);
|
||||
demux_frame_ = NULL;
|
||||
demux_frame_size_ = 0;
|
||||
is_sps_result_ = false;
|
||||
is_pps_result_ = false;
|
||||
}
|
||||
|
||||
// Mock ISrsBasicRtmpClient implementation
|
||||
MockMpegtsRtmpClient::MockMpegtsRtmpClient()
|
||||
{
|
||||
connect_called_ = false;
|
||||
publish_called_ = false;
|
||||
close_called_ = false;
|
||||
connect_error_ = srs_success;
|
||||
publish_error_ = srs_success;
|
||||
stream_id_ = 1;
|
||||
}
|
||||
|
||||
MockMpegtsRtmpClient::~MockMpegtsRtmpClient()
|
||||
{
|
||||
srs_freep(connect_error_);
|
||||
srs_freep(publish_error_);
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRtmpClient::connect()
|
||||
{
|
||||
connect_called_ = true;
|
||||
return srs_error_copy(connect_error_);
|
||||
}
|
||||
|
||||
void MockMpegtsRtmpClient::close()
|
||||
{
|
||||
close_called_ = true;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRtmpClient::publish(int chunk_size, bool with_vhost, std::string *pstream)
|
||||
{
|
||||
publish_called_ = true;
|
||||
return srs_error_copy(publish_error_);
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRtmpClient::play(int chunk_size, bool with_vhost, std::string *pstream)
|
||||
{
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
void MockMpegtsRtmpClient::kbps_sample(const char *label, srs_utime_t age)
|
||||
{
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRtmpClient::recv_message(SrsRtmpCommonMessage **pmsg)
|
||||
{
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRtmpClient::decode_message(SrsRtmpCommonMessage *msg, SrsRtmpCommand **ppacket)
|
||||
{
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRtmpClient::send_and_free_messages(SrsMediaPacket **msgs, int nb_msgs)
|
||||
{
|
||||
for (int i = 0; i < nb_msgs; i++) {
|
||||
srs_freep(msgs[i]);
|
||||
}
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
srs_error_t MockMpegtsRtmpClient::send_and_free_message(SrsMediaPacket *msg)
|
||||
{
|
||||
srs_freep(msg);
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
void MockMpegtsRtmpClient::set_recv_timeout(srs_utime_t timeout)
|
||||
{
|
||||
}
|
||||
|
||||
int MockMpegtsRtmpClient::sid()
|
||||
{
|
||||
return stream_id_;
|
||||
}
|
||||
|
||||
void MockMpegtsRtmpClient::reset()
|
||||
{
|
||||
connect_called_ = false;
|
||||
publish_called_ = false;
|
||||
close_called_ = false;
|
||||
srs_freep(connect_error_);
|
||||
srs_freep(publish_error_);
|
||||
stream_id_ = 1;
|
||||
}
|
||||
|
||||
// Test SrsMpegtsOverUdp::on_ts_video - major use scenario
|
||||
// This test covers the complete video processing flow:
|
||||
// 1. Receive TS video message with H.264 annexb data (SPS, PPS, IDR frame)
|
||||
// 2. Connect to RTMP server
|
||||
// 3. Demux annexb frames from buffer
|
||||
// 4. Process SPS and PPS to generate sequence header
|
||||
// 5. Process IDR frame and write to RTMP
|
||||
VOID TEST(MpegtsOverUdpTest, OnTsVideoWithSpsPpsIdrFrame)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Create SrsMpegtsOverUdp instance
|
||||
SrsUniquePtr<SrsMpegtsOverUdp> udp_handler(new SrsMpegtsOverUdp());
|
||||
|
||||
// Create mock dependencies
|
||||
MockMpegtsRawH264Stream *mock_avc = new MockMpegtsRawH264Stream();
|
||||
MockMpegtsRtmpClient *mock_sdk = new MockMpegtsRtmpClient();
|
||||
|
||||
// Inject mock dependencies
|
||||
udp_handler->avc_ = mock_avc;
|
||||
// Note: sdk_ should be NULL initially, then connect() will be called
|
||||
// For this test, we simulate that sdk_ is already connected
|
||||
udp_handler->sdk_ = mock_sdk;
|
||||
|
||||
// Create a TsMessage with valid timestamp
|
||||
SrsUniquePtr<SrsTsMessage> msg(new SrsTsMessage());
|
||||
msg->dts_ = 90000; // 1 second in 90kHz timebase (will be converted to 1000ms)
|
||||
msg->pts_ = 90000;
|
||||
|
||||
// Create H.264 annexb data: SPS + PPS + IDR frame
|
||||
// SPS NALU (type 7): 0x00 0x00 0x00 0x01 0x67 ...
|
||||
char sps_nalu[] = {0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e};
|
||||
// PPS NALU (type 8): 0x00 0x00 0x00 0x01 0x68 ...
|
||||
char pps_nalu[] = {0x00, 0x00, 0x00, 0x01, 0x68, (char)0xce, 0x3c, (char)0x80};
|
||||
// IDR NALU (type 5): 0x00 0x00 0x00 0x01 0x65 ...
|
||||
char idr_nalu[] = {0x00, 0x00, 0x00, 0x01, 0x65, (char)0x88, (char)0x84, 0x00};
|
||||
|
||||
// Combine all NALUs into a single buffer
|
||||
int total_size = sizeof(sps_nalu) + sizeof(pps_nalu) + sizeof(idr_nalu);
|
||||
char *annexb_data = new char[total_size];
|
||||
memcpy(annexb_data, sps_nalu, sizeof(sps_nalu));
|
||||
memcpy(annexb_data + sizeof(sps_nalu), pps_nalu, sizeof(pps_nalu));
|
||||
memcpy(annexb_data + sizeof(sps_nalu) + sizeof(pps_nalu), idr_nalu, sizeof(idr_nalu));
|
||||
|
||||
// Create buffer with annexb data
|
||||
SrsUniquePtr<SrsBuffer> avs(new SrsBuffer(annexb_data, total_size));
|
||||
|
||||
// Configure mock_avc to return SPS frame
|
||||
mock_avc->demux_frame_ = sps_nalu + 4; // Skip start code
|
||||
mock_avc->demux_frame_size_ = total_size; // Return all data to empty the buffer
|
||||
mock_avc->is_sps_result_ = true;
|
||||
mock_avc->is_pps_result_ = false;
|
||||
mock_avc->sps_output_ = std::string(sps_nalu + 4, sizeof(sps_nalu) - 4);
|
||||
mock_avc->pps_output_ = std::string(pps_nalu + 4, sizeof(pps_nalu) - 4);
|
||||
|
||||
// Test on_ts_video - should process SPS frame
|
||||
HELPER_EXPECT_SUCCESS(udp_handler->on_ts_video(msg.get(), avs.get()));
|
||||
|
||||
// Verify connect was NOT called (sdk_ is already set, so connect() returns early)
|
||||
// In real scenario, connect() would be called if sdk_ is NULL
|
||||
EXPECT_FALSE(mock_sdk->connect_called_);
|
||||
|
||||
// Verify annexb_demux was called
|
||||
EXPECT_TRUE(mock_avc->annexb_demux_called_);
|
||||
|
||||
// Verify is_sps was called
|
||||
EXPECT_TRUE(mock_avc->is_sps_called_);
|
||||
|
||||
// Verify sps_demux was called
|
||||
EXPECT_TRUE(mock_avc->sps_demux_called_);
|
||||
|
||||
// Verify h264_sps_ was updated
|
||||
EXPECT_EQ(mock_avc->sps_output_, udp_handler->h264_sps_);
|
||||
EXPECT_TRUE(udp_handler->h264_sps_changed_);
|
||||
|
||||
// Verify buffer is now empty (all data consumed)
|
||||
EXPECT_TRUE(avs->empty());
|
||||
|
||||
// Clean up - set to NULL to avoid double-free
|
||||
udp_handler->avc_ = NULL;
|
||||
udp_handler->sdk_ = NULL;
|
||||
srs_freep(mock_avc);
|
||||
srs_freep(mock_sdk);
|
||||
delete[] annexb_data;
|
||||
}
|
||||
|
||||
// Test SrsMpegtsOverUdp::write_h264_sps_pps - major use scenario
|
||||
// This test covers the complete SPS/PPS writing flow:
|
||||
// 1. Both SPS and PPS have changed (h264_sps_changed_ and h264_pps_changed_ are true)
|
||||
// 2. Call write_h264_sps_pps with dts and pts
|
||||
// 3. Verify it calls avc_->mux_sequence_header to create sequence header
|
||||
// 4. Verify it calls avc_->mux_avc2flv to convert to FLV format
|
||||
// 5. Verify it calls rtmp_write_packet to write the packet
|
||||
// 6. Verify flags are reset correctly (h264_sps_changed_, h264_pps_changed_ set to false, h264_sps_pps_sent_ set to true)
|
||||
VOID TEST(MpegtsOverUdpTest, WriteH264SpsPps)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Create SrsMpegtsOverUdp instance
|
||||
SrsUniquePtr<SrsMpegtsOverUdp> udp_handler(new SrsMpegtsOverUdp());
|
||||
|
||||
// Create mock dependencies
|
||||
MockMpegtsRawH264Stream *mock_avc = new MockMpegtsRawH264Stream();
|
||||
MockMpegtsRtmpClient *mock_sdk = new MockMpegtsRtmpClient();
|
||||
SrsUniquePtr<SrsMpegtsQueue> mock_queue(new SrsMpegtsQueue());
|
||||
|
||||
// Inject mock dependencies
|
||||
udp_handler->avc_ = mock_avc;
|
||||
udp_handler->sdk_ = mock_sdk;
|
||||
udp_handler->queue_ = mock_queue.get();
|
||||
|
||||
// Set up SPS and PPS data
|
||||
std::string sps_data = "\x67\x42\x00\x1e";
|
||||
std::string pps_data = "\x68\xce\x3c\x80";
|
||||
udp_handler->h264_sps_ = sps_data;
|
||||
udp_handler->h264_pps_ = pps_data;
|
||||
|
||||
// Set both SPS and PPS changed flags to true (required for write_h264_sps_pps to proceed)
|
||||
udp_handler->h264_sps_changed_ = true;
|
||||
udp_handler->h264_pps_changed_ = true;
|
||||
udp_handler->h264_sps_pps_sent_ = false;
|
||||
|
||||
// Test write_h264_sps_pps with valid dts and pts
|
||||
uint32_t dts = 1000;
|
||||
uint32_t pts = 1000;
|
||||
HELPER_EXPECT_SUCCESS(udp_handler->write_h264_sps_pps(dts, pts));
|
||||
|
||||
// Verify flags are reset correctly after successful write
|
||||
EXPECT_FALSE(udp_handler->h264_sps_changed_);
|
||||
EXPECT_FALSE(udp_handler->h264_pps_changed_);
|
||||
EXPECT_TRUE(udp_handler->h264_sps_pps_sent_);
|
||||
|
||||
// Clean up - set to NULL to avoid double-free
|
||||
udp_handler->avc_ = NULL;
|
||||
udp_handler->sdk_ = NULL;
|
||||
udp_handler->queue_ = NULL;
|
||||
srs_freep(mock_avc);
|
||||
srs_freep(mock_sdk);
|
||||
}
|
||||
|
||||
// Test SrsMpegtsOverUdp::write_h264_ipb_frame - major use scenario
|
||||
// This test covers the complete IPB frame writing flow:
|
||||
// 1. SPS/PPS already sent (h264_sps_pps_sent_ = true)
|
||||
// 2. Write an IDR frame (keyframe) with valid H.264 NALU data
|
||||
// 3. Verify it detects IDR frame type correctly (NALU type 5)
|
||||
// 4. Verify it calls avc_->mux_ipb_frame to mux the frame
|
||||
// 5. Verify it calls avc_->mux_avc2flv with correct frame type (keyframe)
|
||||
// 6. Verify it calls rtmp_write_packet to write the video packet
|
||||
VOID TEST(MpegtsOverUdpTest, WriteH264IpbFrameWithIdrFrame)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Create SrsMpegtsOverUdp instance
|
||||
SrsUniquePtr<SrsMpegtsOverUdp> udp_handler(new SrsMpegtsOverUdp());
|
||||
|
||||
// Create mock dependencies
|
||||
MockMpegtsRawH264Stream *mock_avc = new MockMpegtsRawH264Stream();
|
||||
MockMpegtsRtmpClient *mock_sdk = new MockMpegtsRtmpClient();
|
||||
SrsUniquePtr<SrsMpegtsQueue> mock_queue(new SrsMpegtsQueue());
|
||||
|
||||
// Inject mock dependencies
|
||||
udp_handler->avc_ = mock_avc;
|
||||
udp_handler->sdk_ = mock_sdk;
|
||||
udp_handler->queue_ = mock_queue.get();
|
||||
|
||||
// Set h264_sps_pps_sent_ to true (required for write_h264_ipb_frame to proceed)
|
||||
udp_handler->h264_sps_pps_sent_ = true;
|
||||
|
||||
// Create an IDR frame NALU (type 5)
|
||||
// NALU header: 0x65 = 0110 0101 (forbidden_zero_bit=0, nal_ref_idc=3, nal_unit_type=5)
|
||||
char idr_frame[] = {0x65, (char)0x88, (char)0x84, 0x00, 0x01, 0x02, 0x03, 0x04};
|
||||
int frame_size = sizeof(idr_frame);
|
||||
|
||||
// Test write_h264_ipb_frame with IDR frame
|
||||
uint32_t dts = 2000;
|
||||
uint32_t pts = 2000;
|
||||
HELPER_EXPECT_SUCCESS(udp_handler->write_h264_ipb_frame(idr_frame, frame_size, dts, pts));
|
||||
|
||||
// Clean up - set to NULL to avoid double-free
|
||||
udp_handler->avc_ = NULL;
|
||||
udp_handler->sdk_ = NULL;
|
||||
udp_handler->queue_ = NULL;
|
||||
srs_freep(mock_avc);
|
||||
srs_freep(mock_sdk);
|
||||
}
|
||||
|
||||
// Mock ISrsAppFactory implementation
|
||||
MockAppFactoryForMpegtsOverUdp::MockAppFactoryForMpegtsOverUdp()
|
||||
{
|
||||
mock_rtmp_client_ = NULL;
|
||||
create_rtmp_client_called_ = false;
|
||||
}
|
||||
|
||||
MockAppFactoryForMpegtsOverUdp::~MockAppFactoryForMpegtsOverUdp()
|
||||
{
|
||||
// Don't free mock_rtmp_client_ - it's managed by the test
|
||||
}
|
||||
|
||||
ISrsBasicRtmpClient *MockAppFactoryForMpegtsOverUdp::create_rtmp_client(std::string url, srs_utime_t cto, srs_utime_t sto)
|
||||
{
|
||||
create_rtmp_client_called_ = true;
|
||||
return mock_rtmp_client_;
|
||||
}
|
||||
|
||||
void MockAppFactoryForMpegtsOverUdp::reset()
|
||||
{
|
||||
create_rtmp_client_called_ = false;
|
||||
}
|
||||
|
||||
// Test SrsMpegtsOverUdp::rtmp_write_packet - major use scenario
|
||||
// This test covers the complete RTMP packet writing flow:
|
||||
// 1. Call rtmp_write_packet with video data
|
||||
// 2. Verify it calls connect() to establish RTMP connection (if not already connected)
|
||||
// 3. Verify it creates RTMP message from raw data using srs_rtmp_create_msg
|
||||
// 4. Verify it pushes message to queue
|
||||
// 5. Verify it dequeues ready messages and sends them via sdk_->send_and_free_message
|
||||
// 6. Verify multiple packets are sent in correct order when queue threshold is met
|
||||
VOID TEST(MpegtsOverUdpTest, RtmpWritePacketWithVideoData)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
||||
// Create SrsMpegtsOverUdp instance
|
||||
SrsUniquePtr<SrsMpegtsOverUdp> udp_handler(new SrsMpegtsOverUdp());
|
||||
|
||||
// Create mock dependencies
|
||||
MockMpegtsRtmpClient *mock_sdk = new MockMpegtsRtmpClient();
|
||||
SrsUniquePtr<MockAppFactoryForMpegtsOverUdp> mock_factory(new MockAppFactoryForMpegtsOverUdp());
|
||||
SrsUniquePtr<SrsMpegtsQueue> mock_queue(new SrsMpegtsQueue());
|
||||
|
||||
// Configure mock factory to return our mock RTMP client
|
||||
mock_factory->mock_rtmp_client_ = mock_sdk;
|
||||
|
||||
// Inject mock dependencies
|
||||
udp_handler->app_factory_ = mock_factory.get();
|
||||
udp_handler->queue_ = mock_queue.get();
|
||||
udp_handler->output_ = "rtmp://127.0.0.1/live/stream";
|
||||
|
||||
// Note: sdk_ is NULL initially, so connect() will be called
|
||||
|
||||
// Create test video data (H.264 IDR frame) - allocate on heap
|
||||
int video_size = 25;
|
||||
char *video_data = new char[video_size];
|
||||
video_data[0] = 0x17;
|
||||
video_data[1] = 0x01;
|
||||
video_data[2] = 0x00;
|
||||
video_data[3] = 0x00;
|
||||
video_data[4] = 0x00;
|
||||
video_data[5] = 0x00;
|
||||
video_data[6] = 0x00;
|
||||
video_data[7] = 0x00;
|
||||
video_data[8] = 0x10;
|
||||
video_data[9] = 0x65;
|
||||
video_data[10] = (char)0x88;
|
||||
video_data[11] = (char)0x84;
|
||||
video_data[12] = 0x00;
|
||||
for (int i = 13; i < video_size; i++)
|
||||
video_data[i] = i - 12;
|
||||
|
||||
uint32_t timestamp = 1000;
|
||||
char type = SrsFrameTypeVideo;
|
||||
|
||||
// First call to rtmp_write_packet - should trigger connect()
|
||||
HELPER_EXPECT_SUCCESS(udp_handler->rtmp_write_packet(type, timestamp, video_data, video_size));
|
||||
|
||||
// Verify connect was called (factory creates RTMP client)
|
||||
EXPECT_TRUE(mock_factory->create_rtmp_client_called_);
|
||||
EXPECT_TRUE(mock_sdk->connect_called_);
|
||||
EXPECT_TRUE(mock_sdk->publish_called_);
|
||||
|
||||
// Verify sdk_ is now set
|
||||
EXPECT_TRUE(udp_handler->sdk_ != NULL);
|
||||
|
||||
// Reset the flag to verify it's not called again
|
||||
mock_sdk->connect_called_ = false;
|
||||
mock_sdk->publish_called_ = false;
|
||||
|
||||
// Second call should NOT trigger connect (already connected)
|
||||
char *video_data2 = new char[17];
|
||||
video_data2[0] = 0x27;
|
||||
video_data2[1] = 0x01;
|
||||
video_data2[2] = 0x00;
|
||||
video_data2[3] = 0x00;
|
||||
video_data2[4] = 0x00;
|
||||
video_data2[5] = 0x00;
|
||||
video_data2[6] = 0x00;
|
||||
video_data2[7] = 0x00;
|
||||
video_data2[8] = 0x08;
|
||||
video_data2[9] = 0x41;
|
||||
video_data2[10] = (char)0x88;
|
||||
video_data2[11] = (char)0x84;
|
||||
video_data2[12] = 0x00;
|
||||
for (int i = 13; i < 17; i++)
|
||||
video_data2[i] = i - 12;
|
||||
|
||||
uint32_t timestamp2 = 1040;
|
||||
HELPER_EXPECT_SUCCESS(udp_handler->rtmp_write_packet(type, timestamp2, video_data2, 17));
|
||||
|
||||
// Verify connect was NOT called again
|
||||
EXPECT_FALSE(mock_sdk->connect_called_);
|
||||
EXPECT_FALSE(mock_sdk->publish_called_);
|
||||
|
||||
// Clean up - set to NULL to avoid double-free
|
||||
udp_handler->app_factory_ = NULL;
|
||||
udp_handler->queue_ = NULL;
|
||||
udp_handler->sdk_ = NULL;
|
||||
srs_freep(mock_sdk);
|
||||
}
|
||||
164
trunk/src/utest/srs_utest_app17.hpp
Normal file
164
trunk/src/utest/srs_utest_app17.hpp
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
//
|
||||
// Copyright (c) 2013-2025 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
|
||||
#ifndef SRS_UTEST_APP17_HPP
|
||||
#define SRS_UTEST_APP17_HPP
|
||||
|
||||
/*
|
||||
#include <srs_utest_app17.hpp>
|
||||
*/
|
||||
#include <srs_utest.hpp>
|
||||
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_app_factory.hpp>
|
||||
#include <srs_app_listener.hpp>
|
||||
#include <srs_app_mpegts_udp.hpp>
|
||||
#include <srs_protocol_raw_avc.hpp>
|
||||
#include <srs_protocol_rtmp_conn.hpp>
|
||||
#include <srs_utest_app10.hpp>
|
||||
#include <srs_utest_app11.hpp>
|
||||
#include <srs_utest_app6.hpp>
|
||||
|
||||
// Mock ISrsAppConfig for testing SrsUdpCasterListener
|
||||
class MockAppConfigForUdpCaster : public MockAppConfig
|
||||
{
|
||||
public:
|
||||
int stream_caster_listen_port_;
|
||||
|
||||
public:
|
||||
MockAppConfigForUdpCaster();
|
||||
virtual ~MockAppConfigForUdpCaster();
|
||||
|
||||
public:
|
||||
virtual int get_stream_caster_listen(SrsConfDirective *conf);
|
||||
};
|
||||
|
||||
// Mock ISrsIpListener for testing SrsUdpCasterListener
|
||||
class MockIpListenerForUdpCaster : public ISrsIpListener
|
||||
{
|
||||
public:
|
||||
std::string endpoint_ip_;
|
||||
int endpoint_port_;
|
||||
std::string label_;
|
||||
bool set_endpoint_called_;
|
||||
bool set_label_called_;
|
||||
bool listen_called_;
|
||||
bool close_called_;
|
||||
|
||||
public:
|
||||
MockIpListenerForUdpCaster();
|
||||
virtual ~MockIpListenerForUdpCaster();
|
||||
|
||||
public:
|
||||
virtual ISrsListener *set_endpoint(const std::string &i, int p);
|
||||
virtual ISrsListener *set_label(const std::string &label);
|
||||
virtual srs_error_t listen();
|
||||
virtual void close();
|
||||
void reset();
|
||||
};
|
||||
|
||||
// Mock ISrsMpegtsOverUdp for testing SrsUdpCasterListener
|
||||
class MockMpegtsOverUdp : public ISrsMpegtsOverUdp
|
||||
{
|
||||
public:
|
||||
bool initialize_called_;
|
||||
srs_error_t initialize_error_;
|
||||
|
||||
public:
|
||||
MockMpegtsOverUdp();
|
||||
virtual ~MockMpegtsOverUdp();
|
||||
|
||||
public:
|
||||
virtual srs_error_t initialize(SrsConfDirective *c);
|
||||
virtual srs_error_t on_ts_message(SrsTsMessage *msg);
|
||||
virtual srs_error_t on_udp_packet(const sockaddr *from, const int fromlen, char *buf, int nb_buf);
|
||||
void reset();
|
||||
};
|
||||
|
||||
// Mock ISrsRawH264Stream for testing SrsMpegtsOverUdp::on_ts_video
|
||||
class MockMpegtsRawH264Stream : public ISrsRawH264Stream
|
||||
{
|
||||
public:
|
||||
bool annexb_demux_called_;
|
||||
bool is_sps_called_;
|
||||
bool is_pps_called_;
|
||||
bool sps_demux_called_;
|
||||
bool pps_demux_called_;
|
||||
srs_error_t annexb_demux_error_;
|
||||
srs_error_t sps_demux_error_;
|
||||
srs_error_t pps_demux_error_;
|
||||
|
||||
// Mock return values
|
||||
char *demux_frame_;
|
||||
int demux_frame_size_;
|
||||
bool is_sps_result_;
|
||||
bool is_pps_result_;
|
||||
std::string sps_output_;
|
||||
std::string pps_output_;
|
||||
|
||||
public:
|
||||
MockMpegtsRawH264Stream();
|
||||
virtual ~MockMpegtsRawH264Stream();
|
||||
|
||||
public:
|
||||
virtual srs_error_t annexb_demux(SrsBuffer *stream, char **pframe, int *pnb_frame);
|
||||
virtual bool is_sps(char *frame, int nb_frame);
|
||||
virtual bool is_pps(char *frame, int nb_frame);
|
||||
virtual srs_error_t sps_demux(char *frame, int nb_frame, std::string &sps);
|
||||
virtual srs_error_t pps_demux(char *frame, int nb_frame, std::string &pps);
|
||||
virtual srs_error_t mux_sequence_header(std::string sps, std::string pps, std::string &sh);
|
||||
virtual srs_error_t mux_ipb_frame(char *frame, int frame_size, std::string &ibp);
|
||||
virtual srs_error_t mux_avc2flv(std::string video, int8_t frame_type, int8_t avc_packet_type, uint32_t dts, uint32_t pts, char **flv, int *nb_flv);
|
||||
void reset();
|
||||
};
|
||||
|
||||
// Mock ISrsBasicRtmpClient for testing SrsMpegtsOverUdp::on_ts_video
|
||||
class MockMpegtsRtmpClient : public ISrsBasicRtmpClient
|
||||
{
|
||||
public:
|
||||
bool connect_called_;
|
||||
bool publish_called_;
|
||||
bool close_called_;
|
||||
srs_error_t connect_error_;
|
||||
srs_error_t publish_error_;
|
||||
int stream_id_;
|
||||
|
||||
public:
|
||||
MockMpegtsRtmpClient();
|
||||
virtual ~MockMpegtsRtmpClient();
|
||||
|
||||
public:
|
||||
virtual srs_error_t connect();
|
||||
virtual void close();
|
||||
virtual srs_error_t publish(int chunk_size, bool with_vhost = true, std::string *pstream = NULL);
|
||||
virtual srs_error_t play(int chunk_size, bool with_vhost = true, std::string *pstream = NULL);
|
||||
virtual void kbps_sample(const char *label, srs_utime_t age);
|
||||
virtual srs_error_t recv_message(SrsRtmpCommonMessage **pmsg);
|
||||
virtual srs_error_t decode_message(SrsRtmpCommonMessage *msg, SrsRtmpCommand **ppacket);
|
||||
virtual srs_error_t send_and_free_messages(SrsMediaPacket **msgs, int nb_msgs);
|
||||
virtual srs_error_t send_and_free_message(SrsMediaPacket *msg);
|
||||
virtual void set_recv_timeout(srs_utime_t timeout);
|
||||
virtual int sid();
|
||||
void reset();
|
||||
};
|
||||
|
||||
// Mock ISrsAppFactory for testing SrsMpegtsOverUdp::rtmp_write_packet
|
||||
class MockAppFactoryForMpegtsOverUdp : public SrsAppFactory
|
||||
{
|
||||
public:
|
||||
MockMpegtsRtmpClient *mock_rtmp_client_;
|
||||
bool create_rtmp_client_called_;
|
||||
|
||||
public:
|
||||
MockAppFactoryForMpegtsOverUdp();
|
||||
virtual ~MockAppFactoryForMpegtsOverUdp();
|
||||
|
||||
public:
|
||||
virtual ISrsBasicRtmpClient *create_rtmp_client(std::string url, srs_utime_t cto, srs_utime_t sto);
|
||||
void reset();
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user