AI: Improve converage for app rtc module.

This commit is contained in:
OSSRS-AI 2025-09-22 09:32:43 -04:00 committed by winlin
parent ea14caeee5
commit c0fc8cb093
52 changed files with 11228 additions and 393 deletions

View File

@ -402,6 +402,106 @@ testing:
- Verify edge cases like sequence number wrap-around, cache overflow, and null inputs - Verify edge cases like sequence number wrap-around, cache overflow, and null inputs
- Use existing mock helper functions for consistency and maintainability - Use existing mock helper functions for consistency and maintainability
test_object_declaration:
- pattern: "Use unique pointers for object instantiation"
description: "MANDATORY - Always use SrsUniquePtr for object declaration in unit tests instead of stack allocation"
usage: |
WRONG: Stack allocation for SRS classes
SrsRtcPublishStream publish_stream(&mock_exec, &mock_expire, &mock_receiver, cid);
SrsBuffer buffer(data, size);
SrsHttpUri uri;
CORRECT: Use SrsUniquePtr for SRS classes
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, cid));
SrsUniquePtr<SrsBuffer> buffer(new SrsBuffer(data, size));
SrsUniquePtr<SrsHttpUri> uri(new SrsHttpUri());
// Access members using -> operator
HELPER_EXPECT_SUCCESS(publish_stream->initialize());
buffer->write_1bytes(0xff);
uri->parse("http://example.com");
EXCEPTION: Mock objects should be declared directly (stack allocation)
MockRtcPacketReceiver mock_receiver;
MockRtcAsyncCallRequest mock_request("test.vhost", "live", "stream1");
MockRtcAsyncTaskExecutor mock_exec;
rationale: "Consistent with SRS memory management patterns, automatic cleanup, and prevents stack overflow issues with large objects. Mock objects are lightweight and designed for direct instantiation."
mock_class_organization:
- pattern: "Mock class structure"
description: "MANDATORY - Always create mock class declarations in .hpp files and implementations in .cpp files"
usage: |
WRONG: Inline mock class definition in .cpp test file
// In srs_utest_app.cpp
class MockRtcConnection {
public:
bool enabled() { return true; }
srs_error_t send_packet() { return srs_success; }
};
CORRECT: Mock class declaration in .hpp, implementation in .cpp
// In srs_utest_app.hpp
class MockRtcConnection {
public:
bool enabled();
srs_error_t send_packet();
};
// In srs_utest_app.cpp
bool MockRtcConnection::enabled() {
return true;
}
srs_error_t MockRtcConnection::send_packet() {
return srs_success;
}
rationale: "Proper separation of interface and implementation, better code organization, and easier maintenance"
- pattern: "Mock function organization"
description: "MANDATORY - Always declare mock functions in .hpp files and implement in .cpp files"
usage: |
WRONG: Inline mock function in .cpp test file
// In srs_utest_app.cpp
srs_error_t mock_read_function(char* buf, int size) {
return srs_success;
}
CORRECT: Mock function declaration in .hpp, implementation in .cpp
// In srs_utest_app.hpp
srs_error_t mock_read_function(char* buf, int size);
// In srs_utest_app.cpp
srs_error_t mock_read_function(char* buf, int size) {
return srs_success;
}
rationale: "Consistent with SRS coding standards and better code organization"
- pattern: "Reuse existing mocks"
description: "MANDATORY - Always try to use existing mock classes by including the appropriate header file before creating new mocks"
usage: |
CORRECT: Check and reuse existing mocks
// First, include existing mock headers
#include "srs_utest_app.hpp" // For MockRtcConnection, MockHttpServer, etc.
// Use existing mock if available
MockRtcConnection* mock_conn = new MockRtcConnection();
// Only create new mock if none exists
class MockNewFeature {
public:
srs_error_t new_method();
};
rationale: "Reduces code duplication, maintains consistency, and leverages existing test infrastructure"
- pattern: "Mock creation guidelines"
description: "Guidelines for when and how to create new mock classes"
rules:
- "Only create new mock classes if no suitable existing mock is available"
- "Check all existing utest header files (srs_utest_app*.hpp) for reusable mocks"
- "Place new mock class declarations in the appropriate srs_utest_app*.hpp file"
- "Place new mock class implementations in the corresponding srs_utest_app*.cpp file"
- "Follow existing mock naming conventions (Mock prefix + class name)"
- "Keep mock implementations simple and focused on test requirements"
error_handling_macros: error_handling_macros:
- Use HELPER_EXPECT_SUCCESS(x) when expecting a function to succeed (returns srs_success) - Use HELPER_EXPECT_SUCCESS(x) when expecting a function to succeed (returns srs_success)
- Use HELPER_EXPECT_FAILED(x) when expecting a function to fail (returns non-srs_success error) - Use HELPER_EXPECT_FAILED(x) when expecting a function to fail (returns non-srs_success error)

9
.codecov.yml Normal file
View File

@ -0,0 +1,9 @@
coverage:
status:
project:
default:
target: auto
threshold: 2%
patch:
default:
informational: true

2
trunk/configure vendored
View File

@ -382,7 +382,7 @@ if [[ $SRS_UTEST == YES ]]; then
"srs_utest_st" "srs_utest_rtc2" "srs_utest_rtc3" "srs_utest_fmp4" "srs_utest_source_lock" "srs_utest_st" "srs_utest_rtc2" "srs_utest_rtc3" "srs_utest_fmp4" "srs_utest_source_lock"
"srs_utest_stream_token" "srs_utest_rtc_recv_track" "srs_utest_st2" "srs_utest_hevc_structs" "srs_utest_stream_token" "srs_utest_rtc_recv_track" "srs_utest_st2" "srs_utest_hevc_structs"
"srs_utest_coworkers" "srs_utest_pithy_print" "srs_utest_kernel3" "srs_utest_protocol4" "srs_utest_coworkers" "srs_utest_pithy_print" "srs_utest_kernel3" "srs_utest_protocol4"
"srs_utest_protocol3" "srs_utest_app3" "srs_utest_app4") "srs_utest_protocol3" "srs_utest_app3" "srs_utest_app4" "srs_utest_app5" "srs_utest_app6" "srs_utest_app7" "srs_utest_app_rtc2rtmp")
# 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

View File

@ -46,7 +46,7 @@ public:
virtual bool hybrid_dying_water_level() = 0; virtual bool hybrid_dying_water_level() = 0;
}; };
class SrsCircuitBreaker : public ISrsCircuitBreaker, public ISrsFastTimer class SrsCircuitBreaker : public ISrsCircuitBreaker, public ISrsFastTimerHandler
{ {
private: private:
bool enabled_; bool enabled_;

View File

@ -1380,6 +1380,14 @@ srs_error_t SrsConfDirective::read_token(SrsConfigBuffer *buffer, vector<string>
return err; return err;
} }
ISrsAppConfig::ISrsAppConfig()
{
}
ISrsAppConfig::~ISrsAppConfig()
{
}
SrsConfig::SrsConfig() SrsConfig::SrsConfig()
{ {
env_only_ = false; env_only_ = false;

View File

@ -274,12 +274,34 @@ enum SrsReloadState {
SrsReloadStateFinished = 90, SrsReloadStateFinished = 90,
}; };
// The app level config interface.
class ISrsAppConfig : public ISrsConfig
{
public:
ISrsAppConfig();
virtual ~ISrsAppConfig();
public:
virtual bool get_vhost_http_hooks_enabled(std::string vhost) = 0;
virtual SrsConfDirective *get_vhost_on_stop(std::string vhost) = 0;
virtual bool get_rtc_nack_enabled(std::string vhost) = 0;
virtual bool get_rtc_nack_no_copy(std::string vhost) = 0;
virtual bool get_realtime_enabled(std::string vhost, bool is_rtc) = 0;
virtual int get_mw_msgs(std::string vhost, bool is_realtime, bool is_rtc) = 0;
virtual SrsConfDirective *get_vhost_on_unpublish(std::string vhost) = 0;
virtual int get_rtc_drop_for_pt(std::string vhost) = 0;
virtual bool get_rtc_twcc_enabled(std::string vhost) = 0;
virtual bool get_srt_enabled() = 0;
virtual bool get_srt_enabled(std::string vhost) = 0;
virtual bool get_rtc_to_rtmp(std::string vhost) = 0;
};
// The config service provider. // The config service provider.
// For the config supports reload, so never keep the reference cross st-thread, // For the config supports reload, so never keep the reference cross st-thread,
// that is, never save the SrsConfDirective* get by any api of config, // that is, never save the SrsConfDirective* get by any api of config,
// For it maybe free in the reload st-thread cycle. // For it maybe free in the reload st-thread cycle.
// You could keep it before st-thread switch, or simply never keep it. // You could keep it before st-thread switch, or simply never keep it.
class SrsConfig : public ISrsConfig class SrsConfig : public ISrsAppConfig
{ {
friend class SrsConfDirective; friend class SrsConfDirective;
// user command // user command
@ -633,11 +655,11 @@ public:
// Get the mw_msgs, mw wait time in packets for vhost. // Get the mw_msgs, mw wait time in packets for vhost.
// @param vhost, the vhost to get the mw sleep msgs. // @param vhost, the vhost to get the mw sleep msgs.
// TODO: FIXME: add utest for mw config. // TODO: FIXME: add utest for mw config.
virtual int get_mw_msgs(std::string vhost, bool is_realtime, bool is_rtc = false); virtual int get_mw_msgs(std::string vhost, bool is_realtime, bool is_rtc);
// Whether min latency mode enabled. // Whether min latency mode enabled.
// @param vhost, the vhost to get the min_latency. // @param vhost, the vhost to get the min_latency.
// TODO: FIXME: add utest for min_latency. // TODO: FIXME: add utest for min_latency.
virtual bool get_realtime_enabled(std::string vhost, bool is_rtc = false); virtual bool get_realtime_enabled(std::string vhost, bool is_rtc);
// Whether enable tcp nodelay for all clients of vhost. // Whether enable tcp nodelay for all clients of vhost.
virtual bool get_tcp_nodelay(std::string vhost); virtual bool get_tcp_nodelay(std::string vhost);
// The minimal send interval in srs_utime_t. // The minimal send interval in srs_utime_t.

View File

@ -68,7 +68,7 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat()
obj->set("device_id", SrsJsonAny::str(device_id.c_str())); obj->set("device_id", SrsJsonAny::str(device_id.c_str()));
obj->set("ip", SrsJsonAny::str(ip.c_str())); obj->set("ip", SrsJsonAny::str(ip.c_str()));
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
obj->set("server", SrsJsonAny::str(stat->server_id().c_str())); obj->set("server", SrsJsonAny::str(stat->server_id().c_str()));
obj->set("service", SrsJsonAny::str(stat->service_id().c_str())); obj->set("service", SrsJsonAny::str(stat->service_id().c_str()));
obj->set("pid", SrsJsonAny::str(stat->service_pid().c_str())); obj->set("pid", SrsJsonAny::str(stat->service_pid().c_str()));

View File

@ -178,7 +178,7 @@ SrsGoApiRoot::~SrsGoApiRoot()
srs_error_t SrsGoApiRoot::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiRoot::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -217,7 +217,7 @@ SrsGoApiApi::~SrsGoApiApi()
srs_error_t SrsGoApiApi::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiApi::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -244,7 +244,7 @@ SrsGoApiV1::~SrsGoApiV1()
srs_error_t SrsGoApiV1::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiV1::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -300,7 +300,7 @@ SrsGoApiVersion::~SrsGoApiVersion()
srs_error_t SrsGoApiVersion::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiVersion::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -330,7 +330,7 @@ SrsGoApiSummaries::~SrsGoApiSummaries()
srs_error_t SrsGoApiSummaries::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiSummaries::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -354,7 +354,7 @@ SrsGoApiRusages::~SrsGoApiRusages()
srs_error_t SrsGoApiRusages::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiRusages::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -400,7 +400,7 @@ SrsGoApiSelfProcStats::~SrsGoApiSelfProcStats()
srs_error_t SrsGoApiSelfProcStats::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiSelfProcStats::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -478,7 +478,7 @@ SrsGoApiSystemProcStats::~SrsGoApiSystemProcStats()
srs_error_t SrsGoApiSystemProcStats::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiSystemProcStats::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -518,7 +518,7 @@ SrsGoApiMemInfos::~SrsGoApiMemInfos()
srs_error_t SrsGoApiMemInfos::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiMemInfos::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -559,7 +559,7 @@ SrsGoApiAuthors::~SrsGoApiAuthors()
srs_error_t SrsGoApiAuthors::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiAuthors::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -587,7 +587,7 @@ SrsGoApiFeatures::~SrsGoApiFeatures()
srs_error_t SrsGoApiFeatures::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiFeatures::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -656,7 +656,7 @@ SrsGoApiRequests::~SrsGoApiRequests()
srs_error_t SrsGoApiRequests::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r) srs_error_t SrsGoApiRequests::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
@ -703,7 +703,7 @@ srs_error_t SrsGoApiVhosts::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessag
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
// path: {pattern}{vhost_id} // path: {pattern}{vhost_id}
// e.g. /api/v1/vhosts/100 pattern= /api/v1/vhosts/, vhost_id=100 // e.g. /api/v1/vhosts/100 pattern= /api/v1/vhosts/, vhost_id=100
@ -761,7 +761,7 @@ srs_error_t SrsGoApiStreams::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
// path: {pattern}{stream_id} // path: {pattern}{stream_id}
// e.g. /api/v1/streams/100 pattern= /api/v1/streams/, stream_id=100 // e.g. /api/v1/streams/100 pattern= /api/v1/streams/, stream_id=100
@ -823,7 +823,7 @@ srs_error_t SrsGoApiClients::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
// path: {pattern}{client_id} // path: {pattern}{client_id}
// e.g. /api/v1/clients/100 pattern= /api/v1/clients/, client_id=100 // e.g. /api/v1/clients/100 pattern= /api/v1/clients/, client_id=100
@ -1273,7 +1273,7 @@ srs_error_t SrsGoApiMetrics::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa
* error counter * error counter
*/ */
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
std::stringstream ss; std::stringstream ss;
#if defined(__linux__) || defined(SRS_OSX) #if defined(__linux__) || defined(SRS_OSX)

View File

@ -416,8 +416,8 @@ srs_error_t SrsHttpxConn::on_conn_done(srs_error_t r0)
{ {
// Only stat the HTTP streaming clients, ignore all API clients. // Only stat the HTTP streaming clients, ignore all API clients.
if (enable_stat_) { if (enable_stat_) {
SrsStatistic::instance()->on_disconnect(get_id().c_str(), r0); _srs_stat->on_disconnect(get_id().c_str(), r0);
SrsStatistic::instance()->kbps_add_delta(get_id().c_str(), conn_->delta()); _srs_stat->kbps_add_delta(get_id().c_str(), conn_->delta());
} }
// Because we use manager to manage this object, // Because we use manager to manage this object,

View File

@ -57,7 +57,7 @@ srs_error_t SrsHttpHooks::on_connect(string url, ISrsRequest *req)
srs_error_t err = srs_success; srs_error_t err = srs_success;
SrsContextId cid = _srs_context->get_id(); SrsContextId cid = _srs_context->get_id();
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str())); obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str()));
@ -93,7 +93,7 @@ void SrsHttpHooks::on_close(string url, ISrsRequest *req, int64_t send_bytes, in
srs_error_t err = srs_success; srs_error_t err = srs_success;
SrsContextId cid = _srs_context->get_id(); SrsContextId cid = _srs_context->get_id();
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str())); obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str()));
@ -130,7 +130,7 @@ srs_error_t SrsHttpHooks::on_publish(string url, ISrsRequest *req)
srs_error_t err = srs_success; srs_error_t err = srs_success;
SrsContextId cid = _srs_context->get_id(); SrsContextId cid = _srs_context->get_id();
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str())); obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str()));
@ -171,7 +171,7 @@ void SrsHttpHooks::on_unpublish(string url, ISrsRequest *req)
srs_error_t err = srs_success; srs_error_t err = srs_success;
SrsContextId cid = _srs_context->get_id(); SrsContextId cid = _srs_context->get_id();
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str())); obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str()));
@ -215,7 +215,7 @@ srs_error_t SrsHttpHooks::on_play(string url, ISrsRequest *req)
srs_error_t err = srs_success; srs_error_t err = srs_success;
SrsContextId cid = _srs_context->get_id(); SrsContextId cid = _srs_context->get_id();
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str())); obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str()));
@ -257,7 +257,7 @@ void SrsHttpHooks::on_stop(string url, ISrsRequest *req)
srs_error_t err = srs_success; srs_error_t err = srs_success;
SrsContextId cid = _srs_context->get_id(); SrsContextId cid = _srs_context->get_id();
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str())); obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str()));
@ -303,7 +303,7 @@ srs_error_t SrsHttpHooks::on_dvr(SrsContextId c, string url, ISrsRequest *req, s
SrsContextId cid = c; SrsContextId cid = c;
std::string cwd = _srs_config->cwd(); std::string cwd = _srs_config->cwd();
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str())); obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str()));
@ -354,7 +354,7 @@ srs_error_t SrsHttpHooks::on_hls(SrsContextId c, string url, ISrsRequest *req, s
ts_url = prefix + "/" + ts_url; ts_url = prefix + "/" + ts_url;
} }
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str())); obj->set("server_id", SrsJsonAny::str(stat->server_id().c_str()));
@ -407,7 +407,7 @@ srs_error_t SrsHttpHooks::on_hls_notify(SrsContextId c, std::string url, ISrsReq
url = ts_url; url = ts_url;
} }
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
url = srs_strings_replace(url, "[server_id]", stat->server_id().c_str()); url = srs_strings_replace(url, "[server_id]", stat->server_id().c_str());
url = srs_strings_replace(url, "[service_id]", stat->service_id().c_str()); url = srs_strings_replace(url, "[service_id]", stat->service_id().c_str());
@ -526,7 +526,7 @@ srs_error_t SrsHttpHooks::on_forward_backend(string url, ISrsRequest *req, std::
SrsContextId cid = _srs_context->get_id(); SrsContextId cid = _srs_context->get_id();
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object()); SrsUniquePtr<SrsJsonObject> obj(SrsJsonAny::object());
obj->set("action", SrsJsonAny::str("on_forward")); obj->set("action", SrsJsonAny::str("on_forward"));

View File

@ -55,7 +55,7 @@ void SrsHlsVirtualConn::expire()
interrupt_ = true; interrupt_ = true;
// remove statistic quickly // remove statistic quickly
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->on_disconnect(ctx_, srs_success); stat->on_disconnect(ctx_, srs_success);
} }
@ -141,7 +141,7 @@ void SrsHlsStream::on_serve_ts_ctx(ISrsHttpResponseWriter *w, ISrsHttpMessage *r
// Only update the delta, because SrsServer will sample it. Note that SrsServer also does the stat for all clients // Only update the delta, because SrsServer will sample it. Note that SrsServer also does the stat for all clients
// including this one, but it should be ignored because the id is not matched, and instead we use the hls_ctx as // including this one, but it should be ignored because the id is not matched, and instead we use the hls_ctx as
// session id to match the client. // session id to match the client.
SrsStatistic::instance()->kbps_add_delta(ctx, delta); _srs_stat->kbps_add_delta(ctx, delta);
} }
srs_error_t SrsHlsStream::serve_new_session(ISrsHttpResponseWriter *w, ISrsHttpMessage *r, ISrsRequest *req, std::string &ctx) srs_error_t SrsHlsStream::serve_new_session(ISrsHttpResponseWriter *w, ISrsHttpMessage *r, ISrsRequest *req, std::string &ctx)
@ -162,7 +162,7 @@ srs_error_t SrsHlsStream::serve_new_session(ISrsHttpResponseWriter *w, ISrsHttpM
_srs_context->set_id(SrsContextId().set_value(ctx)); _srs_context->set_id(SrsContextId().set_value(ctx));
// We must do stat the client before hooks, because hooks depends on it. // We must do stat the client before hooks, because hooks depends on it.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
if ((err = stat->on_client(ctx, req, NULL, SrsHlsPlay)) != srs_success) { if ((err = stat->on_client(ctx, req, NULL, SrsHlsPlay)) != srs_success) {
return srs_error_wrap(err, "stat on client"); return srs_error_wrap(err, "stat on client");
} }
@ -292,7 +292,7 @@ void SrsHlsStream::alive(std::string ctx, ISrsRequest *req)
map_ctx_info_.insert(make_pair(ctx, conn)); map_ctx_info_.insert(make_pair(ctx, conn));
// Update the conn of stat client, which is used for receiving the event of kickoff. // Update the conn of stat client, which is used for receiving the event of kickoff.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
SrsStatisticClient *client = stat->find_client(ctx); SrsStatisticClient *client = stat->find_client(ctx);
if (client) { if (client) {
client->conn_ = conn; client->conn_ = conn;
@ -387,7 +387,7 @@ srs_error_t SrsHlsStream::on_timer(srs_utime_t interval)
http_hooks_on_stop(info->req_); http_hooks_on_stop(info->req_);
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
// TODO: FIXME: Should finger out the err. // TODO: FIXME: Should finger out the err.
stat->on_disconnect(ctx, srs_success); stat->on_disconnect(ctx, srs_success);

View File

@ -31,7 +31,7 @@ public:
}; };
// Server HLS streaming. // Server HLS streaming.
class SrsHlsStream : public ISrsFastTimer class SrsHlsStream : public ISrsFastTimerHandler
{ {
private: private:
// The period of validity of the ctx // The period of validity of the ctx
@ -53,7 +53,7 @@ private:
srs_error_t http_hooks_on_play(ISrsRequest *req); srs_error_t http_hooks_on_play(ISrsRequest *req);
void http_hooks_on_stop(ISrsRequest *req); void http_hooks_on_stop(ISrsRequest *req);
bool is_interrupt(std::string id); bool is_interrupt(std::string id);
// interface ISrsFastTimer // interface ISrsFastTimerHandler
private: private:
srs_error_t on_timer(srs_utime_t interval); srs_error_t on_timer(srs_utime_t interval);

View File

@ -663,7 +663,7 @@ srs_error_t SrsLiveStream::serve_http_impl(ISrsHttpResponseWriter *w, ISrsHttpMe
req_->ip_ = hc->remote_ip(); req_->ip_ = hc->remote_ip();
// We must do stat the client before hooks, because hooks depends on it. // We must do stat the client before hooks, because hooks depends on it.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
if ((err = stat->on_client(_srs_context->get_id().c_str(), req_, hc, SrsFlvPlay)) != srs_success) { if ((err = stat->on_client(_srs_context->get_id().c_str(), req_, hc, SrsFlvPlay)) != srs_success) {
return srs_error_wrap(err, "stat on client"); return srs_error_wrap(err, "stat on client");
} }

View File

@ -191,7 +191,7 @@ srs_error_t SrsLatestVersion::start()
return srs_success; return srs_success;
} }
server_id_ = SrsStatistic::instance()->server_id(); server_id_ = _srs_stat->server_id();
session_id_ = srs_generate_stat_vid(); session_id_ = srs_generate_stat_vid();
return trd_->start(); return trd_->start();
@ -243,7 +243,7 @@ srs_error_t SrsLatestVersion::query_latest_version(string &url)
<< "&ts=" << srs_time_now_cached() << "&ts=" << srs_time_now_cached()
<< "&alive=" << srsu2ms(srs_time_now_cached() - srs_time_since_startup()) / 1000; << "&alive=" << srsu2ms(srs_time_now_cached() - srs_time_since_startup()) / 1000;
srs_build_features(ss); srs_build_features(ss);
SrsStatistic::instance()->dumps_hints_kv(ss); _srs_stat->dumps_hints_kv(ss);
url = ss.str(); url = ss.str();
SrsHttpUri uri; SrsHttpUri uri;

View File

@ -279,7 +279,7 @@ SrsPublishRecvThread::SrsPublishRecvThread(SrsRtmpServer *rtmp_sdk, ISrsRequest
mr_ = _srs_config->get_mr_enabled(req_->vhost_); mr_ = _srs_config->get_mr_enabled(req_->vhost_);
mr_sleep_ = _srs_config->get_mr_sleep(req_->vhost_); mr_sleep_ = _srs_config->get_mr_sleep(req_->vhost_);
realtime_ = _srs_config->get_realtime_enabled(req_->vhost_); realtime_ = _srs_config->get_realtime_enabled(req_->vhost_, false);
_srs_config->subscribe(this); _srs_config->subscribe(this);
} }

View File

@ -178,9 +178,9 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
} }
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS)); res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
res->set("server", SrsJsonAny::str(SrsStatistic::instance()->server_id().c_str())); res->set("server", SrsJsonAny::str(_srs_stat->server_id().c_str()));
res->set("service", SrsJsonAny::str(SrsStatistic::instance()->service_id().c_str())); res->set("service", SrsJsonAny::str(_srs_stat->service_id().c_str()));
res->set("pid", SrsJsonAny::str(SrsStatistic::instance()->service_pid().c_str())); res->set("pid", SrsJsonAny::str(_srs_stat->service_pid().c_str()));
// TODO: add candidates in response json? // TODO: add candidates in response json?
res->set("sdp", SrsJsonAny::str(ruc.local_sdp_str_.c_str())); res->set("sdp", SrsJsonAny::str(ruc.local_sdp_str_.c_str()));
@ -465,9 +465,9 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter *w, ISrsHtt
} }
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS)); res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
res->set("server", SrsJsonAny::str(SrsStatistic::instance()->server_id().c_str())); res->set("server", SrsJsonAny::str(_srs_stat->server_id().c_str()));
res->set("service", SrsJsonAny::str(SrsStatistic::instance()->service_id().c_str())); res->set("service", SrsJsonAny::str(_srs_stat->service_id().c_str()));
res->set("pid", SrsJsonAny::str(SrsStatistic::instance()->service_pid().c_str())); res->set("pid", SrsJsonAny::str(_srs_stat->service_pid().c_str()));
// TODO: add candidates in response json? // TODO: add candidates in response json?
res->set("sdp", SrsJsonAny::str(ruc.local_sdp_str_.c_str())); res->set("sdp", SrsJsonAny::str(ruc.local_sdp_str_.c_str()));

View File

@ -293,31 +293,31 @@ srs_error_t SrsPlaintextTransport::unprotect_rtcp(void *packet, int *nb_plaintex
return srs_success; return srs_success;
} }
ISrsRtcPLIWorkerHandler::ISrsRtcPLIWorkerHandler() ISrsRtcPliWorkerHandler::ISrsRtcPliWorkerHandler()
{ {
} }
ISrsRtcPLIWorkerHandler::~ISrsRtcPLIWorkerHandler() ISrsRtcPliWorkerHandler::~ISrsRtcPliWorkerHandler()
{ {
} }
SrsRtcPLIWorker::SrsRtcPLIWorker(ISrsRtcPLIWorkerHandler *h) SrsRtcPliWorker::SrsRtcPliWorker(ISrsRtcPliWorkerHandler *h)
{ {
handler_ = h; handler_ = h;
wait_ = srs_cond_new(); wait_ = new SrsCond();
trd_ = new SrsSTCoroutine("pli", this, _srs_context->get_id()); trd_ = new SrsSTCoroutine("pli", this, _srs_context->get_id());
} }
SrsRtcPLIWorker::~SrsRtcPLIWorker() SrsRtcPliWorker::~SrsRtcPliWorker()
{ {
srs_cond_signal(wait_); wait_->signal();
trd_->stop(); trd_->stop();
srs_freep(trd_); srs_freep(trd_);
srs_cond_destroy(wait_); srs_freep(wait_);
} }
srs_error_t SrsRtcPLIWorker::start() srs_error_t SrsRtcPliWorker::start()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
@ -328,13 +328,13 @@ srs_error_t SrsRtcPLIWorker::start()
return err; return err;
} }
void SrsRtcPLIWorker::request_keyframe(uint32_t ssrc, SrsContextId cid) void SrsRtcPliWorker::request_keyframe(uint32_t ssrc, SrsContextId cid)
{ {
plis_.insert(make_pair(ssrc, cid)); plis_.insert(make_pair(ssrc, cid));
srs_cond_signal(wait_); wait_->signal();
} }
srs_error_t SrsRtcPLIWorker::cycle() srs_error_t SrsRtcPliWorker::cycle()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
@ -359,7 +359,8 @@ srs_error_t SrsRtcPLIWorker::cycle()
} }
} }
} }
srs_cond_wait(wait_);
wait_->wait();
} }
return err; return err;
@ -369,18 +370,25 @@ SrsRtcAsyncCallOnStop::SrsRtcAsyncCallOnStop(SrsContextId c, ISrsRequest *r)
{ {
cid_ = c; cid_ = c;
req_ = r->copy(); req_ = r->copy();
hooks_ = _srs_hooks;
context_ = _srs_context;
config_ = _srs_config;
} }
SrsRtcAsyncCallOnStop::~SrsRtcAsyncCallOnStop() SrsRtcAsyncCallOnStop::~SrsRtcAsyncCallOnStop()
{ {
srs_freep(req_); srs_freep(req_);
hooks_ = NULL;
context_ = NULL;
config_ = NULL;
} }
srs_error_t SrsRtcAsyncCallOnStop::call() srs_error_t SrsRtcAsyncCallOnStop::call()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost_)) { if (!config_->get_vhost_http_hooks_enabled(req_->vhost_)) {
return err; return err;
} }
@ -390,7 +398,7 @@ srs_error_t SrsRtcAsyncCallOnStop::call()
vector<string> hooks; vector<string> hooks;
if (true) { if (true) {
SrsConfDirective *conf = _srs_config->get_vhost_on_stop(req_->vhost_); SrsConfDirective *conf = config_->get_vhost_on_stop(req_->vhost_);
if (!conf) { if (!conf) {
return err; return err;
@ -399,12 +407,12 @@ srs_error_t SrsRtcAsyncCallOnStop::call()
hooks = conf->args_; hooks = conf->args_;
} }
SrsContextRestore(_srs_context->get_id()); SrsContextRestore(context_->get_id());
_srs_context->set_id(cid_); context_->set_id(cid_);
for (int i = 0; i < (int)hooks.size(); i++) { for (int i = 0; i < (int)hooks.size(); i++) {
std::string url = hooks.at(i); std::string url = hooks.at(i);
_srs_hooks->on_stop(url, req_); hooks_->on_stop(url, req_);
} }
return err; return err;
@ -415,15 +423,18 @@ std::string SrsRtcAsyncCallOnStop::to_string()
return std::string(""); return std::string("");
} }
SrsRtcPlayStream::SrsRtcPlayStream(SrsRtcConnection *s, const SrsContextId &cid) : source_(new SrsRtcSource()) SrsRtcPlayStream::SrsRtcPlayStream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketSender *sender, const SrsContextId &cid) : source_(new SrsRtcSource())
{ {
exec_ = exec;
expire_ = expire;
sender_ = sender;
cid_ = cid; cid_ = cid;
trd_ = NULL; trd_ = NULL;
req_ = NULL; req_ = NULL;
is_started_ = false; is_started_ = false;
session_ = s;
mw_msgs_ = 0; mw_msgs_ = 0;
realtime_ = true; realtime_ = true;
@ -433,16 +444,20 @@ SrsRtcPlayStream::SrsRtcPlayStream(SrsRtcConnection *s, const SrsContextId &cid)
_srs_config->subscribe(this); _srs_config->subscribe(this);
nack_epp_ = new SrsErrorPithyPrint(); nack_epp_ = new SrsErrorPithyPrint();
pli_worker_ = new SrsRtcPLIWorker(this); pli_worker_ = new SrsRtcPliWorker(this);
cache_ssrc0_ = cache_ssrc1_ = cache_ssrc2_ = 0; cache_ssrc0_ = cache_ssrc1_ = cache_ssrc2_ = 0;
cache_track0_ = cache_track1_ = cache_track2_ = NULL; cache_track0_ = cache_track1_ = cache_track2_ = NULL;
config_ = _srs_config;
rtc_sources_ = _srs_rtc_sources;
stat_ = _srs_stat;
} }
SrsRtcPlayStream::~SrsRtcPlayStream() SrsRtcPlayStream::~SrsRtcPlayStream()
{ {
if (req_) { if (req_ && exec_) {
session_->exec_->exec_rtc_async_work(new SrsRtcAsyncCallOnStop(cid_, req_)); exec_->exec_rtc_async_work(new SrsRtcAsyncCallOnStop(cid_, req_));
} }
_srs_config->unsubscribe(this); _srs_config->unsubscribe(this);
@ -467,9 +482,12 @@ SrsRtcPlayStream::~SrsRtcPlayStream()
} }
// update the statistic when client coveried. // update the statistic when client coveried.
SrsStatistic *stat = SrsStatistic::instance();
// TODO: FIXME: Should finger out the err. // TODO: FIXME: Should finger out the err.
stat->on_disconnect(cid_.c_str(), srs_success); stat_->on_disconnect(cid_.c_str(), srs_success);
config_ = NULL;
rtc_sources_ = NULL;
stat_ = NULL;
} }
srs_error_t SrsRtcPlayStream::initialize(ISrsRequest *req, std::map<uint32_t, SrsRtcTrackDescription *> sub_relations) srs_error_t SrsRtcPlayStream::initialize(ISrsRequest *req, std::map<uint32_t, SrsRtcTrackDescription *> sub_relations)
@ -479,12 +497,11 @@ srs_error_t SrsRtcPlayStream::initialize(ISrsRequest *req, std::map<uint32_t, Sr
req_ = req->copy(); req_ = req->copy();
// We must do stat the client before hooks, because hooks depends on it. // We must do stat the client before hooks, because hooks depends on it.
SrsStatistic *stat = SrsStatistic::instance(); if ((err = stat_->on_client(cid_.c_str(), req_, expire_, SrsRtcConnPlay)) != srs_success) {
if ((err = stat->on_client(cid_.c_str(), req_, session_, SrsRtcConnPlay)) != srs_success) {
return srs_error_wrap(err, "rtc: stat client"); return srs_error_wrap(err, "rtc: stat client");
} }
if ((err = _srs_rtc_sources->fetch_or_create(req_, source_)) != srs_success) { if ((err = rtc_sources_->fetch_or_create(req_, source_)) != srs_success) {
return srs_error_wrap(err, "rtc fetch source failed"); return srs_error_wrap(err, "rtc fetch source failed");
} }
@ -493,19 +510,19 @@ srs_error_t SrsRtcPlayStream::initialize(ISrsRequest *req, std::map<uint32_t, Sr
SrsRtcTrackDescription *desc = it->second; SrsRtcTrackDescription *desc = it->second;
if (desc->type_ == "audio") { if (desc->type_ == "audio") {
SrsRtcAudioSendTrack *track = new SrsRtcAudioSendTrack(session_, desc); SrsRtcAudioSendTrack *track = new SrsRtcAudioSendTrack(sender_, desc);
audio_tracks_.insert(make_pair(ssrc, track)); audio_tracks_.insert(make_pair(ssrc, track));
} }
if (desc->type_ == "video") { if (desc->type_ == "video") {
SrsRtcVideoSendTrack *track = new SrsRtcVideoSendTrack(session_, desc); SrsRtcVideoSendTrack *track = new SrsRtcVideoSendTrack(sender_, desc);
video_tracks_.insert(make_pair(ssrc, track)); video_tracks_.insert(make_pair(ssrc, track));
} }
} }
// TODO: FIXME: Support reload. // TODO: FIXME: Support reload.
nack_enabled_ = _srs_config->get_rtc_nack_enabled(req->vhost_); nack_enabled_ = config_->get_rtc_nack_enabled(req->vhost_);
nack_no_copy_ = _srs_config->get_rtc_nack_no_copy(req->vhost_); nack_no_copy_ = config_->get_rtc_nack_no_copy(req->vhost_);
srs_trace("RTC player nack=%d, nnc=%d", nack_enabled_, nack_no_copy_); srs_trace("RTC player nack=%d, nnc=%d", nack_enabled_, nack_no_copy_);
// Setup tracks. // Setup tracks.
@ -641,8 +658,8 @@ srs_error_t SrsRtcPlayStream::cycle()
return srs_error_wrap(err, "dumps consumer, url=%s", req_->get_stream_url().c_str()); return srs_error_wrap(err, "dumps consumer, url=%s", req_->get_stream_url().c_str());
} }
realtime_ = _srs_config->get_realtime_enabled(req_->vhost_, true); realtime_ = config_->get_realtime_enabled(req_->vhost_, true);
mw_msgs_ = _srs_config->get_mw_msgs(req_->vhost_, realtime_, true); mw_msgs_ = config_->get_mw_msgs(req_->vhost_, realtime_, true);
// TODO: FIXME: Add cost in ms. // TODO: FIXME: Add cost in ms.
SrsContextId cid = source->source_id(); SrsContextId cid = source->source_id();
@ -925,7 +942,15 @@ srs_error_t SrsRtcPlayStream::do_request_keyframe(uint32_t ssrc, SrsContextId ci
return err; return err;
} }
SrsRtcPublishRtcpTimer::SrsRtcPublishRtcpTimer(SrsRtcPublishStream *p) : p_(p) ISrsRtcRtcpSender::ISrsRtcRtcpSender()
{
}
ISrsRtcRtcpSender::~ISrsRtcRtcpSender()
{
}
SrsRtcPublishRtcpTimer::SrsRtcPublishRtcpTimer(ISrsRtcRtcpSender *sender) : sender_(sender)
{ {
lock_ = srs_mutex_new(); lock_ = srs_mutex_new();
_srs_shared_timer->timer1s()->subscribe(this); _srs_shared_timer->timer1s()->subscribe(this);
@ -952,19 +977,19 @@ srs_error_t SrsRtcPublishRtcpTimer::on_timer(srs_utime_t interval)
++_srs_pps_pub->sugar_; ++_srs_pps_pub->sugar_;
if (!p_->is_started_) { if (!sender_->is_sender_started()) {
return err; return err;
} }
// For RR and RRTR. // For RR and RRTR.
++_srs_pps_rr->sugar_; ++_srs_pps_rr->sugar_;
if ((err = p_->send_rtcp_rr()) != srs_success) { if ((err = sender_->send_rtcp_rr()) != srs_success) {
srs_warn("RR err %s", srs_error_desc(err).c_str()); srs_warn("RR err %s", srs_error_desc(err).c_str());
srs_freep(err); srs_freep(err);
} }
if ((err = p_->send_rtcp_xr_rrtr()) != srs_success) { if ((err = sender_->send_rtcp_xr_rrtr()) != srs_success) {
srs_warn("XR err %s", srs_error_desc(err).c_str()); srs_warn("XR err %s", srs_error_desc(err).c_str());
srs_freep(err); srs_freep(err);
} }
@ -972,7 +997,7 @@ srs_error_t SrsRtcPublishRtcpTimer::on_timer(srs_utime_t interval)
return err; return err;
} }
SrsRtcPublishTwccTimer::SrsRtcPublishTwccTimer(SrsRtcPublishStream *p) : p_(p) SrsRtcPublishTwccTimer::SrsRtcPublishTwccTimer(ISrsRtcRtcpSender *sender) : sender_(sender)
{ {
lock_ = srs_mutex_new(); lock_ = srs_mutex_new();
_srs_shared_timer->timer100ms()->subscribe(this); _srs_shared_timer->timer100ms()->subscribe(this);
@ -999,12 +1024,12 @@ srs_error_t SrsRtcPublishTwccTimer::on_timer(srs_utime_t interval)
++_srs_pps_pub->sugar_; ++_srs_pps_pub->sugar_;
if (!p_->is_started_) { if (!sender_->is_sender_started()) {
return err; return err;
} }
// For TWCC feedback. // For TWCC feedback.
if (!p_->twcc_enabled_) { if (!sender_->is_sender_twcc_enabled()) {
return err; return err;
} }
@ -1018,7 +1043,7 @@ srs_error_t SrsRtcPublishTwccTimer::on_timer(srs_utime_t interval)
// We should not depends on the received packet, // We should not depends on the received packet,
// instead we should send feedback every Nms. // instead we should send feedback every Nms.
if ((err = p_->send_periodic_twcc()) != srs_success) { if ((err = sender_->send_periodic_twcc()) != srs_success) {
srs_warn("TWCC err %s", srs_error_desc(err).c_str()); srs_warn("TWCC err %s", srs_error_desc(err).c_str());
srs_freep(err); srs_freep(err);
} }
@ -1030,18 +1055,24 @@ SrsRtcAsyncCallOnUnpublish::SrsRtcAsyncCallOnUnpublish(SrsContextId c, ISrsReque
{ {
cid_ = c; cid_ = c;
req_ = r->copy(); req_ = r->copy();
hooks_ = _srs_hooks;
config_ = _srs_config;
} }
SrsRtcAsyncCallOnUnpublish::~SrsRtcAsyncCallOnUnpublish() SrsRtcAsyncCallOnUnpublish::~SrsRtcAsyncCallOnUnpublish()
{ {
srs_freep(req_); srs_freep(req_);
hooks_ = NULL;
config_ = NULL;
} }
srs_error_t SrsRtcAsyncCallOnUnpublish::call() srs_error_t SrsRtcAsyncCallOnUnpublish::call()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
if (!_srs_config->get_vhost_http_hooks_enabled(req_->vhost_)) { if (!config_->get_vhost_http_hooks_enabled(req_->vhost_)) {
return err; return err;
} }
@ -1051,7 +1082,7 @@ srs_error_t SrsRtcAsyncCallOnUnpublish::call()
vector<string> hooks; vector<string> hooks;
if (true) { if (true) {
SrsConfDirective *conf = _srs_config->get_vhost_on_unpublish(req_->vhost_); SrsConfDirective *conf = config_->get_vhost_on_unpublish(req_->vhost_);
if (!conf) { if (!conf) {
return err; return err;
@ -1065,7 +1096,7 @@ srs_error_t SrsRtcAsyncCallOnUnpublish::call()
for (int i = 0; i < (int)hooks.size(); i++) { for (int i = 0; i < (int)hooks.size(); i++) {
std::string url = hooks.at(i); std::string url = hooks.at(i);
_srs_hooks->on_unpublish(url, req_); hooks_->on_unpublish(url, req_);
} }
return err; return err;
@ -1076,11 +1107,14 @@ std::string SrsRtcAsyncCallOnUnpublish::to_string()
return std::string(""); return std::string("");
} }
SrsRtcPublishStream::SrsRtcPublishStream(SrsRtcConnection *session, const SrsContextId &cid) : source_(new SrsRtcSource()) SrsRtcPublishStream::SrsRtcPublishStream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketReceiver *receiver, const SrsContextId &cid) : source_(new SrsRtcSource())
{ {
exec_ = exec;
expire_ = expire;
receiver_ = receiver;
cid_ = cid; cid_ = cid;
is_started_ = false; is_sender_started_ = false;
session_ = session;
request_keyframe_ = false; request_keyframe_ = false;
pli_epp_ = new SrsErrorPithyPrint(); pli_epp_ = new SrsErrorPithyPrint();
twcc_epp_ = new SrsErrorPithyPrint(3.0); twcc_epp_ = new SrsErrorPithyPrint(3.0);
@ -1096,17 +1130,24 @@ SrsRtcPublishStream::SrsRtcPublishStream(SrsRtcConnection *session, const SrsCon
twcc_id_ = 0; twcc_id_ = 0;
twcc_fb_count_ = 0; twcc_fb_count_ = 0;
pli_worker_ = new SrsRtcPLIWorker(this); pli_worker_ = new SrsRtcPliWorker(this);
last_time_send_twcc_ = 0; last_time_send_twcc_ = 0;
timer_rtcp_ = new SrsRtcPublishRtcpTimer(this); timer_rtcp_ = new SrsRtcPublishRtcpTimer(this);
timer_twcc_ = new SrsRtcPublishTwccTimer(this); timer_twcc_ = new SrsRtcPublishTwccTimer(this);
stat_ = _srs_stat;
config_ = _srs_config;
rtc_sources_ = _srs_rtc_sources;
live_sources_ = _srs_sources;
srt_sources_ = _srs_srt_sources;
circuit_breaker_ = _srs_circuit_breaker;
} }
SrsRtcPublishStream::~SrsRtcPublishStream() SrsRtcPublishStream::~SrsRtcPublishStream()
{ {
if (req_) { if (req_ && exec_) {
session_->exec_->exec_rtc_async_work(new SrsRtcAsyncCallOnUnpublish(cid_, req_)); exec_->exec_rtc_async_work(new SrsRtcAsyncCallOnUnpublish(cid_, req_));
} }
srs_freep(timer_rtcp_); srs_freep(timer_rtcp_);
@ -1133,9 +1174,16 @@ SrsRtcPublishStream::~SrsRtcPublishStream()
srs_freep(req_); srs_freep(req_);
// update the statistic when client coveried. // update the statistic when client coveried.
SrsStatistic *stat = SrsStatistic::instance();
// TODO: FIXME: Should finger out the err. // TODO: FIXME: Should finger out the err.
stat->on_disconnect(cid_.c_str(), srs_success); stat_->on_disconnect(cid_.c_str(), srs_success);
// Optional but just to make it clear.
stat_ = NULL;
config_ = NULL;
rtc_sources_ = NULL;
live_sources_ = NULL;
srt_sources_ = NULL;
circuit_breaker_ = NULL;
} }
srs_error_t SrsRtcPublishStream::initialize(ISrsRequest *r, SrsRtcSourceDescription *stream_desc) srs_error_t SrsRtcPublishStream::initialize(ISrsRequest *r, SrsRtcSourceDescription *stream_desc)
@ -1145,18 +1193,17 @@ srs_error_t SrsRtcPublishStream::initialize(ISrsRequest *r, SrsRtcSourceDescript
req_ = r->copy(); req_ = r->copy();
// We must do stat the client before hooks, because hooks depends on it. // We must do stat the client before hooks, because hooks depends on it.
SrsStatistic *stat = SrsStatistic::instance(); if ((err = stat_->on_client(cid_.c_str(), req_, expire_, SrsRtcConnPublish)) != srs_success) {
if ((err = stat->on_client(cid_.c_str(), req_, session_, SrsRtcConnPublish)) != srs_success) {
return srs_error_wrap(err, "rtc: stat client"); return srs_error_wrap(err, "rtc: stat client");
} }
if (stream_desc->audio_track_desc_) { if (stream_desc->audio_track_desc_) {
audio_tracks_.push_back(new SrsRtcAudioRecvTrack(session_, stream_desc->audio_track_desc_)); audio_tracks_.push_back(new SrsRtcAudioRecvTrack(receiver_, stream_desc->audio_track_desc_));
} }
for (int i = 0; i < (int)stream_desc->video_track_descs_.size(); ++i) { for (int i = 0; i < (int)stream_desc->video_track_descs_.size(); ++i) {
SrsRtcTrackDescription *desc = stream_desc->video_track_descs_.at(i); SrsRtcTrackDescription *desc = stream_desc->video_track_descs_.at(i);
video_tracks_.push_back(new SrsRtcVideoRecvTrack(session_, desc)); video_tracks_.push_back(new SrsRtcVideoRecvTrack(receiver_, desc));
} }
int twcc_id = -1; int twcc_id = -1;
@ -1175,10 +1222,10 @@ srs_error_t SrsRtcPublishStream::initialize(ISrsRequest *r, SrsRtcSourceDescript
rtcp_twcc_.set_media_ssrc(media_ssrc); rtcp_twcc_.set_media_ssrc(media_ssrc);
} }
nack_enabled_ = _srs_config->get_rtc_nack_enabled(req_->vhost_); nack_enabled_ = config_->get_rtc_nack_enabled(req_->vhost_);
nack_no_copy_ = _srs_config->get_rtc_nack_no_copy(req_->vhost_); nack_no_copy_ = config_->get_rtc_nack_no_copy(req_->vhost_);
pt_to_drop_ = (uint16_t)_srs_config->get_rtc_drop_for_pt(req_->vhost_); pt_to_drop_ = (uint16_t)config_->get_rtc_drop_for_pt(req_->vhost_);
twcc_enabled_ = _srs_config->get_rtc_twcc_enabled(req_->vhost_); twcc_enabled_ = config_->get_rtc_twcc_enabled(req_->vhost_);
// No TWCC when negotiate, disable it. // No TWCC when negotiate, disable it.
if (twcc_id <= 0) { if (twcc_id <= 0) {
@ -1199,14 +1246,14 @@ srs_error_t SrsRtcPublishStream::initialize(ISrsRequest *r, SrsRtcSourceDescript
} }
// Setup the publish stream in source to enable PLI as such. // Setup the publish stream in source to enable PLI as such.
if ((err = _srs_rtc_sources->fetch_or_create(req_, source_)) != srs_success) { if ((err = rtc_sources_->fetch_or_create(req_, source_)) != srs_success) {
return srs_error_wrap(err, "create source"); return srs_error_wrap(err, "create source");
} }
source_->set_publish_stream(this); source_->set_publish_stream(this);
// TODO: FIMXE: Check it in SrsRtcConnection::add_publisher? // TODO: FIMXE: Check it in SrsRtcConnection::add_publisher?
SrsSharedPtr<SrsLiveSource> live_source; SrsSharedPtr<SrsLiveSource> live_source;
if ((err = _srs_sources->fetch_or_create(r, live_source)) != srs_success) { if ((err = live_sources_->fetch_or_create(r, live_source)) != srs_success) {
return srs_error_wrap(err, "create live source"); return srs_error_wrap(err, "create live source");
} }
if (!live_source->can_publish(false)) { if (!live_source->can_publish(false)) {
@ -1214,11 +1261,11 @@ srs_error_t SrsRtcPublishStream::initialize(ISrsRequest *r, SrsRtcSourceDescript
} }
// Check whether SRT stream is busy. // Check whether SRT stream is busy.
bool srt_server_enabled = _srs_config->get_srt_enabled(); bool srt_server_enabled = config_->get_srt_enabled();
bool srt_enabled = _srs_config->get_srt_enabled(r->vhost_); bool srt_enabled = config_->get_srt_enabled(r->vhost_);
if (srt_server_enabled && srt_enabled) { if (srt_server_enabled && srt_enabled) {
SrsSharedPtr<SrsSrtSource> srt; SrsSharedPtr<SrsSrtSource> srt;
if ((err = _srs_srt_sources->fetch_or_create(r, srt)) != srs_success) { if ((err = srt_sources_->fetch_or_create(r, srt)) != srs_success) {
return srs_error_wrap(err, "create source"); return srs_error_wrap(err, "create source");
} }
@ -1232,7 +1279,7 @@ srs_error_t SrsRtcPublishStream::initialize(ISrsRequest *r, SrsRtcSourceDescript
// Bridge to RTMP. // Bridge to RTMP.
// TODO: Support bridge to RTSP. // TODO: Support bridge to RTSP.
bool rtc_to_rtmp = _srs_config->get_rtc_to_rtmp(req_->vhost_); bool rtc_to_rtmp = config_->get_rtc_to_rtmp(req_->vhost_);
if (rtc_to_rtmp) { if (rtc_to_rtmp) {
// Disable GOP cache for RTC2RTMP bridge, to keep the streams in sync, // Disable GOP cache for RTC2RTMP bridge, to keep the streams in sync,
// especially for stream merging. // especially for stream merging.
@ -1263,7 +1310,7 @@ srs_error_t SrsRtcPublishStream::start()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
if (is_started_) { if (is_sender_started_) {
return err; return err;
} }
@ -1275,7 +1322,7 @@ srs_error_t SrsRtcPublishStream::start()
return srs_error_wrap(err, "start pli worker"); return srs_error_wrap(err, "start pli worker");
} }
is_started_ = true; is_sender_started_ = true;
return err; return err;
} }
@ -1314,6 +1361,16 @@ const SrsContextId &SrsRtcPublishStream::context_id()
return cid_; return cid_;
} }
bool SrsRtcPublishStream::is_sender_twcc_enabled()
{
return twcc_enabled_;
}
bool SrsRtcPublishStream::is_sender_started()
{
return is_sender_started_;
}
srs_error_t SrsRtcPublishStream::send_rtcp_rr() srs_error_t SrsRtcPublishStream::send_rtcp_rr()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
@ -1466,7 +1523,7 @@ srs_error_t SrsRtcPublishStream::do_on_rtp_plaintext(SrsRtpPacket *&pkt, SrsBuff
} }
// If circuit-breaker is enabled, disable nack. // If circuit-breaker is enabled, disable nack.
if (_srs_circuit_breaker->hybrid_critical_water_level()) { if (circuit_breaker_->hybrid_critical_water_level()) {
++_srs_pps_snack4->sugar_; ++_srs_pps_snack4->sugar_;
return err; return err;
} }
@ -1563,7 +1620,7 @@ srs_error_t SrsRtcPublishStream::send_periodic_twcc()
return srs_error_wrap(err, "encode, count=%u", twcc_fb_count_); return srs_error_wrap(err, "encode, count=%u", twcc_fb_count_);
} }
if ((err = session_->send_rtcp(pkt, buffer->pos())) != srs_success) { if ((err = receiver_->send_rtcp(pkt, buffer->pos())) != srs_success) {
return srs_error_wrap(err, "send twcc, count=%u", twcc_fb_count_); return srs_error_wrap(err, "send twcc, count=%u", twcc_fb_count_);
} }
} }
@ -1678,7 +1735,7 @@ void SrsRtcPublishStream::request_keyframe(uint32_t ssrc, SrsContextId cid)
srs_error_t SrsRtcPublishStream::do_request_keyframe(uint32_t ssrc, SrsContextId sub_cid) srs_error_t SrsRtcPublishStream::do_request_keyframe(uint32_t ssrc, SrsContextId sub_cid)
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
if ((err = session_->send_rtcp_fb_pli(ssrc, sub_cid)) != srs_success) { if ((err = receiver_->send_rtcp_fb_pli(ssrc, sub_cid)) != srs_success) {
srs_warn("PLI err %s", srs_error_desc(err).c_str()); srs_warn("PLI err %s", srs_error_desc(err).c_str());
srs_freep(err); srs_freep(err);
} }
@ -1753,16 +1810,27 @@ void SrsRtcPublishStream::update_send_report_time(uint32_t ssrc, const SrsNtp &n
SrsRtcConnectionNackTimer::SrsRtcConnectionNackTimer(SrsRtcConnection *p) : p_(p) SrsRtcConnectionNackTimer::SrsRtcConnectionNackTimer(SrsRtcConnection *p) : p_(p)
{ {
lock_ = srs_mutex_new(); lock_ = srs_mutex_new();
_srs_shared_timer->timer20ms()->subscribe(this);
shared_timer_ = _srs_shared_timer;
circuit_breaker_ = _srs_circuit_breaker;
} }
SrsRtcConnectionNackTimer::~SrsRtcConnectionNackTimer() SrsRtcConnectionNackTimer::~SrsRtcConnectionNackTimer()
{ {
if (true) { if (true) {
SrsLocker(&lock_); SrsLocker(&lock_);
_srs_shared_timer->timer20ms()->unsubscribe(this); shared_timer_->timer20ms()->unsubscribe(this);
} }
srs_mutex_destroy(lock_); srs_mutex_destroy(lock_);
shared_timer_ = NULL;
circuit_breaker_ = NULL;
}
srs_error_t SrsRtcConnectionNackTimer::initialize()
{
shared_timer_->timer20ms()->subscribe(this);
return srs_success;
} }
srs_error_t SrsRtcConnectionNackTimer::on_timer(srs_utime_t interval) srs_error_t SrsRtcConnectionNackTimer::on_timer(srs_utime_t interval)
@ -1782,7 +1850,7 @@ srs_error_t SrsRtcConnectionNackTimer::on_timer(srs_utime_t interval)
++_srs_pps_conn->sugar_; ++_srs_pps_conn->sugar_;
// If circuit-breaker is enabled, disable nack. // If circuit-breaker is enabled, disable nack.
if (_srs_circuit_breaker->hybrid_critical_water_level()) { if (circuit_breaker_->hybrid_critical_water_level()) {
++_srs_pps_snack4->sugar_; ++_srs_pps_snack4->sugar_;
return err; return err;
} }
@ -2072,6 +2140,10 @@ srs_error_t SrsRtcConnection::initialize(ISrsRequest *r, bool dtls, bool srtp, s
nack_enabled_ = _srs_config->get_rtc_nack_enabled(req_->vhost_); nack_enabled_ = _srs_config->get_rtc_nack_enabled(req_->vhost_);
if ((err = timer_nack_->initialize()) != srs_success) {
return srs_error_wrap(err, "initialize timer nack");
}
srs_trace("RTC init session, user=%s, url=%s, encrypt=%u/%u, DTLS(role=%s, version=%s), timeout=%dms, nack=%d", srs_trace("RTC init session, user=%s, url=%s, encrypt=%u/%u, DTLS(role=%s, version=%s), timeout=%dms, nack=%d",
username.c_str(), r->get_stream_url().c_str(), dtls, srtp, cfg->dtls_role_.c_str(), cfg->dtls_version_.c_str(), username.c_str(), r->get_stream_url().c_str(), dtls, srtp, cfg->dtls_role_.c_str(), cfg->dtls_version_.c_str(),
srsu2msi(session_timeout_), nack_enabled_); srsu2msi(session_timeout_), nack_enabled_);
@ -3530,7 +3602,7 @@ srs_error_t SrsRtcConnection::create_player(ISrsRequest *req, std::map<uint32_t,
return err; return err;
} }
SrsRtcPlayStream *player = new SrsRtcPlayStream(this, _srs_context->get_id()); SrsRtcPlayStream *player = new SrsRtcPlayStream(exec_, this, this, _srs_context->get_id());
if ((err = player->initialize(req, sub_relations)) != srs_success) { if ((err = player->initialize(req, sub_relations)) != srs_success) {
srs_freep(player); srs_freep(player);
return srs_error_wrap(err, "SrsRtcPlayStream init"); return srs_error_wrap(err, "SrsRtcPlayStream init");
@ -3598,7 +3670,7 @@ srs_error_t SrsRtcConnection::create_publisher(ISrsRequest *req, SrsRtcSourceDes
return err; return err;
} }
SrsRtcPublishStream *publisher = new SrsRtcPublishStream(this, _srs_context->get_id()); SrsRtcPublishStream *publisher = new SrsRtcPublishStream(exec_, this, this, _srs_context->get_id());
if ((err = publisher->initialize(req, stream_desc)) != srs_success) { if ((err = publisher->initialize(req, stream_desc)) != srs_success) {
srs_freep(publisher); srs_freep(publisher);
return srs_error_wrap(err, "rtc publisher init"); return srs_error_wrap(err, "rtc publisher init");

View File

@ -56,6 +56,12 @@ class SrsRtcUdpNetwork;
class ISrsRtcNetwork; class ISrsRtcNetwork;
class SrsRtcTcpNetwork; class SrsRtcTcpNetwork;
class SrsStreamPublishToken; class SrsStreamPublishToken;
class ISrsHttpHooks;
class ISrsAppConfig;
class ISrsStatistic;
class ISrsExecRtcAsyncTask;
class ISrsSrtSourceManager;
class ISrsLiveSourceManager;
const uint8_t kSR = 200; const uint8_t kSR = 200;
const uint8_t kRR = 201; const uint8_t kRR = 201;
@ -97,8 +103,8 @@ class SrsSecurityTransport : public ISrsRtcTransport
{ {
private: private:
ISrsRtcNetwork *network_; ISrsRtcNetwork *network_;
SrsDtls *dtls_; ISrsDtls *dtls_;
SrsSRTP *srtp_; ISrsSRTP *srtp_;
bool handshake_done_; bool handshake_done_;
public: public:
@ -171,31 +177,31 @@ public:
}; };
// The handler for PLI worker coroutine. // The handler for PLI worker coroutine.
class ISrsRtcPLIWorkerHandler class ISrsRtcPliWorkerHandler
{ {
public: public:
ISrsRtcPLIWorkerHandler(); ISrsRtcPliWorkerHandler();
virtual ~ISrsRtcPLIWorkerHandler(); virtual ~ISrsRtcPliWorkerHandler();
public: public:
virtual srs_error_t do_request_keyframe(uint32_t ssrc, SrsContextId cid) = 0; virtual srs_error_t do_request_keyframe(uint32_t ssrc, SrsContextId cid) = 0;
}; };
// A worker coroutine to request the PLI. // A worker coroutine to request the PLI.
class SrsRtcPLIWorker : public ISrsCoroutineHandler class SrsRtcPliWorker : public ISrsCoroutineHandler
{ {
private: private:
ISrsCoroutine *trd_; ISrsCoroutine *trd_;
srs_cond_t wait_; ISrsCond *wait_;
ISrsRtcPLIWorkerHandler *handler_; ISrsRtcPliWorkerHandler *handler_;
private: private:
// Key is SSRC, value is the CID of subscriber which requests PLI. // Key is SSRC, value is the CID of subscriber which requests PLI.
std::map<uint32_t, SrsContextId> plis_; std::map<uint32_t, SrsContextId> plis_;
public: public:
SrsRtcPLIWorker(ISrsRtcPLIWorkerHandler *h); SrsRtcPliWorker(ISrsRtcPliWorkerHandler *h);
virtual ~SrsRtcPLIWorker(); virtual ~SrsRtcPliWorker();
public: public:
virtual srs_error_t start(); virtual srs_error_t start();
@ -211,6 +217,9 @@ class SrsRtcAsyncCallOnStop : public ISrsAsyncCallTask
private: private:
SrsContextId cid_; SrsContextId cid_;
ISrsRequest *req_; ISrsRequest *req_;
ISrsHttpHooks *hooks_;
ISrsContext *context_;
ISrsAppConfig *config_;
public: public:
SrsRtcAsyncCallOnStop(SrsContextId c, ISrsRequest *r); SrsRtcAsyncCallOnStop(SrsContextId c, ISrsRequest *r);
@ -222,13 +231,22 @@ public:
}; };
// A RTC play stream, client pull and play stream from SRS. // A RTC play stream, client pull and play stream from SRS.
class SrsRtcPlayStream : public ISrsCoroutineHandler, public ISrsReloadHandler, public ISrsRtcPLIWorkerHandler, public ISrsRtcSourceChangeCallback class SrsRtcPlayStream : public ISrsCoroutineHandler, public ISrsReloadHandler, public ISrsRtcPliWorkerHandler, public ISrsRtcSourceChangeCallback
{ {
private:
ISrsExecRtcAsyncTask *exec_;
ISrsExpire *expire_;
ISrsRtcPacketSender *sender_;
private:
ISrsAppConfig *config_;
ISrsRtcSourceManager *rtc_sources_;
ISrsStatistic *stat_;
private: private:
SrsContextId cid_; SrsContextId cid_;
SrsFastCoroutine *trd_; SrsFastCoroutine *trd_;
SrsRtcConnection *session_; SrsRtcPliWorker *pli_worker_;
SrsRtcPLIWorker *pli_worker_;
private: private:
ISrsRequest *req_; ISrsRequest *req_;
@ -261,7 +279,7 @@ private:
bool is_started_; bool is_started_;
public: public:
SrsRtcPlayStream(SrsRtcConnection *s, const SrsContextId &cid); SrsRtcPlayStream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketSender *sender, const SrsContextId &cid);
virtual ~SrsRtcPlayStream(); virtual ~SrsRtcPlayStream();
public: public:
@ -296,37 +314,54 @@ private:
srs_error_t on_rtcp_ps_feedback(SrsRtcpFbCommon *rtcp); srs_error_t on_rtcp_ps_feedback(SrsRtcpFbCommon *rtcp);
srs_error_t on_rtcp_rr(SrsRtcpRR *rtcp); srs_error_t on_rtcp_rr(SrsRtcpRR *rtcp);
uint32_t get_video_publish_ssrc(uint32_t play_ssrc); uint32_t get_video_publish_ssrc(uint32_t play_ssrc);
// Interface ISrsRtcPLIWorkerHandler // Interface ISrsRtcPliWorkerHandler
public: public:
virtual srs_error_t do_request_keyframe(uint32_t ssrc, SrsContextId cid); virtual srs_error_t do_request_keyframe(uint32_t ssrc, SrsContextId cid);
}; };
// The RTC RTCP sender interface.
class ISrsRtcRtcpSender
{
public:
ISrsRtcRtcpSender();
virtual ~ISrsRtcRtcpSender();
public:
virtual bool is_sender_started() = 0;
virtual srs_error_t send_rtcp_rr() = 0;
virtual srs_error_t send_rtcp_xr_rrtr() = 0;
public:
virtual bool is_sender_twcc_enabled() = 0;
virtual srs_error_t send_periodic_twcc() = 0;
};
// A fast timer for publish stream, for RTCP feedback. // A fast timer for publish stream, for RTCP feedback.
class SrsRtcPublishRtcpTimer : public ISrsFastTimer class SrsRtcPublishRtcpTimer : public ISrsFastTimerHandler
{ {
private: private:
SrsRtcPublishStream *p_; ISrsRtcRtcpSender *sender_;
srs_mutex_t lock_; srs_mutex_t lock_;
public: public:
SrsRtcPublishRtcpTimer(SrsRtcPublishStream *p); SrsRtcPublishRtcpTimer(ISrsRtcRtcpSender *sender);
virtual ~SrsRtcPublishRtcpTimer(); virtual ~SrsRtcPublishRtcpTimer();
// interface ISrsFastTimer // interface ISrsFastTimerHandler
private: private:
srs_error_t on_timer(srs_utime_t interval); srs_error_t on_timer(srs_utime_t interval);
}; };
// A fast timer for publish stream, for TWCC feedback. // A fast timer for publish stream, for TWCC feedback.
class SrsRtcPublishTwccTimer : public ISrsFastTimer class SrsRtcPublishTwccTimer : public ISrsFastTimerHandler
{ {
private: private:
SrsRtcPublishStream *p_; ISrsRtcRtcpSender *sender_;
srs_mutex_t lock_; srs_mutex_t lock_;
public: public:
SrsRtcPublishTwccTimer(SrsRtcPublishStream *p); SrsRtcPublishTwccTimer(ISrsRtcRtcpSender *sender);
virtual ~SrsRtcPublishTwccTimer(); virtual ~SrsRtcPublishTwccTimer();
// interface ISrsFastTimer // interface ISrsFastTimerHandler
private: private:
srs_error_t on_timer(srs_utime_t interval); srs_error_t on_timer(srs_utime_t interval);
}; };
@ -334,6 +369,10 @@ private:
// the rtc on_unpublish async call. // the rtc on_unpublish async call.
class SrsRtcAsyncCallOnUnpublish : public ISrsAsyncCallTask class SrsRtcAsyncCallOnUnpublish : public ISrsAsyncCallTask
{ {
private:
ISrsHttpHooks *hooks_;
ISrsAppConfig *config_;
private: private:
SrsContextId cid_; SrsContextId cid_;
ISrsRequest *req_; ISrsRequest *req_;
@ -348,8 +387,21 @@ public:
}; };
// A RTC publish stream, client push and publish stream to SRS. // A RTC publish stream, client push and publish stream to SRS.
class SrsRtcPublishStream : public ISrsRtpPacketDecodeHandler, public ISrsRtcPublishStream, public ISrsRtcPLIWorkerHandler class SrsRtcPublishStream : public ISrsRtpPacketDecodeHandler, public ISrsRtcPublishStream, public ISrsRtcPliWorkerHandler, public ISrsRtcRtcpSender
{ {
private:
ISrsExecRtcAsyncTask *exec_;
ISrsExpire *expire_;
ISrsRtcPacketReceiver *receiver_;
ISrsCircuitBreaker *circuit_breaker_;
private:
ISrsStatistic *stat_;
ISrsAppConfig *config_;
ISrsRtcSourceManager *rtc_sources_;
ISrsLiveSourceManager *live_sources_;
ISrsSrtSourceManager *srt_sources_;
private: private:
friend class SrsRtcPublishRtcpTimer; friend class SrsRtcPublishRtcpTimer;
friend class SrsRtcPublishTwccTimer; friend class SrsRtcPublishTwccTimer;
@ -359,11 +411,10 @@ private:
private: private:
SrsContextId cid_; SrsContextId cid_;
uint64_t nn_audio_frames_; uint64_t nn_audio_frames_;
SrsRtcPLIWorker *pli_worker_; SrsRtcPliWorker *pli_worker_;
SrsErrorPithyPrint *twcc_epp_; SrsErrorPithyPrint *twcc_epp_;
private: private:
SrsRtcConnection *session_;
uint16_t pt_to_drop_; uint16_t pt_to_drop_;
// Whether enabled nack. // Whether enabled nack.
bool nack_enabled_; bool nack_enabled_;
@ -390,11 +441,11 @@ private:
uint8_t twcc_fb_count_; uint8_t twcc_fb_count_;
SrsRtcpTWCC rtcp_twcc_; SrsRtcpTWCC rtcp_twcc_;
SrsRtpExtensionTypes extension_types_; SrsRtpExtensionTypes extension_types_;
bool is_started_; bool is_sender_started_;
srs_utime_t last_time_send_twcc_; srs_utime_t last_time_send_twcc_;
public: public:
SrsRtcPublishStream(SrsRtcConnection *session, const SrsContextId &cid); SrsRtcPublishStream(ISrsExecRtcAsyncTask *exec, ISrsExpire *expire, ISrsRtcPacketReceiver *receiver, const SrsContextId &cid);
virtual ~SrsRtcPublishStream(); virtual ~SrsRtcPublishStream();
public: public:
@ -405,6 +456,8 @@ public:
virtual const SrsContextId &context_id(); virtual const SrsContextId &context_id();
private: private:
bool is_sender_started();
bool is_sender_twcc_enabled();
srs_error_t send_rtcp_rr(); srs_error_t send_rtcp_rr();
srs_error_t send_rtcp_xr_rrtr(); srs_error_t send_rtcp_xr_rrtr();
@ -450,8 +503,12 @@ private:
}; };
// A fast timer for conntion, for NACK feedback. // A fast timer for conntion, for NACK feedback.
class SrsRtcConnectionNackTimer : public ISrsFastTimer class SrsRtcConnectionNackTimer : public ISrsFastTimerHandler
{ {
private:
ISrsSharedTimer *shared_timer_;
ISrsCircuitBreaker *circuit_breaker_;
private: private:
SrsRtcConnection *p_; SrsRtcConnection *p_;
srs_mutex_t lock_; srs_mutex_t lock_;
@ -459,7 +516,11 @@ private:
public: public:
SrsRtcConnectionNackTimer(SrsRtcConnection *p); SrsRtcConnectionNackTimer(SrsRtcConnection *p);
virtual ~SrsRtcConnectionNackTimer(); virtual ~SrsRtcConnectionNackTimer();
// interface ISrsFastTimer
public:
virtual srs_error_t initialize();
// interface ISrsFastTimerHandler
private: private:
srs_error_t on_timer(srs_utime_t interval); srs_error_t on_timer(srs_utime_t interval);
}; };
@ -479,11 +540,9 @@ public:
// //
// For performance, we use non-public from resource, // For performance, we use non-public from resource,
// see https://stackoverflow.com/questions/3747066/c-cannot-convert-from-base-a-to-derived-type-b-via-virtual-base-a // see https://stackoverflow.com/questions/3747066/c-cannot-convert-from-base-a-to-derived-type-b-via-virtual-base-a
class SrsRtcConnection : public ISrsResource, public ISrsDisposingHandler, public ISrsExpire class SrsRtcConnection : public ISrsResource, public ISrsDisposingHandler, public ISrsExpire, public ISrsRtcPacketSender, public ISrsRtcPacketReceiver
{ {
friend class SrsSecurityTransport; friend class SrsSecurityTransport;
friend class SrsRtcPlayStream;
friend class SrsRtcPublishStream;
private: private:
friend class SrsRtcConnectionNackTimer; friend class SrsRtcConnectionNackTimer;

View File

@ -912,6 +912,14 @@ srs_error_t SrsDtlsEmptyImpl::start_arq()
return srs_success; return srs_success;
} }
ISrsDtls::ISrsDtls()
{
}
ISrsDtls::~ISrsDtls()
{
}
SrsDtls::SrsDtls(ISrsDtlsCallback *callback) SrsDtls::SrsDtls(ISrsDtlsCallback *callback)
{ {
callback_ = callback; callback_ = callback;
@ -950,6 +958,14 @@ srs_error_t SrsDtls::get_srtp_key(std::string &recv_key, std::string &send_key)
return impl_->get_srtp_key(recv_key, send_key); return impl_->get_srtp_key(recv_key, send_key);
} }
ISrsSRTP::ISrsSRTP()
{
}
ISrsSRTP::~ISrsSRTP()
{
}
SrsSRTP::SrsSRTP() SrsSRTP::SrsSRTP()
{ {
recv_ctx_ = NULL; recv_ctx_ = NULL;

View File

@ -201,7 +201,22 @@ protected:
virtual srs_error_t start_arq(); virtual srs_error_t start_arq();
}; };
class SrsDtls // The interface for DTLS.
class ISrsDtls
{
public:
ISrsDtls();
virtual ~ISrsDtls();
public:
virtual srs_error_t initialize(std::string role, std::string version) = 0;
virtual srs_error_t start_active_handshake() = 0;
virtual srs_error_t on_dtls(char *data, int nb_data) = 0;
virtual srs_error_t get_srtp_key(std::string &recv_key, std::string &send_key) = 0;
};
// The DTLS transport.
class SrsDtls : public ISrsDtls
{ {
private: private:
SrsDtlsImpl *impl_; SrsDtlsImpl *impl_;
@ -225,7 +240,23 @@ public:
srs_error_t get_srtp_key(std::string &recv_key, std::string &send_key); srs_error_t get_srtp_key(std::string &recv_key, std::string &send_key);
}; };
class SrsSRTP // The interface for SRTP.
class ISrsSRTP
{
public:
ISrsSRTP();
virtual ~ISrsSRTP();
public:
virtual srs_error_t initialize(std::string recv_key, std::string send_key) = 0;
virtual srs_error_t protect_rtp(void *packet, int *nb_cipher) = 0;
virtual srs_error_t protect_rtcp(void *packet, int *nb_cipher) = 0;
virtual srs_error_t unprotect_rtp(void *packet, int *nb_plaintext) = 0;
virtual srs_error_t unprotect_rtcp(void *packet, int *nb_plaintext) = 0;
};
// The SRTP transport.
class SrsSRTP : public ISrsSRTP
{ {
private: private:
srtp_t recv_ctx_; srtp_t recv_ctx_;

View File

@ -789,8 +789,8 @@ srs_error_t SrsRtcTcpConn::cycle()
srs_error_t err = do_cycle(); srs_error_t err = do_cycle();
// Only stat the HTTP streaming clients, ignore all API clients. // Only stat the HTTP streaming clients, ignore all API clients.
SrsStatistic::instance()->on_disconnect(get_id().c_str(), err); _srs_stat->on_disconnect(get_id().c_str(), err);
SrsStatistic::instance()->kbps_add_delta(get_id().c_str(), delta_); _srs_stat->kbps_add_delta(get_id().c_str(), delta_);
// Only remove session when network is established, because client might use other UDP network. // Only remove session when network is established, because client might use other UDP network.
if (session_ && session_->tcp()->is_establelished()) { if (session_ && session_->tcp()->is_establelished()) {

View File

@ -253,6 +253,14 @@ void SrsRtcConsumer::on_stream_change(SrsRtcSourceDescription *desc)
} }
} }
ISrsRtcSourceManager::ISrsRtcSourceManager()
{
}
ISrsRtcSourceManager::~ISrsRtcSourceManager()
{
}
SrsRtcSourceManager::SrsRtcSourceManager() SrsRtcSourceManager::SrsRtcSourceManager()
{ {
lock_ = srs_mutex_new(); lock_ = srs_mutex_new();
@ -700,7 +708,7 @@ srs_error_t SrsRtcSource::on_publish()
_srs_shared_timer->timer100ms()->subscribe(this); _srs_shared_timer->timer100ms()->subscribe(this);
} }
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->on_stream_publish(req_, _source_id.c_str()); stat->on_stream_publish(req_, _source_id.c_str());
return err; return err;
@ -734,7 +742,7 @@ void SrsRtcSource::on_unpublish()
srs_freep(rtc_bridge_); srs_freep(rtc_bridge_);
} }
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->on_stream_close(req_); stat->on_stream_close(req_);
// Destroy and cleanup source when no publishers and consumers. // Destroy and cleanup source when no publishers and consumers.
@ -1467,6 +1475,9 @@ SrsRtpPacket *SrsRtcFrameBuilderVideoPacketCache::take_packet(uint16_t sequence_
return pkt; return pkt;
} }
// TODO: Should improve the frame builder by jitter and NACK, consider if only RTC2RTMP without
// any RTC player, then if one RTP packet is lost, the frame builder will not request the lost
// packet. This makes the whole GOP not available.
int32_t SrsRtcFrameBuilderVideoPacketCache::find_next_lost_sn(uint16_t current_sn, uint16_t header_sn, uint16_t &end_sn) int32_t SrsRtcFrameBuilderVideoPacketCache::find_next_lost_sn(uint16_t current_sn, uint16_t header_sn, uint16_t &end_sn)
{ {
uint32_t last_rtp_ts = get_rtp_timestamp(header_sn); uint32_t last_rtp_ts = get_rtp_timestamp(header_sn);
@ -1548,7 +1559,8 @@ void SrsRtcFrameBuilderVideoFrameDetector::on_keyframe_start(SrsRtpPacket *pkt)
rtp_key_frame_ts_ = pkt->header_.get_timestamp(); rtp_key_frame_ts_ = pkt->header_.get_timestamp();
header_sn_ = pkt->header_.get_sequence(); header_sn_ = pkt->header_.get_sequence();
lost_sn_ = header_sn_ + 1; lost_sn_ = header_sn_ + 1;
// Received key frame and clean cache of old p frame pkts // Received key frame and clean cache of old p frame pkts.
// TODO: Should use jitter buffer to avoid clear previous P frame in case of reordering.
video_cache_->clear_all(); video_cache_->clear_all();
srs_trace("RTC2RTMP: keyframe set ts=%u, header=%hu, lost=%hu", (uint32_t)rtp_key_frame_ts_, header_sn_, lost_sn_); srs_trace("RTC2RTMP: keyframe set ts=%u, header=%hu, lost=%hu", (uint32_t)rtp_key_frame_ts_, header_sn_, lost_sn_);
} else if (rtp_key_frame_ts_ != pkt->header_.get_timestamp()) { } else if (rtp_key_frame_ts_ != pkt->header_.get_timestamp()) {
@ -1559,6 +1571,8 @@ void SrsRtcFrameBuilderVideoFrameDetector::on_keyframe_start(SrsRtpPacket *pkt)
rtp_key_frame_ts_ = pkt->header_.get_timestamp(); rtp_key_frame_ts_ = pkt->header_.get_timestamp();
header_sn_ = pkt->header_.get_sequence(); header_sn_ = pkt->header_.get_sequence();
lost_sn_ = header_sn_ + 1; lost_sn_ = header_sn_ + 1;
// Received key frame and clean cache of old p frame pkts.
// TODO: Should use jitter buffer to avoid clear previous P frame in case of reordering.
video_cache_->clear_all(); video_cache_->clear_all();
srs_warn("RTC2RTMP: keyframe drop old ts=%u, header=%hu, lost=%hu, set new ts=%u, header=%hu, lost=%hu", srs_warn("RTC2RTMP: keyframe drop old ts=%u, header=%hu, lost=%hu, set new ts=%u, header=%hu, lost=%hu",
(uint32_t)old_ts, old_header_sn, old_lost_sn, (uint32_t)rtp_key_frame_ts_, header_sn_, lost_sn_); (uint32_t)old_ts, old_header_sn, old_lost_sn, (uint32_t)rtp_key_frame_ts_, header_sn_, lost_sn_);
@ -1683,6 +1697,9 @@ srs_error_t SrsRtcFrameBuilderAudioPacketCache::process_packet(SrsRtpPacket *src
(now - last_audio_process_time_) > timeout_; (now - last_audio_process_time_) > timeout_;
uint16_t window_end = last_audio_seq_num_ + SLIDING_WINDOW_SIZE; uint16_t window_end = last_audio_seq_num_ + SLIDING_WINDOW_SIZE;
// TODO: Should improve the audio cache by NACK, consider if only RTC2RTMP without
// any RTC player, then if one RTP packet is lost, the audio cache will not request the lost
// packet. This means some audio packets are lost.
while (!audio_buffer_.empty()) { while (!audio_buffer_.empty()) {
std::map<uint16_t, SrsRtpPacket *>::iterator it = audio_buffer_.begin(); std::map<uint16_t, SrsRtpPacket *>::iterator it = audio_buffer_.begin();
uint16_t next_seq = it->first; uint16_t next_seq = it->first;
@ -1861,7 +1878,7 @@ srs_error_t SrsRtcFrameBuilder::transcode_audio(SrsRtpPacket *pkt)
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
// to common message. // Generate a sequence header for the first audio packet
uint32_t ts = pkt->get_avsync_time(); uint32_t ts = pkt->get_avsync_time();
if (is_first_audio_) { if (is_first_audio_) {
int header_len = 0; int header_len = 0;
@ -1950,8 +1967,10 @@ srs_error_t SrsRtcFrameBuilder::packet_video(SrsRtpPacket *pkt)
if ((err = frame_detector_->detect_frame(current_sn, start, end, got_frame)) != srs_success) { if ((err = frame_detector_->detect_frame(current_sn, start, end, got_frame)) != srs_success) {
return srs_error_wrap(err, "detect frame failed"); return srs_error_wrap(err, "detect frame failed");
} }
if (got_frame && (err = packet_video_rtmp(start, end)) != srs_success) { if (got_frame) {
err = srs_error_wrap(err, "fail to pack video frame, start=%u, end=%u", start, end); if ((err = packet_video_rtmp(start, end)) != srs_success) {
err = srs_error_wrap(err, "fail to pack video frame, start=%u, end=%u", start, end);
}
} }
} }
@ -1976,6 +1995,17 @@ srs_error_t SrsRtcFrameBuilder::packet_video_key_frame(SrsRtpPacket *pkt)
frame_detector_->on_keyframe_start(pkt); frame_detector_->on_keyframe_start(pkt);
// If only contains SPS/PPS, no need to store in cache.
SrsRtpSTAPPayload *stap_payload = dynamic_cast<SrsRtpSTAPPayload *>(pkt->payload());
if (stap_payload && stap_payload->get_idr() == NULL) {
return err;
}
SrsRtpSTAPPayloadHevc *stap_payload_hevc = dynamic_cast<SrsRtpSTAPPayloadHevc *>(pkt->payload());
if (stap_payload_hevc && stap_payload_hevc->get_idr() == NULL) {
return err;
}
// Cache the keyframe packet, which contains IDR, as long as SPS/PPS/VPS sequence header.
video_cache_->store_packet(pkt->copy()); video_cache_->store_packet(pkt->copy());
uint16_t current_sn = pkt->header_.get_sequence(); uint16_t current_sn = pkt->header_.get_sequence();
@ -1985,8 +2015,10 @@ srs_error_t SrsRtcFrameBuilder::packet_video_key_frame(SrsRtpPacket *pkt)
if ((err = frame_detector_->detect_frame(current_sn, start, end, got_frame)) != srs_success) { if ((err = frame_detector_->detect_frame(current_sn, start, end, got_frame)) != srs_success) {
return srs_error_wrap(err, "detect frame failed"); return srs_error_wrap(err, "detect frame failed");
} }
if (got_frame && (err = packet_video_rtmp(start, end)) != srs_success) { if (got_frame) {
err = srs_error_wrap(err, "fail to pack video frame, start=%u, end=%u", start, end); if ((err = packet_video_rtmp(start, end)) != srs_success) {
err = srs_error_wrap(err, "fail to pack video frame, start=%u, end=%u", start, end);
}
} }
} }
@ -2164,7 +2196,7 @@ srs_error_t SrsRtcFrameBuilder::do_packet_sequence_header_hevc(SrsRtpPacket *pkt
char *flv = NULL; char *flv = NULL;
int nb_flv = 0; int nb_flv = 0;
if ((err = hevc->mux_hevc2flv_enhanced(sh, SrsVideoAvcFrameTypeKeyFrame, SrsVideoHEVCFrameTraitPacketTypeSequenceStart, pkt->get_avsync_time(), if ((err = hevc->mux_hevc2flv_enhanced(sh, SrsVideoAvcFrameTypeKeyFrame, SrsVideoHEVCFrameTraitPacketTypeSequenceStart, pkt->get_avsync_time(),
pkt->get_avsync_time(), &flv, &nb_flv)) != srs_success) { pkt->get_avsync_time(), &flv, &nb_flv)) != srs_success) {
return srs_error_wrap(err, "mux sequence header"); return srs_error_wrap(err, "mux sequence header");
} }
@ -2193,8 +2225,11 @@ int SrsRtcFrameBuilder::calculate_packet_payload_size(SrsRtpPacket *pkt)
// H.264 FU-A payload // H.264 FU-A payload
SrsRtpFUAPayload2 *fua_payload = dynamic_cast<SrsRtpFUAPayload2 *>(pkt->payload()); SrsRtpFUAPayload2 *fua_payload = dynamic_cast<SrsRtpFUAPayload2 *>(pkt->payload());
if (fua_payload && fua_payload->size_ > 0) { if (fua_payload) {
int size = fua_payload->size_; int size = fua_payload->size_;
if (size <= 0) {
return 0;
}
if (fua_payload->start_) { if (fua_payload->start_) {
size += 1 + 4; // NALU header + length prefix size += 1 + 4; // NALU header + length prefix
} }
@ -2216,8 +2251,11 @@ int SrsRtcFrameBuilder::calculate_packet_payload_size(SrsRtpPacket *pkt)
// H.265 FU-A payload // H.265 FU-A payload
SrsRtpFUAPayloadHevc2 *fua_payload_hevc = dynamic_cast<SrsRtpFUAPayloadHevc2 *>(pkt->payload()); SrsRtpFUAPayloadHevc2 *fua_payload_hevc = dynamic_cast<SrsRtpFUAPayloadHevc2 *>(pkt->payload());
if (fua_payload_hevc && fua_payload_hevc->size_ > 0) { if (fua_payload_hevc) {
int size = fua_payload_hevc->size_; int size = fua_payload_hevc->size_;
if (size <= 0) {
return 0;
}
if (fua_payload_hevc->start_) { if (fua_payload_hevc->start_) {
size += 2 + 4; // HEVC NALU header + length prefix size += 2 + 4; // HEVC NALU header + length prefix
} }
@ -2239,7 +2277,10 @@ int SrsRtcFrameBuilder::calculate_packet_payload_size(SrsRtpPacket *pkt)
// Raw payload // Raw payload
SrsRtpRawPayload *raw_payload = dynamic_cast<SrsRtpRawPayload *>(pkt->payload()); SrsRtpRawPayload *raw_payload = dynamic_cast<SrsRtpRawPayload *>(pkt->payload());
if (raw_payload && raw_payload->nn_payload_ > 0) { if (raw_payload) {
if (raw_payload->nn_payload_ <= 1) {
return 0; // Ignore empty payload, which only has the NALU header.
}
return 4 + raw_payload->nn_payload_; // length prefix + payload return 4 + raw_payload->nn_payload_; // length prefix + payload
} }
@ -2254,15 +2295,19 @@ void SrsRtcFrameBuilder::write_packet_payload_to_buffer(SrsRtpPacket *pkt, SrsBu
// H.264 FU-A payload // H.264 FU-A payload
SrsRtpFUAPayload2 *fua_payload = dynamic_cast<SrsRtpFUAPayload2 *>(pkt->payload()); SrsRtpFUAPayload2 *fua_payload = dynamic_cast<SrsRtpFUAPayload2 *>(pkt->payload());
if (fua_payload && fua_payload->size_ > 0) { if (fua_payload) {
int size = fua_payload->size_;
if (size <= 0) {
return;
}
if (fua_payload->start_) { if (fua_payload->start_) {
nalu_len = fua_payload->size_ + 1; nalu_len = size + 1;
payload.skip(4); // Skip 4 bytes to write nalu_len later payload.skip(4); // Skip 4 bytes to write nalu_len later
payload.write_1bytes(fua_payload->nri_ | fua_payload->nalu_type_); payload.write_1bytes(fua_payload->nri_ | fua_payload->nalu_type_);
payload.write_bytes(fua_payload->payload_, fua_payload->size_); payload.write_bytes(fua_payload->payload_, size);
} else { } else {
nalu_len += fua_payload->size_; nalu_len += size;
payload.write_bytes(fua_payload->payload_, fua_payload->size_); payload.write_bytes(fua_payload->payload_, size);
if (fua_payload->end_) { if (fua_payload->end_) {
// Write nalu_len back // Write nalu_len back
payload.skip(-(4 + nalu_len)); payload.skip(-(4 + nalu_len));
@ -2288,16 +2333,20 @@ void SrsRtcFrameBuilder::write_packet_payload_to_buffer(SrsRtpPacket *pkt, SrsBu
// H.265 FU-A payload // H.265 FU-A payload
SrsRtpFUAPayloadHevc2 *fua_payload_hevc = dynamic_cast<SrsRtpFUAPayloadHevc2 *>(pkt->payload()); SrsRtpFUAPayloadHevc2 *fua_payload_hevc = dynamic_cast<SrsRtpFUAPayloadHevc2 *>(pkt->payload());
if (fua_payload_hevc && fua_payload_hevc->size_ > 0) { if (fua_payload_hevc) {
int size = fua_payload_hevc->size_;
if (size <= 0) {
return;
}
if (fua_payload_hevc->start_) { if (fua_payload_hevc->start_) {
nalu_len = fua_payload_hevc->size_ + 2; nalu_len = size + 2;
payload.skip(4); // Skip 4 bytes to write nalu_len later payload.skip(4); // Skip 4 bytes to write nalu_len later
payload.write_1bytes(fua_payload_hevc->nalu_type_ << 1); payload.write_1bytes(fua_payload_hevc->nalu_type_ << 1);
payload.write_1bytes(0x01); payload.write_1bytes(0x01);
payload.write_bytes(fua_payload_hevc->payload_, fua_payload_hevc->size_); payload.write_bytes(fua_payload_hevc->payload_, size);
} else { } else {
nalu_len += fua_payload_hevc->size_; nalu_len += size;
payload.write_bytes(fua_payload_hevc->payload_, fua_payload_hevc->size_); payload.write_bytes(fua_payload_hevc->payload_, size);
if (fua_payload_hevc->end_) { if (fua_payload_hevc->end_) {
// Write nalu_len back // Write nalu_len back
payload.skip(-(4 + nalu_len)); payload.skip(-(4 + nalu_len));
@ -2323,7 +2372,10 @@ void SrsRtcFrameBuilder::write_packet_payload_to_buffer(SrsRtpPacket *pkt, SrsBu
// Raw payload // Raw payload
SrsRtpRawPayload *raw_payload = dynamic_cast<SrsRtpRawPayload *>(pkt->payload()); SrsRtpRawPayload *raw_payload = dynamic_cast<SrsRtpRawPayload *>(pkt->payload());
if (raw_payload && raw_payload->nn_payload_ > 0) { if (raw_payload) {
if (raw_payload->nn_payload_ <= 1) {
return; // Ignore empty payload, which only has the NALU header.
}
payload.write_4bytes(raw_payload->nn_payload_); payload.write_4bytes(raw_payload->nn_payload_);
payload.write_bytes(raw_payload->payload_, raw_payload->nn_payload_); payload.write_bytes(raw_payload->payload_, raw_payload->nn_payload_);
return; return;
@ -2359,7 +2411,7 @@ srs_error_t SrsRtcFrameBuilder::packet_video_rtmp(const uint16_t start, const ui
} }
if (0 == nb_payload) { if (0 == nb_payload) {
srs_warn("empty nalu"); srs_info("RTC2RTMP: empty rtp packet, start=%u, end=%u, cnt=%d", start, end, cnt);
// The chrome web browser send RTP packet with empty payload frequently, // The chrome web browser send RTP packet with empty payload frequently,
// reset header_sn_, lost_sn_ and continue to found next frame in this case, // reset header_sn_, lost_sn_ and continue to found next frame in this case,
@ -2369,8 +2421,10 @@ srs_error_t SrsRtcFrameBuilder::packet_video_rtmp(const uint16_t start, const ui
if ((err = frame_detector_->detect_next_frame(end + 1, next_start, next_end, got_frame)) != srs_success) { if ((err = frame_detector_->detect_next_frame(end + 1, next_start, next_end, got_frame)) != srs_success) {
return srs_error_wrap(err, "update frame detector failed"); return srs_error_wrap(err, "update frame detector failed");
} }
if (got_frame && (err = packet_video_rtmp(next_start, next_end)) != srs_success) { if (got_frame) {
err = srs_error_wrap(err, "fail to pack video frame, start=%u, end=%u", next_start, next_end); if ((err = packet_video_rtmp(next_start, next_end)) != srs_success) {
err = srs_error_wrap(err, "fail to pack video frame, start=%u, end=%u", next_start, next_end);
}
} }
return err; return err;
@ -2445,8 +2499,10 @@ srs_error_t SrsRtcFrameBuilder::packet_video_rtmp(const uint16_t start, const ui
if ((err = frame_detector_->detect_next_frame(end + 1, next_start, next_end, got_frame)) != srs_success) { if ((err = frame_detector_->detect_next_frame(end + 1, next_start, next_end, got_frame)) != srs_success) {
return srs_error_wrap(err, "update frame detector failed"); return srs_error_wrap(err, "update frame detector failed");
} }
if (got_frame && (err = packet_video_rtmp(next_start, next_end)) != srs_success) { if (got_frame) {
err = srs_error_wrap(err, "fail to pack video frame, start=%u, end=%u", next_start, next_end); if ((err = packet_video_rtmp(next_start, next_end)) != srs_success) {
err = srs_error_wrap(err, "fail to pack video frame, start=%u, end=%u", next_start, next_end);
}
} }
return err; return err;
@ -3030,9 +3086,17 @@ SrsRtcTrackDescription *SrsRtcSourceDescription::find_track_description_by_ssrc(
return NULL; return NULL;
} }
SrsRtcRecvTrack::SrsRtcRecvTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc, bool is_audio) ISrsRtcPacketReceiver::ISrsRtcPacketReceiver()
{ {
session_ = session; }
ISrsRtcPacketReceiver::~ISrsRtcPacketReceiver()
{
}
SrsRtcRecvTrack::SrsRtcRecvTrack(ISrsRtcPacketReceiver *receiver, SrsRtcTrackDescription *track_desc, bool is_audio)
{
receiver_ = receiver;
track_desc_ = track_desc->copy(); track_desc_ = track_desc->copy();
nack_no_copy_ = false; nack_no_copy_ = false;
@ -3130,7 +3194,7 @@ srs_error_t SrsRtcRecvTrack::send_rtcp_rr()
uint32_t ssrc = track_desc_->ssrc_; uint32_t ssrc = track_desc_->ssrc_;
const uint64_t &last_time = last_sender_report_sys_time_; const uint64_t &last_time = last_sender_report_sys_time_;
if ((err = session_->send_rtcp_rr(ssrc, rtp_queue_, last_time, last_sender_report_ntp_)) != srs_success) { if ((err = receiver_->send_rtcp_rr(ssrc, rtp_queue_, last_time, last_sender_report_ntp_)) != srs_success) {
return srs_error_wrap(err, "ssrc=%u, last_time=%" PRId64, ssrc, last_time); return srs_error_wrap(err, "ssrc=%u, last_time=%" PRId64, ssrc, last_time);
} }
@ -3141,7 +3205,7 @@ srs_error_t SrsRtcRecvTrack::send_rtcp_xr_rrtr()
{ {
srs_error_t err = srs_success; srs_error_t err = srs_success;
if ((err = session_->send_rtcp_xr_rrtr(track_desc_->ssrc_)) != srs_success) { if ((err = receiver_->send_rtcp_xr_rrtr(track_desc_->ssrc_)) != srs_success) {
return srs_error_wrap(err, "ssrc=%u", track_desc_->ssrc_); return srs_error_wrap(err, "ssrc=%u", track_desc_->ssrc_);
} }
@ -3218,13 +3282,13 @@ srs_error_t SrsRtcRecvTrack::do_check_send_nacks(uint32_t &timeout_nacks)
srs_error_t err = srs_success; srs_error_t err = srs_success;
uint32_t sent_nacks = 0; uint32_t sent_nacks = 0;
session_->check_send_nacks(nack_receiver_, track_desc_->ssrc_, sent_nacks, timeout_nacks); receiver_->check_send_nacks(nack_receiver_, track_desc_->ssrc_, sent_nacks, timeout_nacks);
return err; return err;
} }
SrsRtcAudioRecvTrack::SrsRtcAudioRecvTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc) SrsRtcAudioRecvTrack::SrsRtcAudioRecvTrack(ISrsRtcPacketReceiver *receiver, SrsRtcTrackDescription *track_desc)
: SrsRtcRecvTrack(session, track_desc, true) : SrsRtcRecvTrack(receiver, track_desc, true)
{ {
} }
@ -3271,8 +3335,8 @@ srs_error_t SrsRtcAudioRecvTrack::check_send_nacks()
return err; return err;
} }
SrsRtcVideoRecvTrack::SrsRtcVideoRecvTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc) SrsRtcVideoRecvTrack::SrsRtcVideoRecvTrack(ISrsRtcPacketReceiver *receiver, SrsRtcTrackDescription *track_desc)
: SrsRtcRecvTrack(session, track_desc, false) : SrsRtcRecvTrack(receiver, track_desc, false)
{ {
} }
@ -3389,9 +3453,17 @@ uint16_t SrsRtcSeqJitter::correct(uint16_t value)
return jitter_->correct(value); return jitter_->correct(value);
} }
SrsRtcSendTrack::SrsRtcSendTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc, bool is_audio) ISrsRtcPacketSender::ISrsRtcPacketSender()
{ {
session_ = session; }
ISrsRtcPacketSender::~ISrsRtcPacketSender()
{
}
SrsRtcSendTrack::SrsRtcSendTrack(ISrsRtcPacketSender *sender, SrsRtcTrackDescription *track_desc, bool is_audio)
{
sender_ = sender;
track_desc_ = track_desc->copy(); track_desc_ = track_desc->copy();
nack_no_copy_ = false; nack_no_copy_ = false;
@ -3517,7 +3589,7 @@ srs_error_t SrsRtcSendTrack::on_recv_nack(const vector<uint16_t> &lost_seqs)
} }
// By default, we send packets by sendmmsg. // By default, we send packets by sendmmsg.
if ((err = session_->do_send_packet(pkt)) != srs_success) { if ((err = sender_->do_send_packet(pkt)) != srs_success) {
return srs_error_wrap(err, "raw send"); return srs_error_wrap(err, "raw send");
} }
} }
@ -3525,8 +3597,8 @@ srs_error_t SrsRtcSendTrack::on_recv_nack(const vector<uint16_t> &lost_seqs)
return err; return err;
} }
SrsRtcAudioSendTrack::SrsRtcAudioSendTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc) SrsRtcAudioSendTrack::SrsRtcAudioSendTrack(ISrsRtcPacketSender *sender, SrsRtcTrackDescription *track_desc)
: SrsRtcSendTrack(session, track_desc, true) : SrsRtcSendTrack(sender, track_desc, true)
{ {
} }
@ -3558,7 +3630,7 @@ srs_error_t SrsRtcAudioSendTrack::on_rtp(SrsRtpPacket *pkt)
// Rebuild the sequence number and timestamp of packet, see https://github.com/ossrs/srs/issues/3167 // Rebuild the sequence number and timestamp of packet, see https://github.com/ossrs/srs/issues/3167
rebuild_packet(pkt); rebuild_packet(pkt);
if ((err = session_->do_send_packet(pkt)) != srs_success) { if ((err = sender_->do_send_packet(pkt)) != srs_success) {
return srs_error_wrap(err, "raw send"); return srs_error_wrap(err, "raw send");
} }
@ -3575,8 +3647,8 @@ srs_error_t SrsRtcAudioSendTrack::on_rtcp(SrsRtpPacket *pkt)
return err; return err;
} }
SrsRtcVideoSendTrack::SrsRtcVideoSendTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc) SrsRtcVideoSendTrack::SrsRtcVideoSendTrack(ISrsRtcPacketSender *sender, SrsRtcTrackDescription *track_desc)
: SrsRtcSendTrack(session, track_desc, false) : SrsRtcSendTrack(sender, track_desc, false)
{ {
} }
@ -3608,7 +3680,7 @@ srs_error_t SrsRtcVideoSendTrack::on_rtp(SrsRtpPacket *pkt)
// Rebuild the sequence number and timestamp of packet, see https://github.com/ossrs/srs/issues/3167 // Rebuild the sequence number and timestamp of packet, see https://github.com/ossrs/srs/issues/3167
rebuild_packet(pkt); rebuild_packet(pkt);
if ((err = session_->do_send_packet(pkt)) != srs_success) { if ((err = sender_->do_send_packet(pkt)) != srs_success) {
return srs_error_wrap(err, "raw send"); return srs_error_wrap(err, "raw send");
} }

View File

@ -156,8 +156,21 @@ public:
void on_stream_change(SrsRtcSourceDescription *desc); void on_stream_change(SrsRtcSourceDescription *desc);
}; };
// The RTC source manager interface.
class ISrsRtcSourceManager
{
public:
ISrsRtcSourceManager();
virtual ~ISrsRtcSourceManager();
public:
virtual srs_error_t initialize() = 0;
virtual srs_error_t fetch_or_create(ISrsRequest *r, SrsSharedPtr<SrsRtcSource> &pps) = 0;
virtual SrsSharedPtr<SrsRtcSource> fetch(ISrsRequest *r) = 0;
};
// The RTC source manager. // The RTC source manager.
class SrsRtcSourceManager : public ISrsHourGlass class SrsRtcSourceManager : public ISrsRtcSourceManager, public ISrsHourGlass
{ {
private: private:
srs_mutex_t lock_; srs_mutex_t lock_;
@ -218,7 +231,7 @@ public:
}; };
// A Source is a stream, to publish and to play with, binding to SrsRtcPublishStream and SrsRtcPlayStream. // A Source is a stream, to publish and to play with, binding to SrsRtcPublishStream and SrsRtcPlayStream.
class SrsRtcSource : public ISrsRtpTarget, public ISrsFastTimer, public ISrsRtcSourceForConsumer class SrsRtcSource : public ISrsRtpTarget, public ISrsFastTimerHandler, public ISrsRtcSourceForConsumer
{ {
private: private:
// The RTP bridge, convert RTP packets to other protocols. // The RTP bridge, convert RTP packets to other protocols.
@ -323,7 +336,7 @@ public:
virtual bool has_stream_desc(); virtual bool has_stream_desc();
virtual void set_stream_desc(SrsRtcSourceDescription *stream_desc); virtual void set_stream_desc(SrsRtcSourceDescription *stream_desc);
virtual std::vector<SrsRtcTrackDescription *> get_track_desc(std::string type, std::string media_type); virtual std::vector<SrsRtcTrackDescription *> get_track_desc(std::string type, std::string media_type);
// interface ISrsFastTimer // interface ISrsFastTimerHandler
private: private:
srs_error_t on_timer(srs_utime_t interval); srs_error_t on_timer(srs_utime_t interval);
}; };
@ -746,13 +759,29 @@ public:
SrsRtcTrackDescription *find_track_description_by_ssrc(uint32_t ssrc); SrsRtcTrackDescription *find_track_description_by_ssrc(uint32_t ssrc);
}; };
// The RTC packet receiver interface.
class ISrsRtcPacketReceiver
{
public:
ISrsRtcPacketReceiver();
virtual ~ISrsRtcPacketReceiver();
public:
virtual srs_error_t send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer *rtp_queue, const uint64_t &last_send_systime, const SrsNtp &last_send_ntp) = 0;
virtual srs_error_t send_rtcp_xr_rrtr(uint32_t ssrc) = 0;
virtual void check_send_nacks(SrsRtpNackForReceiver *nack, uint32_t ssrc, uint32_t &sent_nacks, uint32_t &timeout_nacks) = 0;
virtual srs_error_t send_rtcp(char *data, int nb_data) = 0;
virtual srs_error_t send_rtcp_fb_pli(uint32_t ssrc, const SrsContextId &cid_of_subscriber) = 0;
};
// The RTC receive track.
class SrsRtcRecvTrack class SrsRtcRecvTrack
{ {
protected: protected:
SrsRtcTrackDescription *track_desc_; SrsRtcTrackDescription *track_desc_;
protected: protected:
SrsRtcConnection *session_; ISrsRtcPacketReceiver *receiver_;
SrsRtpRingBuffer *rtp_queue_; SrsRtpRingBuffer *rtp_queue_;
SrsRtpNackForReceiver *nack_receiver_; SrsRtpNackForReceiver *nack_receiver_;
@ -773,7 +802,7 @@ protected:
uint64_t last_sender_report_sys_time_; uint64_t last_sender_report_sys_time_;
public: public:
SrsRtcRecvTrack(SrsRtcConnection *session, SrsRtcTrackDescription *stream_descs, bool is_audio); SrsRtcRecvTrack(ISrsRtcPacketReceiver *receiver, SrsRtcTrackDescription *stream_descs, bool is_audio);
virtual ~SrsRtcRecvTrack(); virtual ~SrsRtcRecvTrack();
public: public:
@ -806,7 +835,7 @@ protected:
class SrsRtcAudioRecvTrack : public SrsRtcRecvTrack, public ISrsRtpPacketDecodeHandler class SrsRtcAudioRecvTrack : public SrsRtcRecvTrack, public ISrsRtpPacketDecodeHandler
{ {
public: public:
SrsRtcAudioRecvTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc); SrsRtcAudioRecvTrack(ISrsRtcPacketReceiver *receiver, SrsRtcTrackDescription *track_desc);
virtual ~SrsRtcAudioRecvTrack(); virtual ~SrsRtcAudioRecvTrack();
public: public:
@ -820,7 +849,7 @@ public:
class SrsRtcVideoRecvTrack : public SrsRtcRecvTrack, public ISrsRtpPacketDecodeHandler class SrsRtcVideoRecvTrack : public SrsRtcRecvTrack, public ISrsRtpPacketDecodeHandler
{ {
public: public:
SrsRtcVideoRecvTrack(SrsRtcConnection *session, SrsRtcTrackDescription *stream_descs); SrsRtcVideoRecvTrack(ISrsRtcPacketReceiver *receiver, SrsRtcTrackDescription *stream_descs);
virtual ~SrsRtcVideoRecvTrack(); virtual ~SrsRtcVideoRecvTrack();
public: public:
@ -922,6 +951,17 @@ public:
uint16_t correct(uint16_t value); uint16_t correct(uint16_t value);
}; };
// The RTC packet sender interface.
class ISrsRtcPacketSender
{
public:
ISrsRtcPacketSender();
virtual ~ISrsRtcPacketSender();
public:
virtual srs_error_t do_send_packet(SrsRtpPacket *pkt) = 0;
};
class SrsRtcSendTrack class SrsRtcSendTrack
{ {
public: public:
@ -930,7 +970,7 @@ public:
protected: protected:
// The owner connection for this track. // The owner connection for this track.
SrsRtcConnection *session_; ISrsRtcPacketSender *sender_;
// NACK ARQ ring buffer. // NACK ARQ ring buffer.
SrsRtpRingBuffer *rtp_queue_; SrsRtpRingBuffer *rtp_queue_;
@ -946,7 +986,7 @@ private:
SrsErrorPithyPrint *nack_epp; SrsErrorPithyPrint *nack_epp;
public: public:
SrsRtcSendTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc, bool is_audio); SrsRtcSendTrack(ISrsRtcPacketSender *sender, SrsRtcTrackDescription *track_desc, bool is_audio);
virtual ~SrsRtcSendTrack(); virtual ~SrsRtcSendTrack();
public: public:
@ -975,7 +1015,7 @@ public:
class SrsRtcAudioSendTrack : public SrsRtcSendTrack class SrsRtcAudioSendTrack : public SrsRtcSendTrack
{ {
public: public:
SrsRtcAudioSendTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc); SrsRtcAudioSendTrack(ISrsRtcPacketSender *sender, SrsRtcTrackDescription *track_desc);
virtual ~SrsRtcAudioSendTrack(); virtual ~SrsRtcAudioSendTrack();
public: public:
@ -986,7 +1026,7 @@ public:
class SrsRtcVideoSendTrack : public SrsRtcSendTrack class SrsRtcVideoSendTrack : public SrsRtcSendTrack
{ {
public: public:
SrsRtcVideoSendTrack(SrsRtcConnection *session, SrsRtcTrackDescription *track_desc); SrsRtcVideoSendTrack(ISrsRtcPacketSender *sender, SrsRtcTrackDescription *track_desc);
virtual ~SrsRtcVideoSendTrack(); virtual ~SrsRtcVideoSendTrack();
public: public:

View File

@ -541,7 +541,7 @@ srs_error_t SrsRtmpConn::stream_service_cycle()
} }
// We must do stat the client before hooks, because hooks depends on it. // We must do stat the client before hooks, because hooks depends on it.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
if ((err = stat->on_client(_srs_context->get_id().c_str(), req, this, info_->type_)) != srs_success) { if ((err = stat->on_client(_srs_context->get_id().c_str(), req, this, info_->type_)) != srs_success) {
return srs_error_wrap(err, "rtmp: stat client"); return srs_error_wrap(err, "rtmp: stat client");
} }
@ -751,10 +751,10 @@ srs_error_t SrsRtmpConn::do_playing(SrsSharedPtr<SrsLiveSource> source, SrsLiveC
int64_t starttime = -1; int64_t starttime = -1;
// setup the realtime. // setup the realtime.
realtime_ = _srs_config->get_realtime_enabled(req->vhost_); realtime_ = _srs_config->get_realtime_enabled(req->vhost_, false);
// setup the mw config. // setup the mw config.
// when mw_sleep changed, resize the socket send buffer. // when mw_sleep changed, resize the socket send buffer.
mw_msgs_ = _srs_config->get_mw_msgs(req->vhost_, realtime_); mw_msgs_ = _srs_config->get_mw_msgs(req->vhost_, realtime_, false);
mw_sleep_ = _srs_config->get_mw_sleep(req->vhost_); mw_sleep_ = _srs_config->get_mw_sleep(req->vhost_);
transport_->set_socket_buffer(mw_sleep_); transport_->set_socket_buffer(mw_sleep_);
// initialize the send_min_interval // initialize the send_min_interval
@ -877,7 +877,7 @@ srs_error_t SrsRtmpConn::publishing(SrsSharedPtr<SrsLiveSource> source)
} }
// We must do stat the client before hooks, because hooks depends on it. // We must do stat the client before hooks, because hooks depends on it.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
if ((err = stat->on_client(_srs_context->get_id().c_str(), req, this, info_->type_)) != srs_success) { if ((err = stat->on_client(_srs_context->get_id().c_str(), req, this, info_->type_)) != srs_success) {
return srs_error_wrap(err, "rtmp: stat client"); return srs_error_wrap(err, "rtmp: stat client");
} }
@ -979,7 +979,7 @@ srs_error_t SrsRtmpConn::do_publishing(SrsSharedPtr<SrsLiveSource> source, SrsPu
// Update the stat for video fps. // Update the stat for video fps.
// @remark https://github.com/ossrs/srs/issues/851 // @remark https://github.com/ossrs/srs/issues/851
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
if ((err = stat->on_video_frames(req, (int)(rtrd->nb_video_frames() - nb_frames))) != srs_success) { if ((err = stat->on_video_frames(req, (int)(rtrd->nb_video_frames() - nb_frames))) != srs_success) {
return srs_error_wrap(err, "rtmp: stat video frames"); return srs_error_wrap(err, "rtmp: stat video frames");
} }
@ -1557,7 +1557,7 @@ srs_error_t SrsRtmpConn::cycle()
err = do_cycle(); err = do_cycle();
// Update statistic when done. // Update statistic when done.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->kbps_add_delta(get_id().c_str(), delta_); stat->kbps_add_delta(get_id().c_str(), delta_);
stat->on_disconnect(get_id().c_str(), err); stat->on_disconnect(get_id().c_str(), err);

View File

@ -958,7 +958,7 @@ srs_error_t SrsOriginHub::on_audio(SrsMediaPacket *shared_audio)
static int flv_sound_types[] = {1, 2, 0}; static int flv_sound_types[] = {1, 2, 0};
// when got audio stream info. // when got audio stream info.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
if ((err = stat->on_audio_info(req_, format->acodec_->id_, c->sound_rate_, c->sound_type_, c->aac_object_)) != srs_success) { if ((err = stat->on_audio_info(req_, format->acodec_->id_, c->sound_rate_, c->sound_type_, c->aac_object_)) != srs_success) {
return srs_error_wrap(err, "stat audio"); return srs_error_wrap(err, "stat audio");
} }
@ -1042,7 +1042,7 @@ srs_error_t SrsOriginHub::on_video(SrsMediaPacket *shared_video, bool is_sequenc
srs_assert(c); srs_assert(c);
// when got video stream info. // when got video stream info.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
if (c->id_ == SrsVideoCodecIdAVC) { if (c->id_ == SrsVideoCodecIdAVC) {
err = stat->on_video_info(req_, c->id_, c->avc_profile_, c->avc_level_, c->width_, c->height_); err = stat->on_video_info(req_, c->id_, c->avc_profile_, c->avc_level_, c->width_, c->height_);
@ -1550,6 +1550,14 @@ srs_error_t SrsMetaCache::update_vsh(SrsMediaPacket *msg)
return vformat_->on_video(msg); return vformat_->on_video(msg);
} }
ISrsLiveSourceManager::ISrsLiveSourceManager()
{
}
ISrsLiveSourceManager::~ISrsLiveSourceManager()
{
}
SrsLiveSourceManager *_srs_sources = NULL; SrsLiveSourceManager *_srs_sources = NULL;
SrsLiveSourceManager::SrsLiveSourceManager() SrsLiveSourceManager::SrsLiveSourceManager()
@ -2336,7 +2344,7 @@ srs_error_t SrsLiveSource::on_publish()
return srs_error_wrap(err, "bridge publish"); return srs_error_wrap(err, "bridge publish");
} }
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->on_stream_publish(req_, _source_id.c_str()); stat->on_stream_publish(req_, _source_id.c_str());
// When no players, the publisher is idle now. // When no players, the publisher is idle now.
@ -2378,7 +2386,7 @@ void SrsLiveSource::on_unpublish()
ISrsLiveSourceHandler *handler = _srs_server; ISrsLiveSourceHandler *handler = _srs_server;
srs_assert(handler); srs_assert(handler);
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->on_stream_close(req_); stat->on_stream_close(req_);
handler->on_unpublish(req_); handler->on_unpublish(req_);

View File

@ -465,8 +465,25 @@ public:
virtual srs_error_t update_vsh(SrsMediaPacket *msg); virtual srs_error_t update_vsh(SrsMediaPacket *msg);
}; };
// The live source manager interface.
class ISrsLiveSourceManager
{
public:
ISrsLiveSourceManager();
virtual ~ISrsLiveSourceManager();
public:
// create source when fetch from cache failed.
// @param r the client request.
// @param h the event handler for source.
// @param pps the matched source, if success never be NULL.
virtual srs_error_t fetch_or_create(ISrsRequest *r, SrsSharedPtr<SrsLiveSource> &pps) = 0;
// Get the exists source, NULL when not exists.
virtual SrsSharedPtr<SrsLiveSource> fetch(ISrsRequest *r) = 0;
};
// The source manager to create and refresh all stream sources. // The source manager to create and refresh all stream sources.
class SrsLiveSourceManager : public ISrsHourGlass class SrsLiveSourceManager : public ISrsHourGlass, public ISrsLiveSourceManager
{ {
private: private:
srs_mutex_t lock_; srs_mutex_t lock_;

View File

@ -75,7 +75,7 @@ SrsRtspPlayStream::~SrsRtspPlayStream()
} }
// update the statistic when client coveried. // update the statistic when client coveried.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
// TODO: FIXME: Should finger out the err. // TODO: FIXME: Should finger out the err.
stat->on_disconnect(cid_.c_str(), srs_success); stat->on_disconnect(cid_.c_str(), srs_success);
} }
@ -87,7 +87,7 @@ srs_error_t SrsRtspPlayStream::initialize(ISrsRequest *req, std::map<uint32_t, S
req_ = req->copy(); req_ = req->copy();
// We must do stat the client before hooks, because hooks depends on it. // We must do stat the client before hooks, because hooks depends on it.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
if ((err = stat->on_client(cid_.c_str(), req_, session_, SrsRtcConnPlay)) != srs_success) { if ((err = stat->on_client(cid_.c_str(), req_, session_, SrsRtcConnPlay)) != srs_success) {
return srs_error_wrap(err, "RTSP: stat client"); return srs_error_wrap(err, "RTSP: stat client");
} }
@ -487,7 +487,7 @@ srs_error_t SrsRtspConnection::cycle()
err = do_cycle(); err = do_cycle();
// Update statistic when done. // Update statistic when done.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->kbps_add_delta(get_id().c_str(), delta()); stat->kbps_add_delta(get_id().c_str(), delta());
do_teardown(); do_teardown();

View File

@ -429,7 +429,7 @@ srs_error_t SrsRtspSource::on_publish()
return srs_error_wrap(err, "source id change"); return srs_error_wrap(err, "source id change");
} }
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->on_stream_publish(req_, _source_id.c_str()); stat->on_stream_publish(req_, _source_id.c_str());
return err; return err;
@ -449,7 +449,7 @@ void SrsRtspSource::on_unpublish()
} }
_source_id = SrsContextId(); _source_id = SrsContextId();
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->on_stream_close(req_); stat->on_stream_close(req_);
// Destroy and cleanup source when no publishers and consumers. // Destroy and cleanup source when no publishers and consumers.

View File

@ -178,6 +178,9 @@ SrsServer::SrsServer()
ingester_ = new SrsIngester(); ingester_ = new SrsIngester();
timer_ = NULL; timer_ = NULL;
// Initialize global statistic instance.
_srs_stat = new SrsStatistic();
// Initialize WebRTC components // Initialize WebRTC components
rtc_session_manager_ = new SrsRtcSessionManager(); rtc_session_manager_ = new SrsRtcSessionManager();
} }
@ -230,6 +233,9 @@ SrsServer::~SrsServer()
} }
srs_freep(rtc_session_manager_); srs_freep(rtc_session_manager_);
// Cleanup global statistic instance.
srs_freep(_srs_stat);
} }
void SrsServer::dispose() void SrsServer::dispose()
@ -1089,7 +1095,7 @@ srs_error_t SrsServer::notify(int event, srs_utime_t interval, srs_utime_t tick)
void SrsServer::resample_kbps() void SrsServer::resample_kbps()
{ {
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
// collect delta from all clients. // collect delta from all clients.
for (int i = 0; i < (int)_srs_conn_manager->size(); i++) { for (int i = 0; i < (int)_srs_conn_manager->size(); i++) {

View File

@ -230,7 +230,7 @@ srs_error_t SrsMpegtsSrtConn::cycle()
srs_error_t err = do_cycle(); srs_error_t err = do_cycle();
// Update statistic when done. // Update statistic when done.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->kbps_add_delta(get_id().c_str(), delta_); stat->kbps_add_delta(get_id().c_str(), delta_);
stat->on_disconnect(get_id().c_str(), err); stat->on_disconnect(get_id().c_str(), err);
@ -319,7 +319,7 @@ srs_error_t SrsMpegtsSrtConn::publishing()
srs_error_t err = srs_success; srs_error_t err = srs_success;
// We must do stat the client before hooks, because hooks depends on it. // We must do stat the client before hooks, because hooks depends on it.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
if ((err = stat->on_client(_srs_context->get_id().c_str(), req_, this, SrsSrtConnPublish)) != srs_success) { if ((err = stat->on_client(_srs_context->get_id().c_str(), req_, this, SrsSrtConnPublish)) != srs_success) {
return srs_error_wrap(err, "srt: stat client"); return srs_error_wrap(err, "srt: stat client");
} }
@ -348,7 +348,7 @@ srs_error_t SrsMpegtsSrtConn::playing()
srs_error_t err = srs_success; srs_error_t err = srs_success;
// We must do stat the client before hooks, because hooks depends on it. // We must do stat the client before hooks, because hooks depends on it.
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
if ((err = stat->on_client(_srs_context->get_id().c_str(), req_, this, SrsSrtConnPlay)) != srs_success) { if ((err = stat->on_client(_srs_context->get_id().c_str(), req_, this, SrsSrtConnPlay)) != srs_success) {
return srs_error_wrap(err, "srt: stat client"); return srs_error_wrap(err, "srt: stat client");
} }

View File

@ -95,6 +95,14 @@ int SrsSrtPacket::size()
return shared_buffer_->size(); return shared_buffer_->size();
} }
ISrsSrtSourceManager::ISrsSrtSourceManager()
{
}
ISrsSrtSourceManager::~ISrsSrtSourceManager()
{
}
SrsSrtSourceManager::SrsSrtSourceManager() SrsSrtSourceManager::SrsSrtSourceManager()
{ {
lock_ = srs_mutex_new(); lock_ = srs_mutex_new();
@ -684,12 +692,12 @@ srs_error_t SrsSrtFrameBuilder::check_vps_sps_pps_change(SrsTsMessage *msg)
char *flv = NULL; char *flv = NULL;
int nb_flv = 0; int nb_flv = 0;
if ((err = hevc->mux_hevc2flv_enhanced(sh, if ((err = hevc->mux_hevc2flv_enhanced(sh,
SrsVideoAvcFrameTypeKeyFrame, SrsVideoAvcFrameTypeKeyFrame,
SrsVideoHEVCFrameTraitPacketTypeSequenceStart, SrsVideoHEVCFrameTraitPacketTypeSequenceStart,
dts, dts,
pts, pts,
&flv, &flv,
&nb_flv)) != srs_success) { &nb_flv)) != srs_success) {
return srs_error_wrap(err, "hevc sh to flv"); return srs_error_wrap(err, "hevc sh to flv");
} }
@ -1072,7 +1080,7 @@ srs_error_t SrsSrtSource::on_publish()
return srs_error_wrap(err, "bridge on publish"); return srs_error_wrap(err, "bridge on publish");
} }
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->on_stream_publish(req_, _source_id.c_str()); stat->on_stream_publish(req_, _source_id.c_str());
return err; return err;
@ -1085,7 +1093,7 @@ void SrsSrtSource::on_unpublish()
return; return;
} }
SrsStatistic *stat = SrsStatistic::instance(); SrsStatistic *stat = _srs_stat;
stat->on_stream_close(req_); stat->on_stream_close(req_);
if (srt_bridge_) { if (srt_bridge_) {

View File

@ -51,7 +51,21 @@ private:
int actual_buffer_size_; int actual_buffer_size_;
}; };
class SrsSrtSourceManager : public ISrsHourGlass // The SRT source manager interface.
class ISrsSrtSourceManager
{
public:
ISrsSrtSourceManager();
virtual ~ISrsSrtSourceManager();
public:
virtual srs_error_t initialize() = 0;
virtual srs_error_t fetch_or_create(ISrsRequest *r, SrsSharedPtr<SrsSrtSource> &pps) = 0;
virtual SrsSharedPtr<SrsSrtSource> fetch(ISrsRequest *r) = 0;
};
// The SRT source manager.
class SrsSrtSourceManager : public ISrsHourGlass, public ISrsSrtSourceManager
{ {
private: private:
srs_mutex_t lock_; srs_mutex_t lock_;

View File

@ -245,7 +245,15 @@ srs_error_t SrsStatisticClient::dumps(SrsJsonObject *obj)
return err; return err;
} }
SrsStatistic *SrsStatistic::instance_ = NULL; ISrsStatistic::ISrsStatistic()
{
}
ISrsStatistic::~ISrsStatistic()
{
}
SrsStatistic *_srs_stat = NULL;
SrsStatistic::SrsStatistic() SrsStatistic::SrsStatistic()
{ {
@ -287,14 +295,6 @@ SrsStatistic::~SrsStatistic()
rstreams_.clear(); rstreams_.clear();
} }
SrsStatistic *SrsStatistic::instance()
{
if (instance_ == NULL) {
instance_ = new SrsStatistic();
}
return instance_;
}
SrsStatisticVhost *SrsStatistic::find_vhost_by_id(std::string vid) SrsStatisticVhost *SrsStatistic::find_vhost_by_id(std::string vid)
{ {
std::map<string, SrsStatisticVhost *>::iterator it; std::map<string, SrsStatisticVhost *>::iterator it;

View File

@ -130,10 +130,22 @@ public:
virtual srs_error_t dumps(SrsJsonObject *obj); virtual srs_error_t dumps(SrsJsonObject *obj);
}; };
class SrsStatistic // The interface for statistic.
class ISrsStatistic
{
public:
ISrsStatistic();
virtual ~ISrsStatistic();
public:
virtual void on_disconnect(std::string id, srs_error_t err) = 0;
virtual srs_error_t on_client(std::string id, ISrsRequest *req, ISrsExpire *conn, SrsRtmpConnType type) = 0;
};
// The global statistic instance.
class SrsStatistic : public ISrsStatistic
{ {
private: private:
static SrsStatistic *instance_;
// The id to identify the sever. // The id to identify the sever.
std::string server_id_; std::string server_id_;
// The id to identify the service. // The id to identify the service.
@ -167,13 +179,10 @@ private:
// The total of clients errors. // The total of clients errors.
int64_t nb_errs_; int64_t nb_errs_;
private: public:
SrsStatistic(); SrsStatistic();
virtual ~SrsStatistic(); virtual ~SrsStatistic();
public:
static SrsStatistic *instance();
public: public:
virtual SrsStatisticVhost *find_vhost_by_id(std::string vid); virtual SrsStatisticVhost *find_vhost_by_id(std::string vid);
virtual SrsStatisticVhost *find_vhost_by_name(std::string name); virtual SrsStatisticVhost *find_vhost_by_name(std::string name);
@ -259,4 +268,7 @@ public:
// Generate a random string id, with constant prefix. // Generate a random string id, with constant prefix.
extern std::string srs_generate_stat_vid(); extern std::string srs_generate_stat_vid();
// Global statistic instance.
extern SrsStatistic *_srs_stat;
#endif #endif

View File

@ -128,6 +128,14 @@ srs_error_t SrsHourGlass::cycle()
return err; return err;
} }
ISrsFastTimerHandler::ISrsFastTimerHandler()
{
}
ISrsFastTimerHandler::~ISrsFastTimerHandler()
{
}
ISrsFastTimer::ISrsFastTimer() ISrsFastTimer::ISrsFastTimer()
{ {
} }
@ -160,16 +168,16 @@ srs_error_t SrsFastTimer::start()
return err; return err;
} }
void SrsFastTimer::subscribe(ISrsFastTimer *timer) void SrsFastTimer::subscribe(ISrsFastTimerHandler *timer)
{ {
if (std::find(handlers_.begin(), handlers_.end(), timer) == handlers_.end()) { if (std::find(handlers_.begin(), handlers_.end(), timer) == handlers_.end()) {
handlers_.push_back(timer); handlers_.push_back(timer);
} }
} }
void SrsFastTimer::unsubscribe(ISrsFastTimer *timer) void SrsFastTimer::unsubscribe(ISrsFastTimerHandler *timer)
{ {
vector<ISrsFastTimer *>::iterator it = std::find(handlers_.begin(), handlers_.end(), timer); vector<ISrsFastTimerHandler *>::iterator it = std::find(handlers_.begin(), handlers_.end(), timer);
if (it != handlers_.end()) { if (it != handlers_.end()) {
handlers_.erase(it); handlers_.erase(it);
} }
@ -187,7 +195,7 @@ srs_error_t SrsFastTimer::cycle()
++_srs_pps_timer->sugar_; ++_srs_pps_timer->sugar_;
for (int i = 0; i < (int)handlers_.size(); i++) { for (int i = 0; i < (int)handlers_.size(); i++) {
ISrsFastTimer *timer = handlers_.at(i); ISrsFastTimerHandler *timer = handlers_.at(i);
if ((err = timer->on_timer(interval_)) != srs_success) { if ((err = timer->on_timer(interval_)) != srs_success) {
srs_freep(err); // Ignore any error for shared timer. srs_freep(err); // Ignore any error for shared timer.
@ -248,6 +256,14 @@ srs_error_t SrsClockWallMonitor::on_timer(srs_utime_t interval)
return err; return err;
} }
ISrsSharedTimer::ISrsSharedTimer()
{
}
ISrsSharedTimer::~ISrsSharedTimer()
{
}
SrsSharedTimer::SrsSharedTimer() SrsSharedTimer::SrsSharedTimer()
{ {
timer20ms_ = NULL; timer20ms_ = NULL;
@ -300,22 +316,22 @@ srs_error_t SrsSharedTimer::initialize()
return err; return err;
} }
SrsFastTimer *SrsSharedTimer::timer20ms() ISrsFastTimer *SrsSharedTimer::timer20ms()
{ {
return timer20ms_; return timer20ms_;
} }
SrsFastTimer *SrsSharedTimer::timer100ms() ISrsFastTimer *SrsSharedTimer::timer100ms()
{ {
return timer100ms_; return timer100ms_;
} }
SrsFastTimer *SrsSharedTimer::timer1s() ISrsFastTimer *SrsSharedTimer::timer1s()
{ {
return timer1s_; return timer1s_;
} }
SrsFastTimer *SrsSharedTimer::timer5s() ISrsFastTimer *SrsSharedTimer::timer5s()
{ {
return timer5s_; return timer5s_;
} }

View File

@ -96,6 +96,18 @@ public:
}; };
// The handler for fast timer. // The handler for fast timer.
class ISrsFastTimerHandler
{
public:
ISrsFastTimerHandler();
virtual ~ISrsFastTimerHandler();
public:
// Tick when timer is active.
virtual srs_error_t on_timer(srs_utime_t interval) = 0;
};
// The interface for timer.
class ISrsFastTimer class ISrsFastTimer
{ {
public: public:
@ -103,19 +115,19 @@ public:
virtual ~ISrsFastTimer(); virtual ~ISrsFastTimer();
public: public:
// Tick when timer is active. virtual void subscribe(ISrsFastTimerHandler *timer) = 0;
virtual srs_error_t on_timer(srs_utime_t interval) = 0; virtual void unsubscribe(ISrsFastTimerHandler *timer) = 0;
}; };
// The fast timer, shared by objects, for high performance. // The fast timer, shared by objects, for high performance.
// For example, we should never start a timer for each connection or publisher or player, // For example, we should never start a timer for each connection or publisher or player,
// instead, we should start only one fast timer in server. // instead, we should start only one fast timer in server.
class SrsFastTimer : public ISrsCoroutineHandler class SrsFastTimer : public ISrsCoroutineHandler, public ISrsFastTimer
{ {
private: private:
ISrsCoroutine *trd_; ISrsCoroutine *trd_;
srs_utime_t interval_; srs_utime_t interval_;
std::vector<ISrsFastTimer *> handlers_; std::vector<ISrsFastTimerHandler *> handlers_;
ISrsTime *time_; ISrsTime *time_;
public: public:
@ -126,8 +138,8 @@ public:
srs_error_t start(); srs_error_t start();
public: public:
void subscribe(ISrsFastTimer *timer); void subscribe(ISrsFastTimerHandler *timer);
void unsubscribe(ISrsFastTimer *timer); void unsubscribe(ISrsFastTimerHandler *timer);
// Interface ISrsCoroutineHandler // Interface ISrsCoroutineHandler
private: private:
// Cycle the hourglass, which will sleep resolution every time. // Cycle the hourglass, which will sleep resolution every time.
@ -136,7 +148,7 @@ private:
}; };
// To monitor the system wall clock timer deviation. // To monitor the system wall clock timer deviation.
class SrsClockWallMonitor : public ISrsFastTimer class SrsClockWallMonitor : public ISrsFastTimerHandler
{ {
private: private:
ISrsTime *time_; ISrsTime *time_;
@ -144,13 +156,27 @@ private:
public: public:
SrsClockWallMonitor(); SrsClockWallMonitor();
virtual ~SrsClockWallMonitor(); virtual ~SrsClockWallMonitor();
// interface ISrsFastTimer // interface ISrsFastTimerHandler
private: private:
srs_error_t on_timer(srs_utime_t interval); srs_error_t on_timer(srs_utime_t interval);
}; };
// The interface for shared timer.
class ISrsSharedTimer
{
public:
ISrsSharedTimer();
virtual ~ISrsSharedTimer();
public:
virtual ISrsFastTimer *timer20ms() = 0;
virtual ISrsFastTimer *timer100ms() = 0;
virtual ISrsFastTimer *timer1s() = 0;
virtual ISrsFastTimer *timer5s() = 0;
};
// Global shared timer manager // Global shared timer manager
class SrsSharedTimer class SrsSharedTimer : public ISrsSharedTimer
{ {
private: private:
SrsFastTimer *timer20ms_; SrsFastTimer *timer20ms_;
@ -169,10 +195,10 @@ public:
public: public:
// Access to global shared timers // Access to global shared timers
SrsFastTimer *timer20ms(); ISrsFastTimer *timer20ms();
SrsFastTimer *timer100ms(); ISrsFastTimer *timer100ms();
SrsFastTimer *timer1s(); ISrsFastTimer *timer1s();
SrsFastTimer *timer5s(); ISrsFastTimer *timer5s();
}; };
// Global shared timer instance // Global shared timer instance

View File

@ -1257,6 +1257,24 @@ SrsNaluSample *SrsRtpSTAPPayload::get_pps()
return NULL; return NULL;
} }
SrsNaluSample *SrsRtpSTAPPayload::get_idr()
{
int nn_nalus = (int)nalus_.size();
for (int i = 0; i < nn_nalus; i++) {
SrsNaluSample *p = nalus_[i];
if (!p || !p->size_) {
continue;
}
SrsAvcNaluType nalu_type = SrsAvcNaluTypeParse(p->bytes_[0]);
if (nalu_type == SrsAvcNaluTypeIDR) {
return p;
}
}
return NULL;
}
uint64_t SrsRtpSTAPPayload::nb_bytes() uint64_t SrsRtpSTAPPayload::nb_bytes()
{ {
int size = 1; int size = 1;
@ -1630,6 +1648,24 @@ SrsNaluSample *SrsRtpSTAPPayloadHevc::get_pps()
return NULL; return NULL;
} }
SrsNaluSample *SrsRtpSTAPPayloadHevc::get_idr()
{
int nn_nalus = (int)nalus_.size();
for (int i = 0; i < nn_nalus; i++) {
SrsNaluSample *p = nalus_[i];
if (!p || !p->size_) {
continue;
}
SrsHevcNaluType nalu_type = SrsHevcNaluTypeParse(p->bytes_[0]);
if (SrsIsIRAP(nalu_type)) {
return p;
}
}
return NULL;
}
uint64_t SrsRtpSTAPPayloadHevc::nb_bytes() uint64_t SrsRtpSTAPPayloadHevc::nb_bytes()
{ {
int size = 2; int size = 2;

View File

@ -470,6 +470,7 @@ public:
public: public:
SrsNaluSample *get_sps(); SrsNaluSample *get_sps();
SrsNaluSample *get_pps(); SrsNaluSample *get_pps();
SrsNaluSample *get_idr();
// interface ISrsRtpPayloader // interface ISrsRtpPayloader
public: public:
virtual uint64_t nb_bytes(); virtual uint64_t nb_bytes();
@ -545,6 +546,7 @@ public:
SrsNaluSample *get_vps(); SrsNaluSample *get_vps();
SrsNaluSample *get_sps(); SrsNaluSample *get_sps();
SrsNaluSample *get_pps(); SrsNaluSample *get_pps();
SrsNaluSample *get_idr();
// interface ISrsRtpPayloader // interface ISrsRtpPayloader
public: public:
virtual uint64_t nb_bytes(); virtual uint64_t nb_bytes();

View File

@ -44,15 +44,15 @@ VOID TEST(AppResourceManagerTest, FindByFastID)
srs_error_t err = srs_success; srs_error_t err = srs_success;
if (true) { if (true) {
SrsResourceManager m("test"); SrsUniquePtr<SrsResourceManager> m(new SrsResourceManager("test"));
HELPER_EXPECT_SUCCESS(m.start()); HELPER_EXPECT_SUCCESS(m->start());
m.add_with_fast_id(101, new MockIDResource(1)); m->add_with_fast_id(101, new MockIDResource(1));
m.add_with_fast_id(102, new MockIDResource(2)); m->add_with_fast_id(102, new MockIDResource(2));
m.add_with_fast_id(103, new MockIDResource(3)); m->add_with_fast_id(103, new MockIDResource(3));
EXPECT_EQ(1, ((MockIDResource *)m.find_by_fast_id(101))->id); EXPECT_EQ(1, ((MockIDResource *)m->find_by_fast_id(101))->id);
EXPECT_EQ(2, ((MockIDResource *)m.find_by_fast_id(102))->id); EXPECT_EQ(2, ((MockIDResource *)m->find_by_fast_id(102))->id);
EXPECT_EQ(3, ((MockIDResource *)m.find_by_fast_id(103))->id); EXPECT_EQ(3, ((MockIDResource *)m->find_by_fast_id(103))->id);
} }
if (true) { if (true) {
@ -248,30 +248,30 @@ VOID TEST(AppCoroutineTest, SetCidOfCoroutine)
srs_error_t err = srs_success; srs_error_t err = srs_success;
MockCoroutineHandler ch; MockCoroutineHandler ch;
SrsSTCoroutine sc("test", &ch); SrsUniquePtr<SrsSTCoroutine> sc(new SrsSTCoroutine("test", &ch));
ch.trd = &sc; ch.trd = sc.get();
EXPECT_TRUE(sc.cid().empty()); EXPECT_TRUE(sc->cid().empty());
// Start coroutine, which will create the cid. // Start coroutine, which will create the cid.
HELPER_ASSERT_SUCCESS(sc.start()); HELPER_ASSERT_SUCCESS(sc->start());
HELPER_ASSERT_SUCCESS(sc.pull()); HELPER_ASSERT_SUCCESS(sc->pull());
srs_cond_timedwait(ch.running, 100 * SRS_UTIME_MILLISECONDS); srs_cond_timedwait(ch.running, 100 * SRS_UTIME_MILLISECONDS);
EXPECT_TRUE(!sc.cid().empty()); EXPECT_TRUE(!sc->cid().empty());
EXPECT_TRUE(!ch.cid.empty()); EXPECT_TRUE(!ch.cid.empty());
// Should be a new cid. // Should be a new cid.
SrsContextId cid = _srs_context->generate_id(); SrsContextId cid = _srs_context->generate_id();
EXPECT_TRUE(sc.cid().compare(cid) != 0); EXPECT_TRUE(sc->cid().compare(cid) != 0);
EXPECT_TRUE(ch.cid.compare(cid) != 0); EXPECT_TRUE(ch.cid.compare(cid) != 0);
// Set the cid and stop the coroutine. // Set the cid and stop the coroutine.
sc.set_cid(cid); sc->set_cid(cid);
sc.stop(); sc->stop();
// Now the cid should be the new one. // Now the cid should be the new one.
srs_cond_timedwait(ch.exited, 100 * SRS_UTIME_MILLISECONDS); srs_cond_timedwait(ch.exited, 100 * SRS_UTIME_MILLISECONDS);
EXPECT_TRUE(sc.cid().compare(cid) == 0); EXPECT_TRUE(sc->cid().compare(cid) == 0);
EXPECT_TRUE(ch.cid.compare(cid) == 0); EXPECT_TRUE(ch.cid.compare(cid) == 0);
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_UTEST_APP5_HPP
#define SRS_UTEST_APP5_HPP
/*
#include <srs_utest_app5.hpp>
*/
#include <srs_utest.hpp>
#include <srs_app_rtc_codec.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_app_rtc_source.hpp>
#include <srs_kernel_rtc_rtp.hpp>
#include <srs_protocol_amf0.hpp>
#include <srs_protocol_format.hpp>
#include <srs_protocol_rtmp_stack.hpp>
#include <srs_utest_app4.hpp>
// Forward declarations
class SrsMediaPacket;
class SrsRtpPacket;
// Helper functions for creating test objects
SrsRtpPacket *create_test_rtp_packet(uint16_t seq, uint32_t ts, uint32_t ssrc, bool marker = false);
SrsRtcTrackDescription *create_test_track_description(std::string type, uint32_t ssrc);
SrsCodecPayload *create_test_codec_payload(uint8_t pt, std::string name, int sample);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,614 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_UTEST_APP6_HPP
#define SRS_UTEST_APP6_HPP
/*
#include <srs_utest_app6.hpp>
*/
#include <srs_utest.hpp>
#include <srs_app_config.hpp>
#include <srs_app_http_hooks.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_app_rtc_dtls.hpp>
#include <srs_app_rtc_network.hpp>
#include <srs_app_rtc_source.hpp>
#include <srs_app_rtmp_source.hpp>
#include <srs_app_srt_source.hpp>
#include <srs_app_statistic.hpp>
#include <srs_protocol_sdp.hpp>
#include <srs_utest_app2.hpp>
#include <srs_utest_protocol3.hpp>
// Mock DTLS implementation for testing SrsSecurityTransport
class MockDtls : public ISrsDtls
{
public:
srs_error_t initialize_error_;
srs_error_t start_active_handshake_error_;
srs_error_t on_dtls_error_;
srs_error_t get_srtp_key_error_;
std::string last_role_;
std::string last_version_;
std::string recv_key_;
std::string send_key_;
int initialize_count_;
int start_active_handshake_count_;
int on_dtls_count_;
int get_srtp_key_count_;
public:
MockDtls();
virtual ~MockDtls();
public:
virtual srs_error_t initialize(std::string role, std::string version);
virtual srs_error_t start_active_handshake();
virtual srs_error_t on_dtls(char *data, int nb_data);
virtual srs_error_t get_srtp_key(std::string &recv_key, std::string &send_key);
public:
void reset();
void set_initialize_error(srs_error_t err);
void set_start_active_handshake_error(srs_error_t err);
void set_on_dtls_error(srs_error_t err);
void set_get_srtp_key_error(srs_error_t err);
void set_srtp_keys(const std::string &recv_key, const std::string &send_key);
};
// Mock RTC Network for testing SrsSecurityTransport
class MockRtcNetwork : public ISrsRtcNetwork
{
public:
srs_error_t on_dtls_handshake_done_error_;
srs_error_t on_dtls_alert_error_;
srs_error_t protect_rtp_error_;
srs_error_t protect_rtcp_error_;
srs_error_t write_error_;
std::string last_alert_type_;
std::string last_alert_desc_;
int on_dtls_handshake_done_count_;
int on_dtls_alert_count_;
int protect_rtp_count_;
int protect_rtcp_count_;
int write_count_;
bool is_established_;
public:
MockRtcNetwork();
virtual ~MockRtcNetwork();
public:
virtual srs_error_t on_dtls_handshake_done();
virtual srs_error_t on_dtls_alert(std::string type, std::string desc);
virtual srs_error_t protect_rtp(void *packet, int *nb_cipher);
virtual srs_error_t protect_rtcp(void *packet, int *nb_cipher);
virtual bool is_establelished();
virtual srs_error_t write(void *buf, size_t size, ssize_t *nwrite);
public:
void reset();
void set_on_dtls_handshake_done_error(srs_error_t err);
void set_on_dtls_alert_error(srs_error_t err);
void set_protect_rtp_error(srs_error_t err);
void set_protect_rtcp_error(srs_error_t err);
void set_write_error(srs_error_t err);
void set_established(bool established);
};
// Mock SRTP implementation for testing SrsSecurityTransport
class MockSrtp : public ISrsSRTP
{
public:
srs_error_t initialize_error_;
srs_error_t protect_rtp_error_;
srs_error_t protect_rtcp_error_;
srs_error_t unprotect_rtp_error_;
srs_error_t unprotect_rtcp_error_;
std::string last_recv_key_;
std::string last_send_key_;
int initialize_count_;
int protect_rtp_count_;
int protect_rtcp_count_;
int unprotect_rtp_count_;
int unprotect_rtcp_count_;
public:
MockSrtp();
virtual ~MockSrtp();
public:
virtual srs_error_t initialize(std::string recv_key, std::string send_key);
virtual srs_error_t protect_rtp(void *packet, int *nb_cipher);
virtual srs_error_t protect_rtcp(void *packet, int *nb_cipher);
virtual srs_error_t unprotect_rtp(void *packet, int *nb_plaintext);
virtual srs_error_t unprotect_rtcp(void *packet, int *nb_plaintext);
public:
void reset();
void set_initialize_error(srs_error_t err);
void set_protect_rtp_error(srs_error_t err);
void set_protect_rtcp_error(srs_error_t err);
void set_unprotect_rtp_error(srs_error_t err);
void set_unprotect_rtcp_error(srs_error_t err);
};
// Mock PLI Worker Handler for testing SrsRtcPliWorker
class MockRtcPliWorkerHandler : public ISrsRtcPliWorkerHandler
{
public:
srs_error_t do_request_keyframe_error_;
std::vector<std::pair<uint32_t, SrsContextId> > keyframe_requests_;
int do_request_keyframe_count_;
public:
MockRtcPliWorkerHandler();
virtual ~MockRtcPliWorkerHandler();
public:
virtual srs_error_t do_request_keyframe(uint32_t ssrc, SrsContextId cid);
public:
void reset();
void set_do_request_keyframe_error(srs_error_t err);
bool has_keyframe_request(uint32_t ssrc, const SrsContextId &cid);
int get_keyframe_request_count();
};
// Mock PLI Worker for testing SrsRtcPlayStream::on_stream_change
class MockRtcPliWorker : public SrsRtcPliWorker
{
public:
std::vector<std::pair<uint32_t, SrsContextId> > keyframe_requests_;
int request_keyframe_count_;
public:
MockRtcPliWorker(ISrsRtcPliWorkerHandler *h);
virtual ~MockRtcPliWorker();
public:
virtual void request_keyframe(uint32_t ssrc, SrsContextId cid);
public:
void reset();
bool has_keyframe_request(uint32_t ssrc, const SrsContextId &cid);
int get_keyframe_request_count();
};
// Mock HTTP hooks for testing SrsRtcAsyncCallOnStop
class MockHttpHooks : public ISrsHttpHooks
{
public:
std::vector<std::pair<std::string, ISrsRequest *> > on_stop_calls_;
int on_stop_count_;
std::vector<std::pair<std::string, ISrsRequest *> > on_unpublish_calls_;
int on_unpublish_count_;
public:
MockHttpHooks();
virtual ~MockHttpHooks();
virtual srs_error_t on_connect(std::string url, ISrsRequest *req);
virtual void on_close(std::string url, ISrsRequest *req, int64_t send_bytes, int64_t recv_bytes);
virtual srs_error_t on_publish(std::string url, ISrsRequest *req);
virtual void on_unpublish(std::string url, ISrsRequest *req);
virtual srs_error_t on_play(std::string url, ISrsRequest *req);
virtual void on_stop(std::string url, ISrsRequest *req);
virtual srs_error_t on_dvr(SrsContextId cid, std::string url, ISrsRequest *req, std::string file);
virtual srs_error_t on_hls(SrsContextId cid, std::string url, ISrsRequest *req, std::string file, std::string ts_url,
std::string m3u8, std::string m3u8_url, int sn, srs_utime_t duration);
virtual srs_error_t on_hls_notify(SrsContextId cid, std::string url, ISrsRequest *req, std::string ts_url, int nb_notify);
virtual srs_error_t discover_co_workers(std::string url, std::string &host, int &port);
virtual srs_error_t on_forward_backend(std::string url, ISrsRequest *req, std::vector<std::string> &rtmp_urls);
void clear_calls();
};
// Mock context for testing SrsRtcAsyncCallOnStop
class MockContext : public ISrsContext
{
public:
SrsContextId current_id_;
SrsContextId get_id_result_;
public:
MockContext();
virtual ~MockContext();
virtual SrsContextId generate_id();
virtual const SrsContextId &get_id();
virtual const SrsContextId &set_id(const SrsContextId &v);
void set_current_id(const SrsContextId &id);
};
// Mock app config for testing SrsRtcAsyncCallOnStop
class MockAppConfig : public ISrsAppConfig
{
public:
bool http_hooks_enabled_;
SrsConfDirective *on_stop_directive_;
SrsConfDirective *on_unpublish_directive_;
bool rtc_nack_enabled_;
bool rtc_nack_no_copy_;
int rtc_drop_for_pt_;
bool rtc_twcc_enabled_;
bool srt_enabled_;
bool rtc_to_rtmp_;
public:
MockAppConfig();
virtual ~MockAppConfig();
// ISrsConfig methods
virtual srs_utime_t get_pithy_print();
virtual std::string get_default_app_name();
// ISrsAppConfig methods
virtual bool get_vhost_http_hooks_enabled(std::string vhost);
virtual SrsConfDirective *get_vhost_on_stop(std::string vhost);
virtual SrsConfDirective *get_vhost_on_unpublish(std::string vhost);
virtual bool get_rtc_nack_enabled(std::string vhost);
virtual bool get_rtc_nack_no_copy(std::string vhost);
virtual bool get_realtime_enabled(std::string vhost, bool is_rtc);
virtual int get_mw_msgs(std::string vhost, bool is_realtime, bool is_rtc);
virtual int get_rtc_drop_for_pt(std::string vhost);
virtual bool get_rtc_twcc_enabled(std::string vhost);
virtual bool get_srt_enabled();
virtual bool get_srt_enabled(std::string vhost);
virtual bool get_rtc_to_rtmp(std::string vhost);
void set_http_hooks_enabled(bool enabled);
void set_on_stop_urls(const std::vector<std::string> &urls);
void clear_on_stop_directive();
void set_on_unpublish_urls(const std::vector<std::string> &urls);
void clear_on_unpublish_directive();
void set_rtc_nack_enabled(bool enabled);
void set_rtc_nack_no_copy(bool no_copy);
void set_rtc_drop_for_pt(int pt);
void set_rtc_twcc_enabled(bool enabled);
void set_srt_enabled(bool enabled);
void set_rtc_to_rtmp(bool enabled);
};
// Mock request for testing SrsRtcAsyncCallOnStop
class MockRtcAsyncCallRequest : public ISrsRequest
{
public:
std::string vhost_;
std::string app_;
std::string stream_;
public:
MockRtcAsyncCallRequest(std::string vhost = "__defaultVhost__", std::string app = "live", std::string stream = "test");
virtual ~MockRtcAsyncCallRequest();
virtual ISrsRequest *copy();
virtual std::string get_stream_url();
virtual void update_auth(ISrsRequest *req);
virtual void strip();
virtual ISrsRequest *as_http();
};
// Mock RTC source manager for testing SrsRtcPlayStream
class MockRtcSourceManager : public ISrsRtcSourceManager
{
public:
srs_error_t initialize_error_;
srs_error_t fetch_or_create_error_;
int initialize_count_;
int fetch_or_create_count_;
SrsSharedPtr<SrsRtcSource> mock_source_;
public:
MockRtcSourceManager();
virtual ~MockRtcSourceManager();
virtual srs_error_t initialize();
virtual srs_error_t fetch_or_create(ISrsRequest *r, SrsSharedPtr<SrsRtcSource> &pps);
virtual SrsSharedPtr<SrsRtcSource> fetch(ISrsRequest *r);
void set_initialize_error(srs_error_t err);
void set_fetch_or_create_error(srs_error_t err);
void reset();
};
// Mock statistic for testing SrsRtcPlayStream
class MockRtcStatistic : public ISrsStatistic
{
public:
srs_error_t on_client_error_;
int on_client_count_;
int on_disconnect_count_;
std::string last_client_id_;
ISrsRequest *last_client_req_;
ISrsExpire *last_client_conn_;
SrsRtmpConnType last_client_type_;
public:
MockRtcStatistic();
virtual ~MockRtcStatistic();
virtual void on_disconnect(std::string id, srs_error_t err);
virtual srs_error_t on_client(std::string id, ISrsRequest *req, ISrsExpire *conn, SrsRtmpConnType type);
void set_on_client_error(srs_error_t err);
void reset();
};
// Mock RTC async task executor for testing SrsRtcPlayStream::send_packet
class MockRtcAsyncTaskExecutor : public ISrsExecRtcAsyncTask
{
public:
srs_error_t exec_error_;
int exec_count_;
ISrsAsyncCallTask *last_task_;
public:
MockRtcAsyncTaskExecutor();
virtual ~MockRtcAsyncTaskExecutor();
public:
virtual srs_error_t exec_rtc_async_work(ISrsAsyncCallTask *t);
void set_exec_error(srs_error_t err);
void reset();
};
// Mock RTC packet sender for testing SrsRtcPlayStream::send_packet
class MockRtcPacketSender : public ISrsRtcPacketSender
{
public:
srs_error_t send_packet_error_;
int send_packet_count_;
SrsRtpPacket *last_sent_packet_;
public:
MockRtcPacketSender();
virtual ~MockRtcPacketSender();
public:
virtual srs_error_t do_send_packet(SrsRtpPacket *pkt);
void set_send_packet_error(srs_error_t err);
void reset();
};
// Mock RTC send track for testing SrsRtcPlayStream::send_packet
class MockRtcSendTrack : public SrsRtcSendTrack
{
public:
srs_error_t on_rtp_error_;
srs_error_t on_nack_error_;
int on_rtp_count_;
int on_nack_count_;
SrsRtpPacket *last_rtp_packet_;
SrsRtpPacket **last_nack_packet_;
bool nack_set_to_null_;
public:
MockRtcSendTrack(ISrsRtcPacketSender *sender, SrsRtcTrackDescription *track_desc, bool is_audio);
virtual ~MockRtcSendTrack();
public:
virtual srs_error_t on_rtp(SrsRtpPacket *pkt);
virtual srs_error_t on_rtcp(SrsRtpPacket *pkt);
virtual srs_error_t on_nack(SrsRtpPacket **ppkt);
void set_on_rtp_error(srs_error_t err);
void set_on_nack_error(srs_error_t err);
void set_nack_set_to_null(bool v);
void reset();
};
// Mock RTCP classes for testing SrsRtcPlayStream::on_rtcp
class MockRtcpCommon : public SrsRtcpCommon
{
public:
uint8_t mock_type_;
public:
MockRtcpCommon(uint8_t type);
virtual ~MockRtcpCommon();
virtual uint8_t type() const;
};
class MockRtcpRR : public SrsRtcpRR
{
public:
MockRtcpRR(uint32_t sender_ssrc = 0);
virtual ~MockRtcpRR();
};
class MockRtcpNack : public SrsRtcpNack
{
public:
MockRtcpNack(uint32_t sender_ssrc = 0);
virtual ~MockRtcpNack();
};
class MockRtcpFbCommon : public SrsRtcpFbCommon
{
public:
uint8_t mock_rc_;
public:
MockRtcpFbCommon(uint8_t rc = kPLI);
virtual ~MockRtcpFbCommon();
virtual uint8_t get_rc() const;
};
class MockRtcpXr : public SrsRtcpXr
{
public:
MockRtcpXr(uint32_t ssrc = 0);
virtual ~MockRtcpXr();
};
// Mock RTC send track with NACK response capability for testing on_rtcp_nack
class MockRtcSendTrackForNack : public SrsRtcSendTrack
{
public:
srs_error_t on_recv_nack_error_;
int on_recv_nack_count_;
std::vector<uint16_t> last_lost_seqs_;
uint32_t test_ssrc_;
bool track_enabled_;
public:
MockRtcSendTrackForNack(ISrsRtcPacketSender *sender, SrsRtcTrackDescription *track_desc, bool is_audio, uint32_t ssrc);
virtual ~MockRtcSendTrackForNack();
public:
virtual srs_error_t on_rtp(SrsRtpPacket *pkt);
virtual srs_error_t on_rtcp(SrsRtpPacket *pkt);
virtual srs_error_t on_recv_nack(const std::vector<uint16_t> &lost_seqs);
virtual bool has_ssrc(uint32_t ssrc);
virtual bool get_track_status();
public:
void set_track_enabled(bool enabled);
void set_on_recv_nack_error(srs_error_t err);
void reset();
};
// Mock RTCP sender for testing SrsRtcPublishRtcpTimer
class MockRtcRtcpSender : public ISrsRtcRtcpSender
{
public:
bool is_sender_started_;
bool is_sender_twcc_enabled_;
srs_error_t send_rtcp_rr_error_;
srs_error_t send_rtcp_xr_rrtr_error_;
srs_error_t send_periodic_twcc_error_;
int send_rtcp_rr_count_;
int send_rtcp_xr_rrtr_count_;
int send_periodic_twcc_count_;
public:
MockRtcRtcpSender();
virtual ~MockRtcRtcpSender();
public:
virtual bool is_sender_started();
virtual srs_error_t send_rtcp_rr();
virtual srs_error_t send_rtcp_xr_rrtr();
virtual bool is_sender_twcc_enabled();
virtual srs_error_t send_periodic_twcc();
public:
void set_sender_started(bool started);
void set_sender_twcc_enabled(bool enabled);
void set_send_rtcp_rr_error(srs_error_t err);
void set_send_rtcp_xr_rrtr_error(srs_error_t err);
void set_send_periodic_twcc_error(srs_error_t err);
void reset();
};
// Mock RTC packet receiver for testing SrsRtcPublishStream
class MockRtcPacketReceiver : public ISrsRtcPacketReceiver
{
public:
srs_error_t send_rtcp_rr_error_;
srs_error_t send_rtcp_xr_rrtr_error_;
srs_error_t send_rtcp_error_;
srs_error_t send_rtcp_fb_pli_error_;
int send_rtcp_rr_count_;
int send_rtcp_xr_rrtr_count_;
int send_rtcp_count_;
int send_rtcp_fb_pli_count_;
int check_send_nacks_count_;
public:
MockRtcPacketReceiver();
virtual ~MockRtcPacketReceiver();
public:
virtual srs_error_t send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer *rtp_queue, const uint64_t &last_send_systime, const SrsNtp &last_send_ntp);
virtual srs_error_t send_rtcp_xr_rrtr(uint32_t ssrc);
virtual void check_send_nacks(SrsRtpNackForReceiver *nack, uint32_t ssrc, uint32_t &sent_nacks, uint32_t &timeout_nacks);
virtual srs_error_t send_rtcp(char *data, int nb_data);
virtual srs_error_t send_rtcp_fb_pli(uint32_t ssrc, const SrsContextId &cid_of_subscriber);
public:
void set_send_rtcp_rr_error(srs_error_t err);
void set_send_rtcp_xr_rrtr_error(srs_error_t err);
void set_send_rtcp_error(srs_error_t err);
void set_send_rtcp_fb_pli_error(srs_error_t err);
void reset();
};
// Mock expire for testing SrsRtcPublishStream
class MockRtcExpire : public ISrsExpire
{
public:
bool expired_;
public:
MockRtcExpire();
virtual ~MockRtcExpire();
public:
virtual void expire();
void reset();
};
// Mock live source manager for testing SrsRtcPublishStream
class MockLiveSourceManager : public ISrsLiveSourceManager
{
public:
srs_error_t fetch_or_create_error_;
int fetch_or_create_count_;
SrsSharedPtr<SrsLiveSource> mock_source_;
bool can_publish_;
public:
MockLiveSourceManager();
virtual ~MockLiveSourceManager();
virtual srs_error_t fetch_or_create(ISrsRequest *r, SrsSharedPtr<SrsLiveSource> &pps);
virtual SrsSharedPtr<SrsLiveSource> fetch(ISrsRequest *r);
void set_fetch_or_create_error(srs_error_t err);
void set_can_publish(bool can_publish);
void reset();
};
// Mock live source for testing SrsRtcPublishStream
class MockLiveSource : public SrsLiveSource
{
public:
bool can_publish_result_;
public:
MockLiveSource();
virtual ~MockLiveSource();
virtual bool can_publish(bool is_edge);
void set_can_publish(bool can_publish);
};
// Mock SRT source for testing SrsRtcPublishStream
class MockSrtSource : public SrsSrtSource
{
public:
bool can_publish_result_;
public:
MockSrtSource();
virtual ~MockSrtSource();
virtual bool can_publish();
void set_can_publish(bool can_publish);
};
// Mock SRT source manager for testing SrsRtcPublishStream
class MockSrtSourceManager : public ISrsSrtSourceManager
{
public:
srs_error_t initialize_error_;
srs_error_t fetch_or_create_error_;
int initialize_count_;
int fetch_or_create_count_;
SrsSharedPtr<SrsSrtSource> mock_source_;
bool can_publish_;
public:
MockSrtSourceManager();
virtual ~MockSrtSourceManager();
virtual srs_error_t initialize();
virtual srs_error_t fetch_or_create(ISrsRequest *r, SrsSharedPtr<SrsSrtSource> &pps);
virtual SrsSharedPtr<SrsSrtSource> fetch(ISrsRequest *r);
void set_initialize_error(srs_error_t err);
void set_fetch_or_create_error(srs_error_t err);
void set_can_publish(bool can_publish);
void reset();
};
#endif

View File

@ -0,0 +1,533 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#include <srs_utest_app7.hpp>
using namespace std;
#include <srs_app_rtc_conn.hpp>
#include <srs_kernel_error.hpp>
#include <srs_kernel_rtc_rtp.hpp>
#include <srs_utest_app5.hpp>
#include <srs_utest_app6.hpp>
// Mock video recv track implementation
MockRtcVideoRecvTrackForNack::MockRtcVideoRecvTrackForNack(ISrsRtcPacketReceiver *receiver, SrsRtcTrackDescription *track_desc)
: SrsRtcVideoRecvTrack(receiver, track_desc)
{
check_send_nacks_error_ = srs_success;
check_send_nacks_count_ = 0;
}
MockRtcVideoRecvTrackForNack::~MockRtcVideoRecvTrackForNack()
{
}
srs_error_t MockRtcVideoRecvTrackForNack::check_send_nacks()
{
check_send_nacks_count_++;
return check_send_nacks_error_;
}
void MockRtcVideoRecvTrackForNack::set_check_send_nacks_error(srs_error_t err)
{
check_send_nacks_error_ = err;
}
void MockRtcVideoRecvTrackForNack::reset()
{
check_send_nacks_error_ = srs_success;
check_send_nacks_count_ = 0;
}
// Mock audio recv track implementation
MockRtcAudioRecvTrackForNack::MockRtcAudioRecvTrackForNack(ISrsRtcPacketReceiver *receiver, SrsRtcTrackDescription *track_desc)
: SrsRtcAudioRecvTrack(receiver, track_desc)
{
check_send_nacks_error_ = srs_success;
check_send_nacks_count_ = 0;
}
MockRtcAudioRecvTrackForNack::~MockRtcAudioRecvTrackForNack()
{
}
srs_error_t MockRtcAudioRecvTrackForNack::check_send_nacks()
{
check_send_nacks_count_++;
return check_send_nacks_error_;
}
void MockRtcAudioRecvTrackForNack::set_check_send_nacks_error(srs_error_t err)
{
check_send_nacks_error_ = err;
}
void MockRtcAudioRecvTrackForNack::reset()
{
check_send_nacks_error_ = srs_success;
check_send_nacks_count_ = 0;
}
VOID TEST(SrsRtcPublishStreamTest, OnRtpCipherTypicalScenario)
{
srs_error_t err;
// Create mock objects
MockRtcAsyncTaskExecutor mock_exec;
MockRtcExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsContextId cid;
cid.set_value("test-rtp-cipher-stream-id");
// Create SrsRtcPublishStream with mock dependencies
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, cid));
// Test typical RTP packet processing scenario
// Create a simple RTP packet without extension (minimal valid packet)
unsigned char simple_rtp_data[] = {
// RTP header (12 bytes) - no extension
0x80, 0x60, 0x12, 0x34, // V=2, P=0, X=0, CC=0, M=0, PT=96, seq=0x1234
0x56, 0x78, 0x9A, 0xBC, // timestamp
0xDE, 0xF0, 0x12, 0x34 // SSRC
};
// Test normal RTP packet processing (default state: no NACK simulation, no TWCC, no PT drop)
HELPER_EXPECT_SUCCESS(publish_stream->on_rtp_cipher((char *)simple_rtp_data, sizeof(simple_rtp_data)));
// Test with NACK simulation enabled
publish_stream->simulate_nack_drop(1); // Simulate dropping 1 packet
HELPER_EXPECT_SUCCESS(publish_stream->on_rtp_cipher((char *)simple_rtp_data, sizeof(simple_rtp_data)));
// Test with a different RTP packet after NACK simulation is consumed
unsigned char rtp_data2[] = {
// RTP header (12 bytes) - different sequence number
0x80, 0x60, 0x12, 0x35, // V=2, P=0, X=0, CC=0, M=0, PT=96, seq=0x1235
0x56, 0x78, 0x9A, 0xBD, // timestamp
0xDE, 0xF0, 0x12, 0x35 // SSRC
};
HELPER_EXPECT_SUCCESS(publish_stream->on_rtp_cipher((char *)rtp_data2, sizeof(rtp_data2)));
}
VOID TEST(SrsRtcPublishStreamTest, OnRtpPlaintextTypicalScenario)
{
srs_error_t err;
// Create mock objects
MockRtcAsyncTaskExecutor mock_exec;
MockRtcExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsContextId cid;
cid.set_value("test-rtp-plaintext-stream-id");
// Create SrsRtcPublishStream with mock dependencies
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, cid));
// Create video track with matching SSRC for the RTP packet
SrsRtcTrackDescription video_desc;
video_desc.type_ = "video";
video_desc.id_ = "video_track_test";
video_desc.ssrc_ = 0xDEF01234; // SSRC from RTP packet (0xDE, 0xF0, 0x12, 0x34)
video_desc.is_active_ = true;
SrsRtcVideoRecvTrack *video_track = new SrsRtcVideoRecvTrack(&mock_receiver, &video_desc);
publish_stream->video_tracks_.push_back(video_track);
// Enable tracks for processing
publish_stream->set_all_tracks_status(true);
// Test typical RTP plaintext packet processing scenario
// Create a simple RTP packet without extension (minimal valid packet)
unsigned char simple_rtp_data[] = {
// RTP header (12 bytes) - no extension
0x80, 0x60, 0x12, 0x34, // V=2, P=0, X=0, CC=0, M=0, PT=96, seq=0x1234
0x56, 0x78, 0x9A, 0xBC, // timestamp
0xDE, 0xF0, 0x12, 0x34 // SSRC = 0xDEF01234
};
// Test normal RTP plaintext packet processing
// This tests the complete flow: packet wrapping, buffer creation, do_on_rtp_plaintext call, and cleanup
HELPER_EXPECT_SUCCESS(publish_stream->on_rtp_plaintext((char *)simple_rtp_data, sizeof(simple_rtp_data)));
}
VOID TEST(SrsRtcPublishStreamTest, CheckSendNacksTypicalScenario)
{
srs_error_t err;
// Create mock objects
MockRtcAsyncTaskExecutor mock_exec;
MockRtcExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsContextId cid;
cid.set_value("test-check-send-nacks-stream-id");
// Create SrsRtcPublishStream with mock dependencies
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, cid));
// Test scenario 1: NACK disabled - should return success immediately
publish_stream->nack_enabled_ = false;
HELPER_EXPECT_SUCCESS(publish_stream->check_send_nacks());
// Test scenario 2: NACK enabled with video and audio tracks
publish_stream->nack_enabled_ = true;
// Create mock video track
SrsRtcTrackDescription video_desc;
video_desc.type_ = "video";
video_desc.id_ = "video_track_nack_test";
video_desc.ssrc_ = 0x12345678;
video_desc.is_active_ = true;
MockRtcVideoRecvTrackForNack *video_track = new MockRtcVideoRecvTrackForNack(&mock_receiver, &video_desc);
publish_stream->video_tracks_.push_back(video_track);
// Create mock audio track
SrsRtcTrackDescription audio_desc;
audio_desc.type_ = "audio";
audio_desc.id_ = "audio_track_nack_test";
audio_desc.ssrc_ = 0x87654321;
audio_desc.is_active_ = true;
MockRtcAudioRecvTrackForNack *audio_track = new MockRtcAudioRecvTrackForNack(&mock_receiver, &audio_desc);
publish_stream->audio_tracks_.push_back(audio_track);
// Test successful NACK check for both tracks
HELPER_EXPECT_SUCCESS(publish_stream->check_send_nacks());
// Test video track error propagation
video_track->set_check_send_nacks_error(srs_error_new(ERROR_RTC_RTP_MUXER, "mock video track error"));
HELPER_EXPECT_FAILED(publish_stream->check_send_nacks());
video_track->reset(); // Reset to success
// Test audio track error propagation
audio_track->set_check_send_nacks_error(srs_error_new(ERROR_RTC_RTP_MUXER, "mock audio track error"));
HELPER_EXPECT_FAILED(publish_stream->check_send_nacks());
audio_track->reset(); // Reset to success
// Final test: both tracks successful again
HELPER_EXPECT_SUCCESS(publish_stream->check_send_nacks());
}
// Helper function to create video track description with codec
SrsRtcTrackDescription *create_video_track_description_with_codec(std::string codec_name, uint32_t ssrc)
{
SrsRtcTrackDescription *desc = new SrsRtcTrackDescription();
desc->type_ = "video";
desc->ssrc_ = ssrc;
desc->id_ = "test-video-track";
desc->is_active_ = true;
desc->direction_ = "sendrecv";
desc->mid_ = "0";
// Create video payload with specified codec
SrsVideoPayload *video_payload = new SrsVideoPayload(96, codec_name, 90000);
desc->set_codec_payload(video_payload);
return desc;
}
VOID TEST(SrsRtcPublishStreamTest, OnBeforeDecodePayloadTypicalScenario)
{
// Create mock objects
MockRtcAsyncTaskExecutor mock_exec;
MockRtcExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsContextId cid;
cid.set_value("test-on-before-decode-payload-stream-id");
// Create SrsRtcPublishStream with mock dependencies
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, cid));
// Create video track with proper codec payload
SrsUniquePtr<SrsRtcTrackDescription> video_desc(create_video_track_description_with_codec("H264", 0x12345678));
SrsRtcVideoRecvTrack *video_track = new SrsRtcVideoRecvTrack(&mock_receiver, video_desc.get());
publish_stream->video_tracks_.push_back(video_track);
// Create audio track with proper codec payload
SrsUniquePtr<SrsRtcTrackDescription> audio_desc(create_test_track_description("audio", 0x87654321));
SrsRtcAudioRecvTrack *audio_track = new SrsRtcAudioRecvTrack(&mock_receiver, audio_desc.get());
publish_stream->audio_tracks_.push_back(audio_track);
// Test scenario 1: Empty buffer - should return early without processing
SrsUniquePtr<SrsRtpPacket> pkt1(new SrsRtpPacket());
pkt1->header_.set_ssrc(0x12345678); // Video track SSRC
char empty_buffer_data[1024];
SrsBuffer empty_buffer(empty_buffer_data, 0); // Empty buffer
ISrsRtpPayloader *payload1 = NULL;
SrsRtpPacketPayloadType ppt1 = SrsRtpPacketPayloadTypeUnknown;
// Call on_before_decode_payload with empty buffer - should return without setting payload
publish_stream->on_before_decode_payload(pkt1.get(), &empty_buffer, &payload1, &ppt1);
EXPECT_TRUE(payload1 == NULL); // Should remain NULL for empty buffer
EXPECT_EQ(SrsRtpPacketPayloadTypeUnknown, ppt1); // Should remain unknown
// Test scenario 2: Video track processing with non-empty buffer
SrsUniquePtr<SrsRtpPacket> pkt2(new SrsRtpPacket());
pkt2->header_.set_ssrc(0x12345678); // Video track SSRC
char video_buffer_data[1024];
memset(video_buffer_data, 0x42, 100); // Fill with test data
SrsBuffer video_buffer(video_buffer_data, 100); // Non-empty buffer
ISrsRtpPayloader *payload2 = NULL;
SrsRtpPacketPayloadType ppt2 = SrsRtpPacketPayloadTypeUnknown;
// Call on_before_decode_payload for video track - should delegate to video track
publish_stream->on_before_decode_payload(pkt2.get(), &video_buffer, &payload2, &ppt2);
// Video track should have processed the payload (implementation details depend on video track logic)
// Test scenario 3: Audio track processing with non-empty buffer
SrsUniquePtr<SrsRtpPacket> pkt3(new SrsRtpPacket());
pkt3->header_.set_ssrc(0x87654321); // Audio track SSRC
char audio_buffer_data[1024];
memset(audio_buffer_data, 0x55, 80); // Fill with test data
SrsBuffer audio_buffer(audio_buffer_data, 80); // Non-empty buffer
ISrsRtpPayloader *payload3 = NULL;
SrsRtpPacketPayloadType ppt3 = SrsRtpPacketPayloadTypeUnknown;
// Call on_before_decode_payload for audio track - should delegate to audio track
publish_stream->on_before_decode_payload(pkt3.get(), &audio_buffer, &payload3, &ppt3);
// Audio track should have processed the payload and set it to raw payload
EXPECT_TRUE(payload3 != NULL); // Audio track should create raw payload
EXPECT_EQ(SrsRtpPacketPayloadTypeRaw, ppt3); // Audio track should set raw payload type
// Test scenario 4: Unknown SSRC - should not match any track
SrsUniquePtr<SrsRtpPacket> pkt4(new SrsRtpPacket());
pkt4->header_.set_ssrc(0x99999999); // Unknown SSRC
char unknown_buffer_data[1024];
memset(unknown_buffer_data, 0x77, 50); // Fill with test data
SrsBuffer unknown_buffer(unknown_buffer_data, 50); // Non-empty buffer
ISrsRtpPayloader *payload4 = NULL;
SrsRtpPacketPayloadType ppt4 = SrsRtpPacketPayloadTypeUnknown;
// Call on_before_decode_payload for unknown SSRC - should not process
publish_stream->on_before_decode_payload(pkt4.get(), &unknown_buffer, &payload4, &ppt4);
EXPECT_TRUE(payload4 == NULL); // Should remain NULL for unknown SSRC
EXPECT_EQ(SrsRtpPacketPayloadTypeUnknown, ppt4); // Should remain unknown
// Clean up payload created by audio track
if (payload3) {
srs_freep(payload3);
}
}
VOID TEST(SrsRtcPublishStreamTest, SendPeriodicTwccTypicalScenario)
{
srs_error_t err;
// Create mock objects
MockRtcAsyncTaskExecutor mock_exec;
MockRtcExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsContextId cid;
cid.set_value("test-send-periodic-twcc-stream-id");
// Create SrsRtcPublishStream with mock dependencies
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, cid));
// Test scenario 1: No TWCC feedback needed - should return success immediately
HELPER_EXPECT_SUCCESS(publish_stream->send_periodic_twcc());
// Test scenario 2: Add some TWCC packets to trigger feedback
// First, add some packets to the TWCC receiver to make need_feedback() return true
uint16_t test_sn1 = 1000;
uint16_t test_sn2 = 1001;
uint16_t test_sn3 = 1002;
// Add packets to TWCC - this will make need_feedback() return true
HELPER_EXPECT_SUCCESS(publish_stream->on_twcc(test_sn1));
HELPER_EXPECT_SUCCESS(publish_stream->on_twcc(test_sn2));
HELPER_EXPECT_SUCCESS(publish_stream->on_twcc(test_sn3));
// Now send_periodic_twcc should process the feedback and send RTCP packets
HELPER_EXPECT_SUCCESS(publish_stream->send_periodic_twcc());
// Verify that RTCP packets were sent through the mock receiver
EXPECT_TRUE(mock_receiver.send_rtcp_count_ > 0);
// Test scenario 3: Test with receiver send_rtcp error
mock_receiver.set_send_rtcp_error(srs_error_new(ERROR_RTC_RTP_MUXER, "mock send rtcp error"));
// Add more packets to trigger feedback again
HELPER_EXPECT_SUCCESS(publish_stream->on_twcc(1003));
HELPER_EXPECT_SUCCESS(publish_stream->on_twcc(1004));
// send_periodic_twcc should fail due to receiver error
HELPER_EXPECT_FAILED(publish_stream->send_periodic_twcc());
// Reset receiver error for cleanup
mock_receiver.reset();
}
VOID TEST(SrsRtcPublishStreamTest, OnRtcpTypicalScenario)
{
srs_error_t err;
// Create mock objects
MockRtcAsyncTaskExecutor mock_exec;
MockRtcExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsContextId cid;
cid.set_value("test-on-rtcp-stream-id");
// Create SrsRtcPublishStream with mock dependencies
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, cid));
// Test scenario 1: RTCP SR (Sender Report) - should call on_rtcp_sr
SrsUniquePtr<SrsRtcpSR> sr(new SrsRtcpSR());
sr->set_ssrc(0x12345678);
sr->set_ntp(0x123456789ABCDEF0ULL);
sr->set_rtp_ts(1000);
sr->set_rtp_send_packets(100);
sr->set_rtp_send_bytes(50000);
HELPER_EXPECT_SUCCESS(publish_stream->on_rtcp(sr.get()));
// Test scenario 2: RTCP SDES - should be ignored and return success
SrsUniquePtr<SrsRtcpCommon> sdes(new SrsRtcpCommon());
sdes->header_.type = SrsRtcpType_sdes;
sdes->set_ssrc(0xAABBCCDD);
HELPER_EXPECT_SUCCESS(publish_stream->on_rtcp(sdes.get()));
// Test scenario 3: RTCP BYE - should be ignored and return success
SrsUniquePtr<SrsRtcpCommon> bye(new SrsRtcpCommon());
bye->header_.type = SrsRtcpType_bye;
bye->set_ssrc(0xEEFF0011);
HELPER_EXPECT_SUCCESS(publish_stream->on_rtcp(bye.get()));
// Test scenario 4: Unknown RTCP type - should return error
SrsUniquePtr<SrsRtcpCommon> unknown(new SrsRtcpCommon());
unknown->header_.type = 255; // Invalid/unknown RTCP type
unknown->set_ssrc(0x99999999);
HELPER_EXPECT_FAILED(publish_stream->on_rtcp(unknown.get()));
}
VOID TEST(SrsRtcPublishStreamTest, OnRtcpXrTypicalScenario)
{
srs_error_t err;
// Create mock objects
MockRtcAsyncTaskExecutor mock_exec;
MockRtcExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsContextId cid;
cid.set_value("test-on-rtcp-xr-stream-id");
// Create SrsRtcPublishStream with mock dependencies
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, cid));
// Create video track to receive RTT updates
SrsRtcTrackDescription video_desc;
video_desc.type_ = "video";
video_desc.id_ = "video_track_xr_test";
video_desc.ssrc_ = 0x12345678;
video_desc.is_active_ = true;
SrsRtcVideoRecvTrack *video_track = new SrsRtcVideoRecvTrack(&mock_receiver, &video_desc);
publish_stream->video_tracks_.push_back(video_track);
// Create a valid RTCP XR packet with DLRR block (block type 5)
// RTCP XR packet structure:
// - RTCP header (8 bytes): V=2, P=0, RC=0, PT=207(XR), length, SSRC
// - Report block: BT=5, reserved, block_length, SSRC, LRR, DLRR
unsigned char xr_data[] = {
// RTCP header (8 bytes)
0x80, 0xCF, 0x00, 0x05, // V=2, P=0, RC=0, PT=207(XR), length=5 (24 bytes total)
0x87, 0x65, 0x43, 0x21, // SSRC of XR packet sender
// DLRR report block (16 bytes)
0x05, 0x00, 0x00, 0x03, // BT=5 (DLRR), reserved=0, block_length=3 (12 bytes)
0x12, 0x34, 0x56, 0x78, // SSRC of receiver (matches video track SSRC)
0x12, 0x34, 0x56, 0x78, // LRR (Last Receiver Report) - 32-bit compact NTP
0x00, 0x00, 0x10, 0x00 // DLRR (Delay since Last Receiver Report) - 32-bit value
};
// Create SrsRtcpXr object and set its data
SrsUniquePtr<SrsRtcpXr> xr(new SrsRtcpXr());
xr->set_ssrc(0x87654321);
// Set the raw data for the XR packet (simulate what decode() would do)
xr->data_ = (char *)xr_data;
xr->nb_data_ = sizeof(xr_data);
// Test RTCP XR processing - should parse DLRR block and update RTT
HELPER_EXPECT_SUCCESS(publish_stream->on_rtcp_xr(xr.get()));
// The function should have processed the DLRR block and called update_rtt
// RTT calculation: compact_ntp - lrr - dlrr
// This is a typical scenario where RTT is calculated from timing information
}
VOID TEST(SrsRtcPublishStreamTest, RequestKeyframeTypicalScenario)
{
srs_error_t err;
// Create mock objects
MockRtcAsyncTaskExecutor mock_exec;
MockRtcExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsContextId cid;
cid.set_value("test-request-keyframe-stream-id");
// Create SrsRtcPublishStream with mock dependencies
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, cid));
// Test typical keyframe request scenario
uint32_t test_ssrc = 0x12345678;
SrsContextId subscriber_cid;
subscriber_cid.set_value("test-subscriber-cid");
// Test request_keyframe function - should delegate to pli_worker and log appropriately
publish_stream->request_keyframe(test_ssrc, subscriber_cid);
// Test do_request_keyframe function - should call receiver's send_rtcp_fb_pli
HELPER_EXPECT_SUCCESS(publish_stream->do_request_keyframe(test_ssrc, subscriber_cid));
// Verify that PLI packet was sent through the mock receiver
EXPECT_EQ(1, mock_receiver.send_rtcp_fb_pli_count_);
// Test error handling in do_request_keyframe
mock_receiver.set_send_rtcp_fb_pli_error(srs_error_new(ERROR_RTC_RTP_MUXER, "mock PLI send error"));
// Should still return success but log the error (error is freed internally)
HELPER_EXPECT_SUCCESS(publish_stream->do_request_keyframe(test_ssrc, subscriber_cid));
// Verify that PLI packet send was attempted again
EXPECT_EQ(2, mock_receiver.send_rtcp_fb_pli_count_);
// Reset receiver for cleanup
mock_receiver.reset();
}
VOID TEST(SrsRtcPublishStreamTest, UpdateRttTypicalScenario)
{
// Create mock objects
MockRtcAsyncTaskExecutor mock_exec;
MockRtcExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsContextId cid;
cid.set_value("test-update-rtt-stream-id");
// Create SrsRtcPublishStream with mock dependencies
SrsUniquePtr<SrsRtcPublishStream> publish_stream(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, cid));
// Create audio track with specific SSRC
SrsRtcTrackDescription audio_desc;
audio_desc.type_ = "audio";
audio_desc.id_ = "audio_track_rtt_test";
audio_desc.ssrc_ = 0x87654321;
audio_desc.is_active_ = true;
SrsRtcAudioRecvTrack *audio_track = new SrsRtcAudioRecvTrack(&mock_receiver, &audio_desc);
publish_stream->audio_tracks_.push_back(audio_track);
// Test typical RTT update scenario for audio track
uint32_t test_ssrc = 0x87654321; // Matches audio track SSRC
int test_rtt = 50; // 50ms RTT
// Call update_rtt - should find audio track and update its RTT
publish_stream->update_rtt(test_ssrc, test_rtt);
// The function should have found the audio track and called update_rtt on it
// This delegates to the NACK receiver which updates its RTT and nack_interval
// No return value to check, but the function should complete without error
// Test with unknown SSRC - should not find any track and return silently
uint32_t unknown_ssrc = 0x99999999;
publish_stream->update_rtt(unknown_ssrc, test_rtt);
// Function should handle unknown SSRC gracefully without error
}

View File

@ -0,0 +1,53 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_UTEST_APP7_HPP
#define SRS_UTEST_APP7_HPP
/*
#include <srs_utest_app7.hpp>
*/
#include <srs_utest.hpp>
#include <srs_app_rtc_conn.hpp>
#include <srs_app_rtc_source.hpp>
#include <srs_utest_app6.hpp>
// Mock video recv track for testing check_send_nacks
class MockRtcVideoRecvTrackForNack : public SrsRtcVideoRecvTrack
{
public:
srs_error_t check_send_nacks_error_;
int check_send_nacks_count_;
public:
MockRtcVideoRecvTrackForNack(ISrsRtcPacketReceiver *receiver, SrsRtcTrackDescription *track_desc);
virtual ~MockRtcVideoRecvTrackForNack();
public:
virtual srs_error_t check_send_nacks();
void set_check_send_nacks_error(srs_error_t err);
void reset();
};
// Mock audio recv track for testing check_send_nacks
class MockRtcAudioRecvTrackForNack : public SrsRtcAudioRecvTrack
{
public:
srs_error_t check_send_nacks_error_;
int check_send_nacks_count_;
public:
MockRtcAudioRecvTrackForNack(ISrsRtcPacketReceiver *receiver, SrsRtcTrackDescription *track_desc);
virtual ~MockRtcAudioRecvTrackForNack();
public:
virtual srs_error_t check_send_nacks();
void set_check_send_nacks_error(srs_error_t err);
void reset();
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_UTEST_APP_RTC2RTMP_HPP
#define SRS_UTEST_APP_RTC2RTMP_HPP
/*
#include <srs_utest_app_rtc2rtmp.hpp>
*/
#include <srs_utest.hpp>
#include <srs_app_stream_bridge.hpp>
#include <srs_protocol_rtmp_stack.hpp>
// Mock frame target for testing SrsRtcFrameBuilder
class MockRtc2RtmpFrameTarget : public ISrsFrameTarget
{
public:
int on_frame_count_;
SrsMediaPacket *last_frame_;
srs_error_t frame_error_;
public:
MockRtc2RtmpFrameTarget();
virtual ~MockRtc2RtmpFrameTarget();
virtual srs_error_t on_frame(SrsMediaPacket *frame);
void reset();
void set_frame_error(srs_error_t err);
};
// Mock request class for testing
class MockRtc2RtmpRequest : public ISrsRequest
{
public:
std::string vhost_;
std::string app_;
std::string stream_;
std::string host_;
public:
MockRtc2RtmpRequest(std::string vhost = "__defaultVhost__", std::string app = "live", std::string stream = "test");
virtual ~MockRtc2RtmpRequest();
virtual ISrsRequest *copy();
virtual std::string get_stream_url();
virtual void update_auth(ISrsRequest *req);
virtual void strip();
virtual ISrsRequest *as_http();
};
extern SrsNaluSample *mock_create_nalu_sample(const uint8_t *data, int size);
extern SrsRtpPacket *mock_create_stap_packet_with_sps_pps(uint16_t seq, uint32_t ts);
extern SrsRtpPacket *mock_create_idr_packet(uint16_t seq, uint32_t ts, bool marker);
extern SrsRtpPacket *mock_create_p_frame_packet(uint16_t seq, uint32_t ts, bool marker);
extern SrsRtpPacket *mock_create_audio_packet(uint16_t seq, uint32_t ts, uint32_t avsync_time);
#endif

View File

@ -3169,7 +3169,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig2)
EXPECT_FALSE(conf.get_mr_enabled("ossrs.net")); EXPECT_FALSE(conf.get_mr_enabled("ossrs.net"));
EXPECT_EQ(350 * SRS_UTIME_MILLISECONDS, conf.get_mr_sleep("ossrs.net")); EXPECT_EQ(350 * SRS_UTIME_MILLISECONDS, conf.get_mr_sleep("ossrs.net"));
EXPECT_EQ(350 * SRS_UTIME_MILLISECONDS, conf.get_mw_sleep("ossrs.net")); EXPECT_EQ(350 * SRS_UTIME_MILLISECONDS, conf.get_mw_sleep("ossrs.net"));
EXPECT_FALSE(conf.get_realtime_enabled("ossrs.net")); EXPECT_FALSE(conf.get_realtime_enabled("ossrs.net", false));
EXPECT_FALSE(conf.get_tcp_nodelay("ossrs.net")); EXPECT_FALSE(conf.get_tcp_nodelay("ossrs.net"));
EXPECT_EQ(0, (int)conf.get_send_min_interval("ossrs.net")); EXPECT_EQ(0, (int)conf.get_send_min_interval("ossrs.net"));
EXPECT_FALSE(conf.get_reduce_sequence_header("ossrs.net")); EXPECT_FALSE(conf.get_reduce_sequence_header("ossrs.net"));
@ -3231,7 +3231,7 @@ VOID TEST(ConfigMainTest, CheckVhostConfig2)
if (true) { if (true) {
MockSrsConfig conf; MockSrsConfig conf;
HELPER_ASSERT_SUCCESS(conf.mock_parse(_MIN_OK_CONF "vhost ossrs.net{min_latency on;}")); HELPER_ASSERT_SUCCESS(conf.mock_parse(_MIN_OK_CONF "vhost ossrs.net{min_latency on;}"));
EXPECT_TRUE(conf.get_realtime_enabled("ossrs.net")); EXPECT_TRUE(conf.get_realtime_enabled("ossrs.net", false));
} }
if (true) { if (true) {

View File

@ -547,43 +547,43 @@ VOID TEST(KernelFastBufferTest, Grow)
srs_error_t err; srs_error_t err;
if (true) { if (true) {
SrsFastStream b(5); SrsUniquePtr<SrsFastStream> b(new SrsFastStream(5));
MockBufferReader r("Hello, world!"); MockBufferReader r("Hello, world!");
HELPER_ASSERT_SUCCESS(b.grow(&r, 5)); HELPER_ASSERT_SUCCESS(b->grow(&r, 5));
b.skip(2); b->skip(2);
HELPER_ASSERT_FAILED(b.grow(&r, 6)); HELPER_ASSERT_FAILED(b->grow(&r, 6));
} }
if (true) { if (true) {
SrsFastStream b(5); SrsUniquePtr<SrsFastStream> b(new SrsFastStream(5));
MockBufferReader r("Hello, world!"); MockBufferReader r("Hello, world!");
HELPER_ASSERT_SUCCESS(b.grow(&r, 5)); HELPER_ASSERT_SUCCESS(b->grow(&r, 5));
b.skip(5); b->skip(5);
HELPER_ASSERT_FAILED(b.grow(&r, 6)); HELPER_ASSERT_FAILED(b->grow(&r, 6));
} }
if (true) { if (true) {
SrsFastStream b(6); SrsUniquePtr<SrsFastStream> b(new SrsFastStream(6));
MockBufferReader r("Hello, world!"); MockBufferReader r("Hello, world!");
HELPER_ASSERT_SUCCESS(b.grow(&r, 5)); HELPER_ASSERT_SUCCESS(b->grow(&r, 5));
EXPECT_EQ('H', b.read_1byte()); EXPECT_EQ('H', b->read_1byte());
EXPECT_EQ('e', b.read_1byte()); EXPECT_EQ('e', b->read_1byte());
EXPECT_EQ('l', b.read_1byte()); EXPECT_EQ('l', b->read_1byte());
b.skip(2); b->skip(2);
HELPER_ASSERT_SUCCESS(b.grow(&r, 2)); HELPER_ASSERT_SUCCESS(b->grow(&r, 2));
b.skip(2); b->skip(2);
HELPER_ASSERT_SUCCESS(b.grow(&r, 5)); HELPER_ASSERT_SUCCESS(b->grow(&r, 5));
EXPECT_EQ('w', b.read_1byte()); EXPECT_EQ('w', b->read_1byte());
EXPECT_EQ('o', b.read_1byte()); EXPECT_EQ('o', b->read_1byte());
EXPECT_EQ('r', b.read_1byte()); EXPECT_EQ('r', b->read_1byte());
b.skip(2); b->skip(2);
} }
if (true) { if (true) {
@ -1696,11 +1696,11 @@ VOID TEST(KernelFLVTest, CoverFLVVodError)
VOID TEST(KernelStreamTest, StreamPos) VOID TEST(KernelStreamTest, StreamPos)
{ {
char data[1024]; char data[1024];
SrsBuffer s(data, 1024); SrsUniquePtr<SrsBuffer> s(new SrsBuffer(data, 1024));
EXPECT_TRUE(s.pos() == 0); EXPECT_TRUE(s->pos() == 0);
s.read_bytes(data, 1024); s->read_bytes(data, 1024);
EXPECT_TRUE(s.pos() == 1024); EXPECT_TRUE(s->pos() == 1024);
} }
/** /**
@ -1709,11 +1709,11 @@ VOID TEST(KernelStreamTest, StreamPos)
VOID TEST(KernelStreamTest, StreamEmpty) VOID TEST(KernelStreamTest, StreamEmpty)
{ {
char data[1024]; char data[1024];
SrsBuffer s(data, 1024); SrsUniquePtr<SrsBuffer> s(new SrsBuffer(data, 1024));
EXPECT_FALSE(s.empty()); EXPECT_FALSE(s->empty());
s.read_bytes(data, 1024); s->read_bytes(data, 1024);
EXPECT_TRUE(s.empty()); EXPECT_TRUE(s->empty());
} }
/** /**
@ -1722,15 +1722,15 @@ VOID TEST(KernelStreamTest, StreamEmpty)
VOID TEST(KernelStreamTest, StreamRequire) VOID TEST(KernelStreamTest, StreamRequire)
{ {
char data[1024]; char data[1024];
SrsBuffer s(data, 1024); SrsUniquePtr<SrsBuffer> s(new SrsBuffer(data, 1024));
EXPECT_TRUE(s.require(1)); EXPECT_TRUE(s->require(1));
EXPECT_TRUE(s.require(1024)); EXPECT_TRUE(s->require(1024));
s.read_bytes(data, 1000); s->read_bytes(data, 1000);
EXPECT_TRUE(s.require(1)); EXPECT_TRUE(s->require(1));
s.read_bytes(data, 24); s->read_bytes(data, 24);
EXPECT_FALSE(s.require(1)); EXPECT_FALSE(s->require(1));
} }
/** /**
@ -1739,14 +1739,14 @@ VOID TEST(KernelStreamTest, StreamRequire)
VOID TEST(KernelStreamTest, StreamSkip) VOID TEST(KernelStreamTest, StreamSkip)
{ {
char data[1024]; char data[1024];
SrsBuffer s(data, 1024); SrsUniquePtr<SrsBuffer> s(new SrsBuffer(data, 1024));
EXPECT_EQ(0, s.pos()); EXPECT_EQ(0, s->pos());
s.skip(1); s->skip(1);
EXPECT_EQ(1, s.pos()); EXPECT_EQ(1, s->pos());
s.skip(-1); s->skip(-1);
EXPECT_EQ(0, s.pos()); EXPECT_EQ(0, s->pos());
} }
/** /**
@ -1755,17 +1755,17 @@ VOID TEST(KernelStreamTest, StreamSkip)
VOID TEST(KernelStreamTest, StreamRead1Bytes) VOID TEST(KernelStreamTest, StreamRead1Bytes)
{ {
char data[1024]; char data[1024];
SrsBuffer s(data, 1024); SrsUniquePtr<SrsBuffer> s(new SrsBuffer(data, 1024));
data[0] = 0x12; data[0] = 0x12;
data[99] = 0x13; data[99] = 0x13;
data[100] = 0x14; data[100] = 0x14;
data[101] = 0x15; data[101] = 0x15;
EXPECT_EQ(0x12, s.read_1bytes()); EXPECT_EQ(0x12, s->read_1bytes());
s.skip(-1 * s.pos()); s->skip(-1 * s->pos());
s.skip(100); s->skip(100);
EXPECT_EQ(0x14, s.read_1bytes()); EXPECT_EQ(0x14, s->read_1bytes());
} }
/** /**
@ -1774,7 +1774,7 @@ VOID TEST(KernelStreamTest, StreamRead1Bytes)
VOID TEST(KernelStreamTest, StreamRead2Bytes) VOID TEST(KernelStreamTest, StreamRead2Bytes)
{ {
char data[1024]; char data[1024];
SrsBuffer s(data, 1024); SrsUniquePtr<SrsBuffer> s(new SrsBuffer(data, 1024));
data[0] = 0x01; data[0] = 0x01;
data[1] = 0x02; data[1] = 0x02;
@ -1787,12 +1787,12 @@ VOID TEST(KernelStreamTest, StreamRead2Bytes)
data[8] = 0x09; data[8] = 0x09;
data[9] = 0x0a; data[9] = 0x0a;
EXPECT_EQ(0x0102, s.read_2bytes()); EXPECT_EQ(0x0102, s->read_2bytes());
EXPECT_EQ(0x0304, s.read_2bytes()); EXPECT_EQ(0x0304, s->read_2bytes());
s.skip(-1 * s.pos()); s->skip(-1 * s->pos());
s.skip(3); s->skip(3);
EXPECT_EQ(0x0405, s.read_2bytes()); EXPECT_EQ(0x0405, s->read_2bytes());
} }
/** /**

View File

@ -104,7 +104,7 @@ public:
void clear(); void clear();
}; };
class MockSrsFastTimer : public ISrsFastTimer class MockSrsFastTimer : public ISrsFastTimerHandler
{ {
public: public:
std::vector<srs_utime_t> timer_calls_; std::vector<srs_utime_t> timer_calls_;

View File

@ -15,6 +15,8 @@
#include <srs_kernel_rtc_rtp.hpp> #include <srs_kernel_rtc_rtp.hpp>
#include <srs_protocol_conn.hpp> #include <srs_protocol_conn.hpp>
#include <srs_utest_app6.hpp>
#include <srs_utest_protocol3.hpp>
#include <srs_utest_service.hpp> #include <srs_utest_service.hpp>
#include <vector> #include <vector>
@ -1328,11 +1330,15 @@ VOID TEST(KernelRTCTest, DumpsHexToString)
VOID TEST(KernelRTCTest, NACKFetchRTPPacket) VOID TEST(KernelRTCTest, NACKFetchRTPPacket)
{ {
SrsRtcConnection s(NULL, SrsContextId()); // Create mock interfaces for SrsRtcPlayStream constructor
SrsRtcPlayStream play(&s, SrsContextId()); MockRtcAsyncTaskExecutor mock_executor;
MockExpire mock_expire;
MockRtcPacketSender mock_sender;
SrsUniquePtr<SrsRtcPlayStream> play(new SrsRtcPlayStream(&mock_executor, &mock_expire, &mock_sender, SrsContextId()));
SrsRtcTrackDescription ds; SrsRtcTrackDescription ds;
SrsRtcVideoSendTrack *track = new SrsRtcVideoSendTrack(&s, &ds); SrsRtcVideoSendTrack *track = new SrsRtcVideoSendTrack(&mock_sender, &ds);
SrsUniquePtr<SrsRtcVideoSendTrack> track_uptr(track); SrsUniquePtr<SrsRtcVideoSendTrack> track_uptr(track);
// The RTP queue will free the packet. // The RTP queue will free the packet.
@ -1556,8 +1562,12 @@ VOID TEST(KernelRTCTest, DefaultTrackStatus)
// Enable it by player. // Enable it by player.
if (true) { if (true) {
SrsRtcConnection s(NULL, SrsContextId()); // Create mock interfaces for SrsRtcPlayStream constructor
SrsRtcPlayStream play(&s, SrsContextId()); MockRtcAsyncTaskExecutor mock_executor;
MockExpire mock_expire;
MockRtcPacketSender mock_sender;
SrsUniquePtr<SrsRtcPlayStream> play(new SrsRtcPlayStream(&mock_executor, &mock_expire, &mock_sender, SrsContextId()));
SrsRtcAudioSendTrack *audio; SrsRtcAudioSendTrack *audio;
SrsRtcVideoSendTrack *video; SrsRtcVideoSendTrack *video;
@ -1566,27 +1576,29 @@ VOID TEST(KernelRTCTest, DefaultTrackStatus)
ds.type_ = "audio"; ds.type_ = "audio";
ds.id_ = "NSNWOn19NDn12o8nNeji2"; ds.id_ = "NSNWOn19NDn12o8nNeji2";
ds.ssrc_ = 100; ds.ssrc_ = 100;
play.audio_tracks_[ds.ssrc_] = audio = new SrsRtcAudioSendTrack(&s, &ds); play->audio_tracks_[ds.ssrc_] = audio = new SrsRtcAudioSendTrack(&mock_sender, &ds);
} }
if (true) { if (true) {
SrsRtcTrackDescription ds; SrsRtcTrackDescription ds;
ds.type_ = "video"; ds.type_ = "video";
ds.id_ = "VMo22nfLDn122nfnDNL2"; ds.id_ = "VMo22nfLDn122nfnDNL2";
ds.ssrc_ = 200; ds.ssrc_ = 200;
play.video_tracks_[ds.ssrc_] = video = new SrsRtcVideoSendTrack(&s, &ds); play->video_tracks_[ds.ssrc_] = video = new SrsRtcVideoSendTrack(&mock_sender, &ds);
} }
EXPECT_FALSE(audio->get_track_status()); EXPECT_FALSE(audio->get_track_status());
EXPECT_FALSE(video->get_track_status()); EXPECT_FALSE(video->get_track_status());
play.set_all_tracks_status(true); play->set_all_tracks_status(true);
EXPECT_TRUE(audio->get_track_status()); EXPECT_TRUE(audio->get_track_status());
EXPECT_TRUE(video->get_track_status()); EXPECT_TRUE(video->get_track_status());
} }
// Enable it by publisher. // Enable it by publisher.
if (true) { if (true) {
SrsRtcConnection s(NULL, SrsContextId()); MockRtcAsyncTaskExecutor mock_exec;
SrsRtcPublishStream publish(&s, SrsContextId()); MockExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsUniquePtr<SrsRtcPublishStream> publish(new SrsRtcPublishStream(&mock_exec, &mock_expire, &mock_receiver, SrsContextId()));
SrsRtcAudioRecvTrack *audio; SrsRtcAudioRecvTrack *audio;
SrsRtcVideoRecvTrack *video; SrsRtcVideoRecvTrack *video;
@ -1595,21 +1607,21 @@ VOID TEST(KernelRTCTest, DefaultTrackStatus)
ds.type_ = "audio"; ds.type_ = "audio";
ds.id_ = "NSNWOn19NDn12o8nNeji2"; ds.id_ = "NSNWOn19NDn12o8nNeji2";
ds.ssrc_ = 100; ds.ssrc_ = 100;
audio = new SrsRtcAudioRecvTrack(&s, &ds); audio = new SrsRtcAudioRecvTrack(&mock_receiver, &ds);
publish.audio_tracks_.push_back(audio); publish->audio_tracks_.push_back(audio);
} }
if (true) { if (true) {
SrsRtcTrackDescription ds; SrsRtcTrackDescription ds;
ds.type_ = "video"; ds.type_ = "video";
ds.id_ = "VMo22nfLDn122nfnDNL2"; ds.id_ = "VMo22nfLDn122nfnDNL2";
ds.ssrc_ = 200; ds.ssrc_ = 200;
video = new SrsRtcVideoRecvTrack(&s, &ds); video = new SrsRtcVideoRecvTrack(&mock_receiver, &ds);
publish.video_tracks_.push_back(video); publish->video_tracks_.push_back(video);
} }
EXPECT_FALSE(audio->get_track_status()); EXPECT_FALSE(audio->get_track_status());
EXPECT_FALSE(video->get_track_status()); EXPECT_FALSE(video->get_track_status());
publish.set_all_tracks_status(true); publish->set_all_tracks_status(true);
EXPECT_TRUE(audio->get_track_status()); EXPECT_TRUE(audio->get_track_status());
EXPECT_TRUE(video->get_track_status()); EXPECT_TRUE(video->get_track_status());
} }
@ -1646,15 +1658,17 @@ VOID TEST(KernelRTCTest, Ntp)
VOID TEST(KernelRTCTest, SyncTimestampBySenderReportNormal) VOID TEST(KernelRTCTest, SyncTimestampBySenderReportNormal)
{ {
SrsRtcConnection s(NULL, SrsContextId()); MockRtcAsyncTaskExecutor mock_exec;
SrsRtcPublishStream publish(&s, SrsContextId()); MockExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsRtcPublishStream publish(&mock_exec, &mock_expire, &mock_receiver, SrsContextId());
SrsRtcTrackDescription video_ds; SrsRtcTrackDescription video_ds;
video_ds.type_ = "video"; video_ds.type_ = "video";
video_ds.id_ = "VMo22nfLDn122nfnDNL2"; video_ds.id_ = "VMo22nfLDn122nfnDNL2";
video_ds.ssrc_ = 200; video_ds.ssrc_ = 200;
SrsRtcVideoRecvTrack *video = new SrsRtcVideoRecvTrack(&s, &video_ds); SrsRtcVideoRecvTrack *video = new SrsRtcVideoRecvTrack(&mock_receiver, &video_ds);
publish.video_tracks_.push_back(video); publish.video_tracks_.push_back(video);
publish.set_all_tracks_status(true); publish.set_all_tracks_status(true);
@ -1712,15 +1726,17 @@ VOID TEST(KernelRTCTest, SyncTimestampBySenderReportNormal)
VOID TEST(KernelRTCTest, SyncTimestampBySenderReportOutOfOrder) VOID TEST(KernelRTCTest, SyncTimestampBySenderReportOutOfOrder)
{ {
SrsRtcConnection s(NULL, SrsContextId()); MockRtcAsyncTaskExecutor mock_exec;
SrsRtcPublishStream publish(&s, SrsContextId()); MockExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsRtcPublishStream publish(&mock_exec, &mock_expire, &mock_receiver, SrsContextId());
SrsRtcTrackDescription video_ds; SrsRtcTrackDescription video_ds;
video_ds.type_ = "video"; video_ds.type_ = "video";
video_ds.id_ = "VMo22nfLDn122nfnDNL2"; video_ds.id_ = "VMo22nfLDn122nfnDNL2";
video_ds.ssrc_ = 200; video_ds.ssrc_ = 200;
SrsRtcVideoRecvTrack *video = new SrsRtcVideoRecvTrack(&s, &video_ds); SrsRtcVideoRecvTrack *video = new SrsRtcVideoRecvTrack(&mock_receiver, &video_ds);
publish.video_tracks_.push_back(video); publish.video_tracks_.push_back(video);
publish.set_all_tracks_status(true); publish.set_all_tracks_status(true);
@ -1783,15 +1799,17 @@ VOID TEST(KernelRTCTest, SyncTimestampBySenderReportOutOfOrder)
VOID TEST(KernelRTCTest, SyncTimestampBySenderReportConsecutive) VOID TEST(KernelRTCTest, SyncTimestampBySenderReportConsecutive)
{ {
SrsRtcConnection s(NULL, SrsContextId()); MockRtcAsyncTaskExecutor mock_exec;
SrsRtcPublishStream publish(&s, SrsContextId()); MockExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsRtcPublishStream publish(&mock_exec, &mock_expire, &mock_receiver, SrsContextId());
SrsRtcTrackDescription video_ds; SrsRtcTrackDescription video_ds;
video_ds.type_ = "video"; video_ds.type_ = "video";
video_ds.id_ = "VMo22nfLDn122nfnDNL2"; video_ds.id_ = "VMo22nfLDn122nfnDNL2";
video_ds.ssrc_ = 200; video_ds.ssrc_ = 200;
SrsRtcVideoRecvTrack *video = new SrsRtcVideoRecvTrack(&s, &video_ds); SrsRtcVideoRecvTrack *video = new SrsRtcVideoRecvTrack(&mock_receiver, &video_ds);
publish.video_tracks_.push_back(video); publish.video_tracks_.push_back(video);
publish.set_all_tracks_status(true); publish.set_all_tracks_status(true);
@ -1887,15 +1905,17 @@ VOID TEST(KernelRTCTest, SrsRtcpNack)
VOID TEST(KernelRTCTest, SyncTimestampBySenderReportDuplicated) VOID TEST(KernelRTCTest, SyncTimestampBySenderReportDuplicated)
{ {
SrsRtcConnection s(NULL, SrsContextId()); MockRtcAsyncTaskExecutor mock_exec;
SrsRtcPublishStream publish(&s, SrsContextId()); MockExpire mock_expire;
MockRtcPacketReceiver mock_receiver;
SrsRtcPublishStream publish(&mock_exec, &mock_expire, &mock_receiver, SrsContextId());
SrsRtcTrackDescription video_ds; SrsRtcTrackDescription video_ds;
video_ds.type_ = "video"; video_ds.type_ = "video";
video_ds.id_ = "VMo22nfLDn122nfnDNL2"; video_ds.id_ = "VMo22nfLDn122nfnDNL2";
video_ds.ssrc_ = 200; video_ds.ssrc_ = 200;
SrsRtcVideoRecvTrack *video = new SrsRtcVideoRecvTrack(&s, &video_ds); SrsRtcVideoRecvTrack *video = new SrsRtcVideoRecvTrack(&mock_receiver, &video_ds);
publish.video_tracks_.push_back(video); publish.video_tracks_.push_back(video);
publish.set_all_tracks_status(true); publish.set_all_tracks_status(true);

View File

@ -37,18 +37,15 @@ VOID TEST(StTest, MutexPtrSugar)
VOID TEST(StTest, StUtimeInMicroseconds) VOID TEST(StTest, StUtimeInMicroseconds)
{ {
st_utime_t st_time_1 = st_utime(); st_utime_t st_time_1 = st_utime();
// sleep 1 microsecond usleep(1); // sleep 1 microsecond
#if !defined(SRS_CYGWIN64)
usleep(1);
#endif
st_utime_t st_time_2 = st_utime(); st_utime_t st_time_2 = st_utime();
EXPECT_GT(st_time_1, 0); EXPECT_GT(st_time_1, 0);
EXPECT_GT(st_time_2, 0); EXPECT_GT(st_time_2, 0);
EXPECT_GE(st_time_2, st_time_1); EXPECT_GE(st_time_2, st_time_1);
// st_time_2 - st_time_1 should be in range of [1, 300] microseconds // st_time_2 - st_time_1 should be in range of [1, 1000] microseconds
EXPECT_GE(st_time_2 - st_time_1, 0); EXPECT_GE(st_time_2 - st_time_1, 0);
EXPECT_LE(st_time_2 - st_time_1, 300); EXPECT_LE(st_time_2 - st_time_1, 1000);
} }
static inline st_utime_t time_gettimeofday() static inline st_utime_t time_gettimeofday()