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_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_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_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
|
# Always include SRT utest
|
||||||
MODULE_FILES+=("srs_utest_srt")
|
MODULE_FILES+=("srs_utest_srt")
|
||||||
if [[ $SRS_GB28181 == YES ]]; then
|
if [[ $SRS_GB28181 == YES ]]; then
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,14 @@ using namespace std;
|
||||||
|
|
||||||
#define SRS_HTTP_FLV_STREAM_BUFFER 4096
|
#define SRS_HTTP_FLV_STREAM_BUFFER 4096
|
||||||
|
|
||||||
|
ISrsHttpFlvListener::ISrsHttpFlvListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsHttpFlvListener::~ISrsHttpFlvListener()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SrsHttpFlvListener::SrsHttpFlvListener()
|
SrsHttpFlvListener::SrsHttpFlvListener()
|
||||||
{
|
{
|
||||||
listener_ = new SrsTcpListener(this);
|
listener_ = new SrsTcpListener(this);
|
||||||
|
|
@ -84,6 +92,14 @@ srs_error_t SrsHttpFlvListener::on_tcp_client(ISrsListener *listener, srs_netfd_
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISrsAppCasterFlv::ISrsAppCasterFlv()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsAppCasterFlv::~ISrsAppCasterFlv()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SrsAppCasterFlv::SrsAppCasterFlv()
|
SrsAppCasterFlv::SrsAppCasterFlv()
|
||||||
{
|
{
|
||||||
http_mux_ = new SrsHttpServeMux();
|
http_mux_ = new SrsHttpServeMux();
|
||||||
|
|
@ -254,6 +270,14 @@ srs_error_t SrsAppCasterFlv::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISrsDynamicHttpConn::ISrsDynamicHttpConn()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsDynamicHttpConn::~ISrsDynamicHttpConn()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SrsDynamicHttpConn::SrsDynamicHttpConn(ISrsResourceManager *cm, srs_netfd_t fd, SrsHttpServeMux *m, string cip, int cport)
|
SrsDynamicHttpConn::SrsDynamicHttpConn(ISrsResourceManager *cm, srs_netfd_t fd, SrsHttpServeMux *m, string cip, int cport)
|
||||||
{
|
{
|
||||||
// Create a identify for this client.
|
// Create a identify for this client.
|
||||||
|
|
@ -423,13 +447,23 @@ srs_error_t SrsDynamicHttpConn::start()
|
||||||
return conn_->start();
|
return conn_->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISrsHttpFileReader::ISrsHttpFileReader()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsHttpFileReader::~ISrsHttpFileReader()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SrsHttpFileReader::SrsHttpFileReader(ISrsHttpResponseReader *h)
|
SrsHttpFileReader::SrsHttpFileReader(ISrsHttpResponseReader *h)
|
||||||
{
|
{
|
||||||
http_ = h;
|
http_ = h;
|
||||||
|
file_reader_ = new SrsFileReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsHttpFileReader::~SrsHttpFileReader()
|
SrsHttpFileReader::~SrsHttpFileReader()
|
||||||
{
|
{
|
||||||
|
srs_freep(file_reader_);
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsHttpFileReader::open(std::string /*file*/)
|
srs_error_t SrsHttpFileReader::open(std::string /*file*/)
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,18 @@ class SrsAppCasterFlv;
|
||||||
#include <srs_kernel_file.hpp>
|
#include <srs_kernel_file.hpp>
|
||||||
#include <srs_protocol_conn.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.
|
// A TCP listener, for flv stream server.
|
||||||
class SrsHttpFlvListener : public ISrsTcpHandler, public ISrsListener
|
class SrsHttpFlvListener : public ISrsHttpFlvListener
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
SrsTcpListener *listener_;
|
SrsTcpListener *listener_;
|
||||||
|
|
@ -50,8 +60,18 @@ public:
|
||||||
virtual srs_error_t on_tcp_client(ISrsListener *listener, srs_netfd_t stfd);
|
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.
|
// The stream caster for flv stream over HTTP POST.
|
||||||
class SrsAppCasterFlv : public ISrsTcpHandler, public ISrsResourceManager, public ISrsHttpHandler
|
class SrsAppCasterFlv : public ISrsAppCasterFlv
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::string output_;
|
std::string output_;
|
||||||
|
|
@ -90,7 +110,17 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
// The dynamic http connection, never drop the body.
|
// 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:
|
private:
|
||||||
// The manager object to manage the connection.
|
// 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.
|
// 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:
|
private:
|
||||||
ISrsHttpResponseReader *http_;
|
ISrsHttpResponseReader *http_;
|
||||||
|
SrsFileReader *file_reader_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SrsHttpFileReader(ISrsHttpResponseReader *h);
|
SrsHttpFileReader(ISrsHttpResponseReader *h);
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ public:
|
||||||
public:
|
public:
|
||||||
virtual ISrsListener *set_endpoint(const std::string &i, int p) = 0;
|
virtual ISrsListener *set_endpoint(const std::string &i, int p) = 0;
|
||||||
virtual ISrsListener *set_label(const std::string &label) = 0;
|
virtual ISrsListener *set_label(const std::string &label) = 0;
|
||||||
|
virtual void close() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The tcp connection handler.
|
// The tcp connection handler.
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#include <srs_app_config.hpp>
|
#include <srs_app_config.hpp>
|
||||||
|
#include <srs_app_factory.hpp>
|
||||||
#include <srs_app_rtmp_conn.hpp>
|
#include <srs_app_rtmp_conn.hpp>
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
#include <srs_app_utility.hpp>
|
#include <srs_app_utility.hpp>
|
||||||
|
|
@ -35,19 +36,23 @@ SrsUdpCasterListener::SrsUdpCasterListener()
|
||||||
{
|
{
|
||||||
caster_ = new SrsMpegtsOverUdp();
|
caster_ = new SrsMpegtsOverUdp();
|
||||||
listener_ = new SrsUdpListener(caster_);
|
listener_ = new SrsUdpListener(caster_);
|
||||||
|
|
||||||
|
config_ = _srs_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsUdpCasterListener::~SrsUdpCasterListener()
|
SrsUdpCasterListener::~SrsUdpCasterListener()
|
||||||
{
|
{
|
||||||
srs_freep(listener_);
|
srs_freep(listener_);
|
||||||
srs_freep(caster_);
|
srs_freep(caster_);
|
||||||
|
|
||||||
|
config_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsUdpCasterListener::initialize(SrsConfDirective *conf)
|
srs_error_t SrsUdpCasterListener::initialize(SrsConfDirective *conf)
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
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) {
|
if (port <= 0) {
|
||||||
return srs_error_new(ERROR_STREAM_CASTER_PORT, "invalid port=%d", port);
|
return srs_error_new(ERROR_STREAM_CASTER_PORT, "invalid port=%d", port);
|
||||||
}
|
}
|
||||||
|
|
@ -78,6 +83,14 @@ void SrsUdpCasterListener::close()
|
||||||
listener_->close();
|
listener_->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISrsMpegtsQueue::ISrsMpegtsQueue()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsMpegtsQueue::~ISrsMpegtsQueue()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SrsMpegtsQueue::SrsMpegtsQueue()
|
SrsMpegtsQueue::SrsMpegtsQueue()
|
||||||
{
|
{
|
||||||
nb_audios_ = nb_videos_ = 0;
|
nb_audios_ = nb_videos_ = 0;
|
||||||
|
|
@ -152,6 +165,14 @@ SrsMediaPacket *SrsMpegtsQueue::dequeue()
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ISrsMpegtsOverUdp::ISrsMpegtsOverUdp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ISrsMpegtsOverUdp::~ISrsMpegtsOverUdp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SrsMpegtsOverUdp::SrsMpegtsOverUdp()
|
SrsMpegtsOverUdp::SrsMpegtsOverUdp()
|
||||||
{
|
{
|
||||||
context_ = new SrsTsContext();
|
context_ = new SrsTsContext();
|
||||||
|
|
@ -166,6 +187,9 @@ SrsMpegtsOverUdp::SrsMpegtsOverUdp()
|
||||||
h264_sps_pps_sent_ = false;
|
h264_sps_pps_sent_ = false;
|
||||||
queue_ = new SrsMpegtsQueue();
|
queue_ = new SrsMpegtsQueue();
|
||||||
pprint_ = SrsPithyPrint::create_caster();
|
pprint_ = SrsPithyPrint::create_caster();
|
||||||
|
|
||||||
|
config_ = _srs_config;
|
||||||
|
app_factory_ = _srs_app_factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsMpegtsOverUdp::~SrsMpegtsOverUdp()
|
SrsMpegtsOverUdp::~SrsMpegtsOverUdp()
|
||||||
|
|
@ -178,11 +202,14 @@ SrsMpegtsOverUdp::~SrsMpegtsOverUdp()
|
||||||
srs_freep(aac_);
|
srs_freep(aac_);
|
||||||
srs_freep(queue_);
|
srs_freep(queue_);
|
||||||
srs_freep(pprint_);
|
srs_freep(pprint_);
|
||||||
|
|
||||||
|
config_ = NULL;
|
||||||
|
app_factory_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
srs_error_t SrsMpegtsOverUdp::initialize(SrsConfDirective *c)
|
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;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -654,7 +681,7 @@ srs_error_t SrsMpegtsOverUdp::connect()
|
||||||
|
|
||||||
srs_utime_t cto = SRS_CONSTS_RTMP_TIMEOUT;
|
srs_utime_t cto = SRS_CONSTS_RTMP_TIMEOUT;
|
||||||
srs_utime_t sto = SRS_CONSTS_RTMP_PULSE;
|
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) {
|
if ((err = sdk_->connect()) != srs_success) {
|
||||||
close();
|
close();
|
||||||
|
|
|
||||||
|
|
@ -15,18 +15,27 @@ struct sockaddr;
|
||||||
|
|
||||||
class SrsBuffer;
|
class SrsBuffer;
|
||||||
class SrsTsContext;
|
class SrsTsContext;
|
||||||
|
class ISrsTsContext;
|
||||||
class SrsConfDirective;
|
class SrsConfDirective;
|
||||||
class SrsSimpleStream;
|
class SrsSimpleStream;
|
||||||
class SrsRtmpClient;
|
class SrsRtmpClient;
|
||||||
class SrsStSocket;
|
class SrsStSocket;
|
||||||
class ISrsRequest;
|
class ISrsRequest;
|
||||||
class SrsRawH264Stream;
|
class SrsRawH264Stream;
|
||||||
|
class ISrsRawH264Stream;
|
||||||
class SrsMediaPacket;
|
class SrsMediaPacket;
|
||||||
class SrsRawAacStream;
|
class SrsRawAacStream;
|
||||||
|
class ISrsRawAacStream;
|
||||||
struct SrsRawAacStreamCodec;
|
struct SrsRawAacStreamCodec;
|
||||||
class SrsPithyPrint;
|
class SrsPithyPrint;
|
||||||
|
class ISrsPithyPrint;
|
||||||
class SrsSimpleRtmpClient;
|
class SrsSimpleRtmpClient;
|
||||||
|
class ISrsBasicRtmpClient;
|
||||||
class SrsMpegtsOverUdp;
|
class SrsMpegtsOverUdp;
|
||||||
|
class ISrsIpListener;
|
||||||
|
class ISrsMpegtsOverUdp;
|
||||||
|
class ISrsAppConfig;
|
||||||
|
class ISrsAppFactory;
|
||||||
|
|
||||||
#include <srs_app_listener.hpp>
|
#include <srs_app_listener.hpp>
|
||||||
#include <srs_app_st.hpp>
|
#include <srs_app_st.hpp>
|
||||||
|
|
@ -36,8 +45,11 @@ class SrsMpegtsOverUdp;
|
||||||
class SrsUdpCasterListener : public ISrsListener
|
class SrsUdpCasterListener : public ISrsListener
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
SrsUdpListener *listener_;
|
ISrsAppConfig *config_;
|
||||||
SrsMpegtsOverUdp *caster_;
|
|
||||||
|
private:
|
||||||
|
ISrsIpListener *listener_;
|
||||||
|
ISrsMpegtsOverUdp *caster_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SrsUdpCasterListener();
|
SrsUdpCasterListener();
|
||||||
|
|
@ -49,10 +61,22 @@ public:
|
||||||
void close();
|
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.
|
// The queue for mpegts over udp to send packets.
|
||||||
// For the aac in mpegts contains many flv packets in a pes packet,
|
// For the aac in mpegts contains many flv packets in a pes packet,
|
||||||
// we must recalc the timestamp.
|
// we must recalc the timestamp.
|
||||||
class SrsMpegtsQueue
|
class SrsMpegtsQueue : public ISrsMpegtsQueue
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// The key: dts, value: msg.
|
// The key: dts, value: msg.
|
||||||
|
|
@ -69,19 +93,34 @@ public:
|
||||||
virtual SrsMediaPacket *dequeue();
|
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.
|
// The mpegts over udp stream caster.
|
||||||
class SrsMpegtsOverUdp : public ISrsTsHandler, public ISrsUdpHandler
|
class SrsMpegtsOverUdp : public ISrsMpegtsOverUdp
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
SrsTsContext *context_;
|
ISrsAppConfig *config_;
|
||||||
|
ISrsAppFactory *app_factory_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ISrsTsContext *context_;
|
||||||
SrsSimpleStream *buffer_;
|
SrsSimpleStream *buffer_;
|
||||||
std::string output_;
|
std::string output_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SrsSimpleRtmpClient *sdk_;
|
ISrsBasicRtmpClient *sdk_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SrsRawH264Stream *avc_;
|
ISrsRawH264Stream *avc_;
|
||||||
std::string h264_sps_;
|
std::string h264_sps_;
|
||||||
bool h264_sps_changed_;
|
bool h264_sps_changed_;
|
||||||
std::string h264_pps_;
|
std::string h264_pps_;
|
||||||
|
|
@ -89,12 +128,12 @@ private:
|
||||||
bool h264_sps_pps_sent_;
|
bool h264_sps_pps_sent_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SrsRawAacStream *aac_;
|
ISrsRawAacStream *aac_;
|
||||||
std::string aac_specific_config_;
|
std::string aac_specific_config_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SrsMpegtsQueue *queue_;
|
ISrsMpegtsQueue *queue_;
|
||||||
SrsPithyPrint *pprint_;
|
ISrsPithyPrint *pprint_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SrsMpegtsOverUdp();
|
SrsMpegtsOverUdp();
|
||||||
|
|
|
||||||
|
|
@ -451,6 +451,10 @@ srs_error_t MockIpListener::listen()
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MockIpListener::close()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void MockIpListener::reset()
|
void MockIpListener::reset()
|
||||||
{
|
{
|
||||||
endpoint_ip_ = "";
|
endpoint_ip_ = "";
|
||||||
|
|
@ -605,6 +609,10 @@ srs_error_t MockIpListenerForGbListen::listen()
|
||||||
return srs_success;
|
return srs_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MockIpListenerForGbListen::close()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void MockIpListenerForGbListen::reset()
|
void MockIpListenerForGbListen::reset()
|
||||||
{
|
{
|
||||||
listen_called_ = false;
|
listen_called_ = false;
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,7 @@ public:
|
||||||
virtual ISrsListener *set_endpoint(const std::string &i, int p);
|
virtual ISrsListener *set_endpoint(const std::string &i, int p);
|
||||||
virtual ISrsListener *set_label(const std::string &label);
|
virtual ISrsListener *set_label(const std::string &label);
|
||||||
virtual srs_error_t listen();
|
virtual srs_error_t listen();
|
||||||
|
virtual void close();
|
||||||
void reset();
|
void reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -275,6 +276,7 @@ public:
|
||||||
virtual ISrsListener *set_endpoint(const std::string &i, int p);
|
virtual ISrsListener *set_endpoint(const std::string &i, int p);
|
||||||
virtual ISrsListener *set_label(const std::string &label);
|
virtual ISrsListener *set_label(const std::string &label);
|
||||||
virtual srs_error_t listen();
|
virtual srs_error_t listen();
|
||||||
|
virtual void close();
|
||||||
void reset();
|
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