AI: Add utest to cover dash module.

This commit is contained in:
OSSRS-AI 2025-10-09 09:34:11 -04:00 committed by winlin
parent 646b833757
commit de3d5bd1f5
27 changed files with 4619 additions and 139 deletions

3
trunk/configure vendored
View File

@ -384,7 +384,8 @@ if [[ $SRS_UTEST == YES ]]; then
"srs_utest_coworkers" "srs_utest_pithy_print" "srs_utest_kernel3" "srs_utest_protocol4"
"srs_utest_protocol3" "srs_utest_app" "srs_utest_app2" "srs_utest_app3" "srs_utest_app4"
"srs_utest_app5" "srs_utest_app6" "srs_utest_app7" "srs_utest_app8" "srs_utest_app9"
"srs_utest_app10" "srs_utest_app11" "srs_utest_app12" "srs_utest_app13" "srs_utest_app14")
"srs_utest_app10" "srs_utest_app11" "srs_utest_app12" "srs_utest_app13" "srs_utest_app14"
"srs_utest_app15")
# Always include SRT utest
MODULE_FILES+=("srs_utest_srt")
if [[ $SRS_GB28181 == YES ]]; then

View File

@ -344,6 +344,7 @@ public:
virtual std::string get_rtc_server_protocol() = 0;
virtual std::vector<std::string> get_rtc_server_listens() = 0;
virtual int get_rtc_server_reuseport() = 0;
virtual bool get_rtc_server_encrypt() = 0;
public:
// RTSP config
@ -453,6 +454,8 @@ public:
virtual bool get_rtc_to_rtmp(std::string vhost) = 0;
virtual srs_utime_t get_rtc_stun_timeout(std::string vhost) = 0;
virtual bool get_rtc_stun_strict_check(std::string vhost) = 0;
virtual std::string get_rtc_dtls_role(std::string vhost) = 0;
virtual std::string get_rtc_dtls_version(std::string vhost) = 0;
virtual SrsConfDirective *get_vhost_on_hls(std::string vhost) = 0;
virtual SrsConfDirective *get_vhost_on_hls_notify(std::string vhost) = 0;
virtual bool get_hls_enabled(std::string vhost) = 0;
@ -483,6 +486,16 @@ public:
virtual bool get_hls_ctx_enabled(std::string vhost) = 0;
virtual bool get_hls_ts_ctx_enabled(std::string vhost) = 0;
virtual bool get_hls_recover(std::string vhost) = 0;
virtual bool get_dash_enabled(std::string vhost) = 0;
virtual bool get_dash_enabled(SrsConfDirective *vhost) = 0;
virtual srs_utime_t get_dash_fragment(std::string vhost) = 0;
virtual srs_utime_t get_dash_update_period(std::string vhost) = 0;
virtual srs_utime_t get_dash_timeshift(std::string vhost) = 0;
virtual std::string get_dash_path(std::string vhost) = 0;
virtual std::string get_dash_mpd_file(std::string vhost) = 0;
virtual int get_dash_window_size(std::string vhost) = 0;
virtual bool get_dash_cleanup(std::string vhost) = 0;
virtual srs_utime_t get_dash_dispose(std::string vhost) = 0;
virtual bool get_forward_enabled(std::string vhost) = 0;
virtual SrsConfDirective *get_forwards(std::string vhost) = 0;
virtual srs_utime_t get_queue_length(std::string vhost) = 0;

View File

@ -7,6 +7,7 @@
#include <srs_app_dash.hpp>
#include <srs_app_config.hpp>
#include <srs_app_factory.hpp>
#include <srs_app_rtmp_source.hpp>
#include <srs_app_utility.hpp>
#include <srs_core_autofree.hpp>
@ -35,23 +36,33 @@ string srs_time_to_utc_format_str(srs_utime_t u)
return std::string(print_buf, ret);
}
ISrsInitMp4::ISrsInitMp4()
{
}
ISrsInitMp4::~ISrsInitMp4()
{
}
SrsInitMp4::SrsInitMp4()
{
fw_ = new SrsFileWriter();
init_ = new SrsMp4M2tsInitEncoder();
fragment_ = new SrsFragment();
}
SrsInitMp4::~SrsInitMp4()
{
srs_freep(init_);
srs_freep(fw_);
srs_freep(fragment_);
}
srs_error_t SrsInitMp4::write(SrsFormat *format, bool video, int tid)
{
srs_error_t err = srs_success;
string path_tmp = tmppath();
string path_tmp = fragment_->tmppath();
if ((err = fw_->open(path_tmp)) != srs_success) {
return srs_error_wrap(err, "Open init mp4 failed, path=%s", path_tmp.c_str());
}
@ -67,19 +78,88 @@ srs_error_t SrsInitMp4::write(SrsFormat *format, bool video, int tid)
return err;
}
void SrsInitMp4::set_path(std::string v)
{
fragment_->set_path(v);
}
std::string SrsInitMp4::tmppath()
{
return fragment_->tmppath();
}
srs_error_t SrsInitMp4::rename()
{
return fragment_->rename();
}
void SrsInitMp4::append(int64_t dts)
{
fragment_->append(dts);
}
srs_error_t SrsInitMp4::create_dir()
{
return fragment_->create_dir();
}
void SrsInitMp4::set_number(uint64_t n)
{
fragment_->set_number(n);
}
uint64_t SrsInitMp4::number()
{
return fragment_->number();
}
srs_utime_t SrsInitMp4::duration()
{
return fragment_->duration();
}
srs_error_t SrsInitMp4::unlink_tmpfile()
{
return fragment_->unlink_tmpfile();
}
srs_utime_t SrsInitMp4::get_start_dts()
{
return fragment_->get_start_dts();
}
srs_error_t SrsInitMp4::unlink_file()
{
return fragment_->unlink_file();
}
ISrsFragmentedMp4::ISrsFragmentedMp4()
{
}
ISrsFragmentedMp4::~ISrsFragmentedMp4()
{
}
SrsFragmentedMp4::SrsFragmentedMp4()
{
fw_ = new SrsFileWriter();
enc_ = new SrsMp4M2tsSegmentEncoder();
fragment_ = new SrsFragment();
config_ = _srs_config;
}
SrsFragmentedMp4::~SrsFragmentedMp4()
{
srs_freep(enc_);
srs_freep(fw_);
srs_freep(fragment_);
config_ = NULL;
}
srs_error_t SrsFragmentedMp4::initialize(ISrsRequest *r, bool video, int64_t time, SrsMpdWriter *mpd, uint32_t tid)
srs_error_t SrsFragmentedMp4::initialize(ISrsRequest *r, bool video, int64_t time, ISrsMpdWriter *mpd, uint32_t tid)
{
srs_error_t err = srs_success;
@ -91,16 +171,16 @@ srs_error_t SrsFragmentedMp4::initialize(ISrsRequest *r, bool video, int64_t tim
(uint32_t)sequence_number, file_home.c_str(), file_name.c_str());
}
string home = _srs_config->get_dash_path(r->vhost_);
set_path(home + "/" + file_home + "/" + file_name);
string home = config_->get_dash_path(r->vhost_);
fragment_->set_path(home + "/" + file_home + "/" + file_name);
// Set number of the fragment, use in mpd SegmentTemplate@startNumber later.
set_number(sequence_number);
fragment_->set_number(sequence_number);
if ((err = create_dir()) != srs_success) {
if ((err = fragment_->create_dir()) != srs_success) {
return srs_error_wrap(err, "create dir");
}
string path_tmp = tmppath();
string path_tmp = fragment_->tmppath();
if ((err = fw_->open(path_tmp)) != srs_success) {
return srs_error_wrap(err, "Open fmp4 failed, path=%s", path_tmp.c_str());
}
@ -136,7 +216,7 @@ srs_error_t SrsFragmentedMp4::write(SrsMediaPacket *shared_msg, SrsFormat *forma
return err;
}
append(shared_msg->timestamp_);
fragment_->append(shared_msg->timestamp_);
return err;
}
@ -151,13 +231,76 @@ srs_error_t SrsFragmentedMp4::reap(uint64_t &dts)
srs_freep(fw_);
if ((err = rename()) != srs_success) {
if ((err = fragment_->rename()) != srs_success) {
return srs_error_wrap(err, "rename");
}
return err;
}
void SrsFragmentedMp4::set_path(std::string v)
{
fragment_->set_path(v);
}
std::string SrsFragmentedMp4::tmppath()
{
return fragment_->tmppath();
}
srs_error_t SrsFragmentedMp4::rename()
{
return fragment_->rename();
}
void SrsFragmentedMp4::append(int64_t dts)
{
fragment_->append(dts);
}
srs_error_t SrsFragmentedMp4::create_dir()
{
return fragment_->create_dir();
}
void SrsFragmentedMp4::set_number(uint64_t n)
{
fragment_->set_number(n);
}
srs_utime_t SrsFragmentedMp4::duration()
{
return fragment_->duration();
}
srs_error_t SrsFragmentedMp4::unlink_tmpfile()
{
return fragment_->unlink_tmpfile();
}
uint64_t SrsFragmentedMp4::number()
{
return fragment_->number();
}
srs_utime_t SrsFragmentedMp4::get_start_dts()
{
return fragment_->get_start_dts();
}
srs_error_t SrsFragmentedMp4::unlink_file()
{
return fragment_->unlink_file();
}
ISrsMpdWriter::ISrsMpdWriter()
{
}
ISrsMpdWriter::~ISrsMpdWriter()
{
}
SrsMpdWriter::SrsMpdWriter()
{
req_ = NULL;
@ -168,10 +311,15 @@ SrsMpdWriter::SrsMpdWriter()
video_number_ = 0;
audio_number_ = 0;
config_ = _srs_config;
app_factory_ = _srs_app_factory;
}
SrsMpdWriter::~SrsMpdWriter()
{
config_ = NULL;
app_factory_ = NULL;
}
void SrsMpdWriter::dispose()
@ -204,16 +352,16 @@ srs_error_t SrsMpdWriter::on_publish()
{
ISrsRequest *r = req_;
fragment_ = _srs_config->get_dash_fragment(r->vhost_);
update_period_ = _srs_config->get_dash_update_period(r->vhost_);
timeshit_ = _srs_config->get_dash_timeshift(r->vhost_);
home_ = _srs_config->get_dash_path(r->vhost_);
mpd_file_ = _srs_config->get_dash_mpd_file(r->vhost_);
fragment_ = config_->get_dash_fragment(r->vhost_);
update_period_ = config_->get_dash_update_period(r->vhost_);
timeshit_ = config_->get_dash_timeshift(r->vhost_);
home_ = config_->get_dash_path(r->vhost_);
mpd_file_ = config_->get_dash_mpd_file(r->vhost_);
SrsPath path;
string mpd_path = srs_path_build_stream(mpd_file_, req_->vhost_, req_->app_, req_->stream_);
fragment_home_ = path.filepath_dir(mpd_path) + "/" + req_->stream_;
window_size_ = _srs_config->get_dash_window_size(r->vhost_);
window_size_ = config_->get_dash_window_size(r->vhost_);
srs_trace("DASH: Config fragment=%dms, period=%dms, window=%d, timeshit=%dms, home=%s, mpd=%s",
srsu2msi(fragment_), srsu2msi(update_period_), window_size_, srsu2msi(timeshit_), home_.c_str(), mpd_file_.c_str());
@ -225,7 +373,7 @@ void SrsMpdWriter::on_unpublish()
{
}
srs_error_t SrsMpdWriter::write(SrsFormat *format, SrsFragmentWindow *afragments, SrsFragmentWindow *vfragments)
srs_error_t SrsMpdWriter::write(SrsFormat *format, ISrsFragmentWindow *afragments, ISrsFragmentWindow *vfragments)
{
srs_error_t err = srs_success;
@ -305,7 +453,7 @@ srs_error_t SrsMpdWriter::write(SrsFormat *format, SrsFragmentWindow *afragments
ss << " </Period>" << endl;
ss << "</MPD>" << endl;
SrsUniquePtr<SrsFileWriter> fw(new SrsFileWriter());
SrsUniquePtr<ISrsFileWriter> fw(app_factory_->create_file_writer());
string full_path_tmp = full_path + ".tmp";
if ((err = fw->open(full_path_tmp)) != srs_success) {
@ -357,6 +505,14 @@ srs_utime_t SrsMpdWriter::get_availability_start_time()
return availability_start_time_;
}
ISrsDashController::ISrsDashController()
{
}
ISrsDashController::~ISrsDashController()
{
}
SrsDashController::SrsDashController()
{
req_ = NULL;
@ -372,6 +528,9 @@ SrsDashController::SrsDashController()
first_dts_ = -1;
video_reaped_ = false;
fragment_ = 0;
app_factory_ = _srs_app_factory;
config_ = _srs_config;
}
SrsDashController::~SrsDashController()
@ -381,6 +540,9 @@ SrsDashController::~SrsDashController()
srs_freep(acurrent_);
srs_freep(vfragments_);
srs_freep(afragments_);
app_factory_ = NULL;
config_ = NULL;
}
void SrsDashController::dispose()
@ -430,8 +592,8 @@ srs_error_t SrsDashController::on_publish()
ISrsRequest *r = req_;
fragment_ = _srs_config->get_dash_fragment(r->vhost_);
home_ = _srs_config->get_dash_path(r->vhost_);
fragment_ = config_->get_dash_fragment(r->vhost_);
home_ = config_->get_dash_path(r->vhost_);
if ((err = mpd_->on_publish()) != srs_success) {
return srs_error_wrap(err, "mpd");
@ -439,11 +601,11 @@ srs_error_t SrsDashController::on_publish()
srs_freep(vcurrent_);
srs_freep(vfragments_);
vfragments_ = new SrsFragmentWindow();
vfragments_ = app_factory_->create_fragment_window();
srs_freep(acurrent_);
srs_freep(afragments_);
afragments_ = new SrsFragmentWindow();
afragments_ = app_factory_->create_fragment_window();
audio_dts_ = 0;
video_dts_ = 0;
@ -498,7 +660,7 @@ srs_error_t SrsDashController::on_audio(SrsMediaPacket *shared_audio, SrsFormat
audio_dts_ = shared_audio->timestamp_;
if (!acurrent_) {
acurrent_ = new SrsFragmentedMp4();
acurrent_ = app_factory_->create_fragmented_mp4();
if ((err = acurrent_->initialize(req_, false, audio_dts_ * SRS_UTIME_MILLISECONDS, mpd_, audio_track_id_)) != srs_success) {
return srs_error_wrap(err, "Initialize the audio fragment failed");
@ -521,7 +683,7 @@ srs_error_t SrsDashController::on_audio(SrsMediaPacket *shared_audio, SrsFormat
}
afragments_->append(acurrent_);
acurrent_ = new SrsFragmentedMp4();
acurrent_ = app_factory_->create_fragmented_mp4();
if ((err = acurrent_->initialize(req_, false, audio_dts_ * SRS_UTIME_MILLISECONDS, mpd_, audio_track_id_)) != srs_success) {
return srs_error_wrap(err, "Initialize the audio fragment failed");
@ -536,8 +698,8 @@ srs_error_t SrsDashController::on_audio(SrsMediaPacket *shared_audio, SrsFormat
return srs_error_wrap(err, "Write audio to fragment failed");
}
srs_utime_t fragment = _srs_config->get_dash_fragment(req_->vhost_);
int window_size = _srs_config->get_dash_window_size(req_->vhost_);
srs_utime_t fragment = config_->get_dash_fragment(req_->vhost_);
int window_size = config_->get_dash_window_size(req_->vhost_);
int dash_window = 2 * window_size * fragment;
if (afragments_->size() > window_size) {
int w = 0;
@ -550,7 +712,7 @@ srs_error_t SrsDashController::on_audio(SrsMediaPacket *shared_audio, SrsFormat
afragments_->shrink(dash_window);
}
bool dash_cleanup = _srs_config->get_dash_cleanup(req_->vhost_);
bool dash_cleanup = config_->get_dash_cleanup(req_->vhost_);
// remove the m4s file.
afragments_->clear_expired(dash_cleanup);
@ -570,7 +732,7 @@ srs_error_t SrsDashController::on_video(SrsMediaPacket *shared_video, SrsFormat
video_dts_ = shared_video->timestamp_;
if (!vcurrent_) {
vcurrent_ = new SrsFragmentedMp4();
vcurrent_ = app_factory_->create_fragmented_mp4();
if ((err = vcurrent_->initialize(req_, true, video_dts_ * SRS_UTIME_MILLISECONDS, mpd_, video_track_id_)) != srs_success) {
return srs_error_wrap(err, "Initialize the video fragment failed");
@ -594,7 +756,7 @@ srs_error_t SrsDashController::on_video(SrsMediaPacket *shared_video, SrsFormat
video_reaped_ = true;
vfragments_->append(vcurrent_);
vcurrent_ = new SrsFragmentedMp4();
vcurrent_ = app_factory_->create_fragmented_mp4();
if ((err = vcurrent_->initialize(req_, true, video_dts_ * SRS_UTIME_MILLISECONDS, mpd_, video_track_id_)) != srs_success) {
return srs_error_wrap(err, "Initialize the video fragment failed");
@ -609,8 +771,8 @@ srs_error_t SrsDashController::on_video(SrsMediaPacket *shared_video, SrsFormat
return srs_error_wrap(err, "Write video to fragment failed");
}
srs_utime_t fragment = _srs_config->get_dash_fragment(req_->vhost_);
int window_size = _srs_config->get_dash_window_size(req_->vhost_);
srs_utime_t fragment = config_->get_dash_fragment(req_->vhost_);
int window_size = config_->get_dash_window_size(req_->vhost_);
int dash_window = 2 * window_size * fragment;
if (vfragments_->size() > window_size) {
int w = 0;
@ -623,7 +785,7 @@ srs_error_t SrsDashController::on_video(SrsMediaPacket *shared_video, SrsFormat
vfragments_->shrink(dash_window);
}
bool dash_cleanup = _srs_config->get_dash_cleanup(req_->vhost_);
bool dash_cleanup = config_->get_dash_cleanup(req_->vhost_);
// remove the m4s file.
vfragments_->clear_expired(dash_cleanup);
@ -668,7 +830,7 @@ srs_error_t SrsDashController::refresh_init_mp4(SrsMediaPacket *msg, SrsFormat *
path += "/audio-init.mp4";
}
SrsUniquePtr<SrsInitMp4> init_mp4(new SrsInitMp4());
SrsUniquePtr<ISrsInitMp4> init_mp4(app_factory_->create_init_mp4());
init_mp4->set_path(path);
@ -703,11 +865,15 @@ SrsDash::SrsDash()
enabled_ = false;
disposable_ = false;
last_update_time_ = 0;
config_ = _srs_config;
}
SrsDash::~SrsDash()
{
srs_freep(controller_);
config_ = NULL;
}
void SrsDash::dispose()
@ -717,7 +883,7 @@ void SrsDash::dispose()
}
// Ignore when dash_dispose disabled.
srs_utime_t dash_dispose = _srs_config->get_dash_dispose(req_->vhost_);
srs_utime_t dash_dispose = config_->get_dash_dispose(req_->vhost_);
if (!dash_dispose) {
return;
}
@ -737,7 +903,7 @@ srs_error_t SrsDash::cycle()
return err;
}
srs_utime_t dash_dispose = _srs_config->get_dash_dispose(req_->vhost_);
srs_utime_t dash_dispose = config_->get_dash_dispose(req_->vhost_);
if (dash_dispose <= 0) {
return err;
}
@ -760,7 +926,7 @@ srs_error_t SrsDash::cycle()
srs_utime_t SrsDash::cleanup_delay()
{
// We use larger timeout to cleanup the HLS, after disposed it if required.
return _srs_config->get_dash_dispose(req_->vhost_) * 1.1;
return config_->get_dash_dispose(req_->vhost_) * 1.1;
}
// CRITICAL: This method is called AFTER the source has been added to the source pool
@ -769,7 +935,7 @@ srs_utime_t SrsDash::cleanup_delay()
// IMPORTANT: All field initialization in this method MUST NOT cause coroutine context switches.
// This prevents the race condition where multiple coroutines could create duplicate sources
// for the same stream when context switches occurred during initialization.
srs_error_t SrsDash::initialize(SrsOriginHub *h, ISrsRequest *r)
srs_error_t SrsDash::initialize(ISrsOriginHub *h, ISrsRequest *r)
{
srs_error_t err = srs_success;
@ -792,7 +958,7 @@ srs_error_t SrsDash::on_publish()
return err;
}
if (!_srs_config->get_dash_enabled(req_->vhost_)) {
if (!config_->get_dash_enabled(req_->vhost_)) {
return err;
}
enabled_ = true;

View File

@ -16,19 +16,43 @@
class ISrsRequest;
class SrsOriginHub;
class ISrsOriginHub;
class SrsMediaPacket;
class SrsFormat;
class SrsFileWriter;
class ISrsFileWriter;
class SrsMpdWriter;
class ISrsMpdWriter;
class SrsMp4M2tsInitEncoder;
class ISrsMp4M2tsInitEncoder;
class SrsMp4M2tsSegmentEncoder;
class ISrsMp4M2tsSegmentEncoder;
class SrsFragment;
class ISrsFragment;
class ISrsAppFactory;
class ISrsDashController;
class ISrsFragmentWindow;
class ISrsAppConfig;
// The init mp4 fragment interface.
class ISrsInitMp4 : public ISrsFragment
{
public:
ISrsInitMp4();
virtual ~ISrsInitMp4();
public:
// Write the init mp4 file, with the tid(track id).
virtual srs_error_t write(SrsFormat *format, bool video, int tid) = 0;
};
// The init mp4 for FMP4.
class SrsInitMp4 : public SrsFragment
class SrsInitMp4 : public ISrsInitMp4
{
private:
SrsFileWriter *fw_;
SrsMp4M2tsInitEncoder *init_;
ISrsFileWriter *fw_;
ISrsMp4M2tsInitEncoder *init_;
ISrsFragment *fragment_;
public:
SrsInitMp4();
@ -37,14 +61,48 @@ public:
public:
// Write the init mp4 file, with the tid(track id).
virtual srs_error_t write(SrsFormat *format, bool video, int tid);
public:
// ISrsFragment interface implementations - delegate to fragment_
virtual void set_path(std::string v);
virtual std::string tmppath();
virtual srs_error_t rename();
virtual void append(int64_t dts);
virtual srs_error_t create_dir();
virtual void set_number(uint64_t n);
virtual uint64_t number();
virtual srs_utime_t duration();
virtual srs_error_t unlink_tmpfile();
virtual srs_utime_t get_start_dts();
virtual srs_error_t unlink_file();
};
// The FMP4(Fragmented MP4) for DASH streaming.
class SrsFragmentedMp4 : public SrsFragment
class ISrsFragmentedMp4 : public ISrsFragment
{
public:
ISrsFragmentedMp4();
virtual ~ISrsFragmentedMp4();
public:
// Initialize the fragment, create the home dir, open the file.
virtual srs_error_t initialize(ISrsRequest *r, bool video, int64_t time, ISrsMpdWriter *mpd, uint32_t tid) = 0;
// Write media message to fragment.
virtual srs_error_t write(SrsMediaPacket *shared_msg, SrsFormat *format) = 0;
// Reap the fragment, close the fd and rename tmp to official file.
virtual srs_error_t reap(uint64_t &dts) = 0;
};
// The FMP4(Fragmented MP4) for DASH streaming.
class SrsFragmentedMp4 : public ISrsFragmentedMp4
{
private:
SrsFileWriter *fw_;
SrsMp4M2tsSegmentEncoder *enc_;
ISrsAppConfig *config_;
private:
ISrsFileWriter *fw_;
ISrsMp4M2tsSegmentEncoder *enc_;
ISrsFragment *fragment_;
public:
SrsFragmentedMp4();
@ -52,16 +110,60 @@ public:
public:
// Initialize the fragment, create the home dir, open the file.
virtual srs_error_t initialize(ISrsRequest *r, bool video, int64_t time, SrsMpdWriter *mpd, uint32_t tid);
virtual srs_error_t initialize(ISrsRequest *r, bool video, int64_t time, ISrsMpdWriter *mpd, uint32_t tid);
// Write media message to fragment.
virtual srs_error_t write(SrsMediaPacket *shared_msg, SrsFormat *format);
// Reap the fragment, close the fd and rename tmp to official file.
virtual srs_error_t reap(uint64_t &dts);
public:
// ISrsFragment interface implementations - delegate to fragment_
virtual void set_path(std::string v);
virtual std::string tmppath();
virtual srs_error_t rename();
virtual void append(int64_t dts);
virtual srs_error_t create_dir();
virtual void set_number(uint64_t n);
virtual uint64_t number();
virtual srs_utime_t duration();
virtual srs_error_t unlink_tmpfile();
virtual srs_utime_t get_start_dts();
virtual srs_error_t unlink_file();
};
// The writer to write MPD for DASH.
class SrsMpdWriter
class ISrsMpdWriter
{
public:
ISrsMpdWriter();
virtual ~ISrsMpdWriter();
public:
virtual void dispose() = 0;
public:
virtual srs_error_t initialize(ISrsRequest *r) = 0;
virtual srs_error_t on_publish() = 0;
virtual void on_unpublish() = 0;
// Write MPD according to parsed format of stream.
virtual srs_error_t write(SrsFormat *format, ISrsFragmentWindow *afragments, ISrsFragmentWindow *vfragments) = 0;
public:
// Get the fragment relative home and filename.
// The basetime is the absolute time in srs_utime_t, while the sn(sequence number) is basetime/fragment.
virtual srs_error_t get_fragment(bool video, std::string &home, std::string &filename, int64_t time, int64_t &sn) = 0;
// Set the availabilityStartTime once, map the timestamp in media to utc time.
virtual void set_availability_start_time(srs_utime_t t) = 0;
virtual srs_utime_t get_availability_start_time() = 0;
};
// The writer to write MPD for DASH.
class SrsMpdWriter : public ISrsMpdWriter
{
private:
ISrsAppConfig *config_;
ISrsAppFactory *app_factory_;
private:
ISrsRequest *req_;
@ -101,7 +203,7 @@ public:
virtual srs_error_t on_publish();
virtual void on_unpublish();
// Write MPD according to parsed format of stream.
virtual srs_error_t write(SrsFormat *format, SrsFragmentWindow *afragments, SrsFragmentWindow *vfragments);
virtual srs_error_t write(SrsFormat *format, ISrsFragmentWindow *afragments, ISrsFragmentWindow *vfragments);
public:
// Get the fragment relative home and filename.
@ -112,19 +214,41 @@ public:
virtual srs_utime_t get_availability_start_time();
};
// The controller for DASH, control the MPD and FMP4 generating system.
class SrsDashController
// The DASH controller interface.
class ISrsDashController
{
public:
ISrsDashController();
virtual ~ISrsDashController();
public:
virtual void dispose() = 0;
public:
virtual srs_error_t initialize(ISrsRequest *r) = 0;
virtual srs_error_t on_publish() = 0;
virtual void on_unpublish() = 0;
virtual srs_error_t on_audio(SrsMediaPacket *shared_audio, SrsFormat *format) = 0;
virtual srs_error_t on_video(SrsMediaPacket *shared_video, SrsFormat *format) = 0;
};
// The controller for DASH, control the MPD and FMP4 generating system.
class SrsDashController : public ISrsDashController
{
private:
ISrsAppConfig *config_;
ISrsAppFactory *app_factory_;
private:
ISrsRequest *req_;
SrsFormat *format_;
SrsMpdWriter *mpd_;
ISrsMpdWriter *mpd_;
private:
SrsFragmentedMp4 *vcurrent_;
SrsFragmentWindow *vfragments_;
SrsFragmentedMp4 *acurrent_;
SrsFragmentWindow *afragments_;
ISrsFragmentedMp4 *vcurrent_;
ISrsFragmentWindow *vfragments_;
ISrsFragmentedMp4 *acurrent_;
ISrsFragmentWindow *afragments_;
// Current audio dts.
uint64_t audio_dts_;
// Current video dts.
@ -175,7 +299,7 @@ public:
virtual srs_utime_t cleanup_delay() = 0;
public:
virtual srs_error_t initialize(SrsOriginHub *h, ISrsRequest *r) = 0;
virtual srs_error_t initialize(ISrsOriginHub *h, ISrsRequest *r) = 0;
virtual srs_error_t on_publish() = 0;
virtual srs_error_t on_audio(SrsMediaPacket *shared_audio, SrsFormat *format) = 0;
virtual srs_error_t on_video(SrsMediaPacket *shared_video, SrsFormat *format) = 0;
@ -185,6 +309,9 @@ public:
// The MPEG-DASH encoder, transmux RTMP to DASH.
class SrsDash : public ISrsDash
{
private:
ISrsAppConfig *config_;
private:
bool enabled_;
bool disposable_;
@ -192,8 +319,8 @@ private:
private:
ISrsRequest *req_;
SrsOriginHub *hub_;
SrsDashController *controller_;
ISrsOriginHub *hub_;
ISrsDashController *controller_;
public:
SrsDash();
@ -206,7 +333,7 @@ public:
public:
// Initalize the encoder.
virtual srs_error_t initialize(SrsOriginHub *h, ISrsRequest *r);
virtual srs_error_t initialize(ISrsOriginHub *h, ISrsRequest *r);
// When stream start publishing.
virtual srs_error_t on_publish();
// When got an shared audio message.

View File

@ -8,7 +8,9 @@
#include <srs_app_caster_flv.hpp>
#include <srs_app_config.hpp>
#include <srs_app_dash.hpp>
#include <srs_app_dvr.hpp>
#include <srs_app_fragment.hpp>
#include <srs_app_gb28181.hpp>
#include <srs_app_rtmp_conn.hpp>
#include <srs_app_rtmp_source.hpp>
@ -140,6 +142,21 @@ ISrsGbSession *SrsAppFactory::create_gb_session()
}
#endif
ISrsInitMp4 *SrsAppFactory::create_init_mp4()
{
return new SrsInitMp4();
}
ISrsFragmentWindow *SrsAppFactory::create_fragment_window()
{
return new SrsFragmentWindow();
}
ISrsFragmentedMp4 *SrsAppFactory::create_fragmented_mp4()
{
return new SrsFragmentedMp4();
}
SrsFinalFactory::SrsFinalFactory()
{
}

View File

@ -31,6 +31,10 @@ class ISrsMp4Encoder;
class ISrsDvrSegmenter;
class ISrsGbMediaTcpConn;
class ISrsGbSession;
class ISrsFragment;
class ISrsInitMp4;
class ISrsFragmentWindow;
class ISrsFragmentedMp4;
// The factory to create app objects.
class ISrsAppFactory
@ -63,6 +67,9 @@ public:
virtual ISrsGbMediaTcpConn *create_gb_media_tcp_conn() = 0;
virtual ISrsGbSession *create_gb_session() = 0;
#endif
virtual ISrsInitMp4 *create_init_mp4() = 0;
virtual ISrsFragmentWindow *create_fragment_window() = 0;
virtual ISrsFragmentedMp4 *create_fragmented_mp4() = 0;
};
// The factory to create app objects.
@ -96,6 +103,9 @@ public:
virtual ISrsGbMediaTcpConn *create_gb_media_tcp_conn();
virtual ISrsGbSession *create_gb_session();
#endif
virtual ISrsInitMp4 *create_init_mp4();
virtual ISrsFragmentWindow *create_fragment_window();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
};
extern ISrsAppFactory *_srs_app_factory;

View File

@ -14,6 +14,14 @@
#include <unistd.h>
using namespace std;
ISrsFragment::ISrsFragment()
{
}
ISrsFragment::~ISrsFragment()
{
}
SrsFragment::SrsFragment()
{
dur_ = 0;
@ -155,22 +163,30 @@ uint64_t SrsFragment::number()
return number_;
}
ISrsFragmentWindow::ISrsFragmentWindow()
{
}
ISrsFragmentWindow::~ISrsFragmentWindow()
{
}
SrsFragmentWindow::SrsFragmentWindow()
{
}
SrsFragmentWindow::~SrsFragmentWindow()
{
vector<SrsFragment *>::iterator it;
vector<ISrsFragment *>::iterator it;
for (it = fragments_.begin(); it != fragments_.end(); ++it) {
SrsFragment *fragment = *it;
ISrsFragment *fragment = *it;
srs_freep(fragment);
}
fragments_.clear();
for (it = expired_fragments_.begin(); it != expired_fragments_.end(); ++it) {
SrsFragment *fragment = *it;
ISrsFragment *fragment = *it;
srs_freep(fragment);
}
expired_fragments_.clear();
@ -180,10 +196,10 @@ void SrsFragmentWindow::dispose()
{
srs_error_t err = srs_success;
std::vector<SrsFragment *>::iterator it;
std::vector<ISrsFragment *>::iterator it;
for (it = fragments_.begin(); it != fragments_.end(); ++it) {
SrsFragment *fragment = *it;
ISrsFragment *fragment = *it;
if ((err = fragment->unlink_file()) != srs_success) {
srs_warn("Unlink ts failed %s", srs_error_desc(err).c_str());
srs_freep(err);
@ -193,7 +209,7 @@ void SrsFragmentWindow::dispose()
fragments_.clear();
for (it = expired_fragments_.begin(); it != expired_fragments_.end(); ++it) {
SrsFragment *fragment = *it;
ISrsFragment *fragment = *it;
if ((err = fragment->unlink_file()) != srs_success) {
srs_warn("Unlink ts failed %s", srs_error_desc(err).c_str());
srs_freep(err);
@ -203,7 +219,7 @@ void SrsFragmentWindow::dispose()
expired_fragments_.clear();
}
void SrsFragmentWindow::append(SrsFragment *fragment)
void SrsFragmentWindow::append(ISrsFragment *fragment)
{
fragments_.push_back(fragment);
}
@ -215,7 +231,7 @@ void SrsFragmentWindow::shrink(srs_utime_t window)
int remove_index = -1;
for (int i = (int)fragments_.size() - 1; i >= 0; i--) {
SrsFragment *fragment = fragments_[i];
ISrsFragment *fragment = fragments_[i];
duration += fragment->duration();
if (duration > window) {
@ -225,7 +241,7 @@ void SrsFragmentWindow::shrink(srs_utime_t window)
}
for (int i = 0; i < remove_index && !fragments_.empty(); i++) {
SrsFragment *fragment = *fragments_.begin();
ISrsFragment *fragment = *fragments_.begin();
fragments_.erase(fragments_.begin());
expired_fragments_.push_back(fragment);
}
@ -235,10 +251,10 @@ void SrsFragmentWindow::clear_expired(bool delete_files)
{
srs_error_t err = srs_success;
std::vector<SrsFragment *>::iterator it;
std::vector<ISrsFragment *>::iterator it;
for (it = expired_fragments_.begin(); it != expired_fragments_.end(); ++it) {
SrsFragment *fragment = *it;
ISrsFragment *fragment = *it;
if (delete_files && (err = fragment->unlink_file()) != srs_success) {
srs_warn("Unlink ts failed, %s", srs_error_desc(err).c_str());
srs_freep(err);
@ -253,10 +269,10 @@ srs_utime_t SrsFragmentWindow::max_duration()
{
srs_utime_t v = 0;
std::vector<SrsFragment *>::iterator it;
std::vector<ISrsFragment *>::iterator it;
for (it = fragments_.begin(); it != fragments_.end(); ++it) {
SrsFragment *fragment = *it;
ISrsFragment *fragment = *it;
v = srs_max(v, fragment->duration());
}
@ -268,7 +284,7 @@ bool SrsFragmentWindow::empty()
return fragments_.empty();
}
SrsFragment *SrsFragmentWindow::first()
ISrsFragment *SrsFragmentWindow::first()
{
return fragments_.at(0);
}
@ -278,7 +294,7 @@ int SrsFragmentWindow::size()
return (int)fragments_.size();
}
SrsFragment *SrsFragmentWindow::at(int index)
ISrsFragment *SrsFragmentWindow::at(int index)
{
return fragments_.at(index);
}

View File

@ -12,9 +12,44 @@
#include <string>
#include <vector>
// Forward declarations
class SrsFormat;
// The fragment interface.
class ISrsFragment
{
public:
ISrsFragment();
virtual ~ISrsFragment();
public:
// Set the full path of fragment.
virtual void set_path(std::string v) = 0;
// Get the temporary path for file.
virtual std::string tmppath() = 0;
// Rename the temp file to final file.
virtual srs_error_t rename() = 0;
// Append a frame with dts into fragment.
virtual void append(int64_t dts) = 0;
// Create the dir for file recursively.
virtual srs_error_t create_dir() = 0;
// Set the number of this fragment.
virtual void set_number(uint64_t n) = 0;
// Get the number of this fragment.
virtual uint64_t number() = 0;
// Get the duration of fragment in srs_utime_t.
virtual srs_utime_t duration() = 0;
// Unlink the temporary file.
virtual srs_error_t unlink_tmpfile() = 0;
// Get the start dts of fragment.
virtual srs_utime_t get_start_dts() = 0;
// Unlink the fragment, to delete the file.
virtual srs_error_t unlink_file() = 0;
};
// Represent a fragment, such as HLS segment, DVR segment or DASH segment.
// It's a media file, for example FLV or MP4, with duration.
class SrsFragment
class SrsFragment : public ISrsFragment
{
private:
// The duration in srs_utime_t.
@ -68,13 +103,39 @@ public:
virtual uint64_t number();
};
// The fragment window interface.
class ISrsFragmentWindow
{
public:
ISrsFragmentWindow();
virtual ~ISrsFragmentWindow();
public:
// Dispose all fragments, delete the files.
virtual void dispose() = 0;
// Append a new fragment, which is ready to delivery to client.
virtual void append(ISrsFragment *fragment) = 0;
// Shrink the window, push the expired fragment to a queue.
virtual void shrink(srs_utime_t window) = 0;
// Clear the expired fragments.
virtual void clear_expired(bool delete_files) = 0;
// Get the max duration in srs_utime_t of all fragments.
virtual srs_utime_t max_duration() = 0;
public:
virtual bool empty() = 0;
virtual ISrsFragment *first() = 0;
virtual int size() = 0;
virtual ISrsFragment *at(int index) = 0;
};
// The fragment window manage a series of fragment.
class SrsFragmentWindow
class SrsFragmentWindow : public ISrsFragmentWindow
{
private:
std::vector<SrsFragment *> fragments_;
std::vector<ISrsFragment *> fragments_;
// The expired fragments, need to be free in future.
std::vector<SrsFragment *> expired_fragments_;
std::vector<ISrsFragment *> expired_fragments_;
public:
SrsFragmentWindow();
@ -84,7 +145,7 @@ public:
// Dispose all fragments, delete the files.
virtual void dispose();
// Append a new fragment, which is ready to delivery to client.
virtual void append(SrsFragment *fragment);
virtual void append(ISrsFragment *fragment);
// Shrink the window, push the expired fragment to a queue.
virtual void shrink(srs_utime_t window);
// Clear the expired fragments.
@ -94,9 +155,9 @@ public:
public:
virtual bool empty();
virtual SrsFragment *first();
virtual ISrsFragment *first();
virtual int size();
virtual SrsFragment *at(int index);
virtual ISrsFragment *at(int index);
};
#endif

View File

@ -328,7 +328,7 @@ SrsGbListener::SrsGbListener()
media_listener_ = new SrsTcpListener(this);
config_ = _srs_config;
api_server_owner_ = _srs_server;
api_server_owner_ = NULL;
gb_manager_ = _srs_gb_manager;
app_factory_ = _srs_app_factory;
}
@ -348,6 +348,12 @@ srs_error_t SrsGbListener::initialize(SrsConfDirective *conf)
{
srs_error_t err = srs_success;
// We should initialize the owner in initialize, because the SRS server
// is not ready in the constructor.
if (!api_server_owner_) {
api_server_owner_ = _srs_server;
}
srs_freep(conf_);
conf_ = conf->copy();
@ -381,6 +387,10 @@ srs_error_t SrsGbListener::listen_api()
srs_error_t err = srs_success;
ISrsHttpServeMux *mux = api_server_owner_->api_server();
if (!mux) {
return err;
}
if ((err = mux->handle("/gb/v1/publish/", new SrsGoApiGbPublish(conf_))) != srs_success) {
return srs_error_wrap(err, "handle publish");
}

View File

@ -29,15 +29,27 @@ using namespace std;
// To limit user to use too long password, to cause unknown issue.
#define SRS_ICE_PWD_MAX 32
SrsGoApiRtcPlay::SrsGoApiRtcPlay(SrsServer *server)
SrsGoApiRtcPlay::SrsGoApiRtcPlay(ISrsRtcApiServer *server)
{
server_ = server;
security_ = new SrsSecurity();
config_ = _srs_config;
stat_ = _srs_stat;
rtc_sources_ = _srs_rtc_sources;
live_sources_ = _srs_sources;
hooks_ = _srs_hooks;
}
SrsGoApiRtcPlay::~SrsGoApiRtcPlay()
{
srs_freep(security_);
config_ = NULL;
stat_ = NULL;
rtc_sources_ = NULL;
live_sources_ = NULL;
hooks_ = NULL;
}
// Request:
@ -135,7 +147,7 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
ruc.req_->app_, ruc.req_->stream_, ruc.req_->port_, ruc.req_->param_);
// discovery vhost, resolve the vhost from config
SrsConfDirective *parsed_vhost = _srs_config->get_vhost(ruc.req_->vhost_);
SrsConfDirective *parsed_vhost = config_->get_vhost(ruc.req_->vhost_);
if (parsed_vhost) {
ruc.req_->vhost_ = parsed_vhost->arg0();
}
@ -162,7 +174,7 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
ruc.dtls_ = (dtls != "false");
if (srtp.empty()) {
ruc.srtp_ = _srs_config->get_rtc_server_encrypt();
ruc.srtp_ = config_->get_rtc_server_encrypt();
} else {
ruc.srtp_ = (srtp != "false");
}
@ -178,9 +190,9 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
}
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
res->set("server", SrsJsonAny::str(_srs_stat->server_id().c_str()));
res->set("service", SrsJsonAny::str(_srs_stat->service_id().c_str()));
res->set("pid", SrsJsonAny::str(_srs_stat->service_pid().c_str()));
res->set("server", SrsJsonAny::str(stat_->server_id().c_str()));
res->set("service", SrsJsonAny::str(stat_->service_id().c_str()));
res->set("pid", SrsJsonAny::str(stat_->service_pid().c_str()));
// TODO: add candidates in response json?
res->set("sdp", SrsJsonAny::str(ruc.local_sdp_str_.c_str()));
@ -200,13 +212,13 @@ srs_error_t SrsGoApiRtcPlay::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa
SrsSdp local_sdp;
// Config for SDP and session.
local_sdp.session_config_.dtls_role_ = _srs_config->get_rtc_dtls_role(ruc->req_->vhost_);
local_sdp.session_config_.dtls_version_ = _srs_config->get_rtc_dtls_version(ruc->req_->vhost_);
local_sdp.session_config_.dtls_role_ = config_->get_rtc_dtls_role(ruc->req_->vhost_);
local_sdp.session_config_.dtls_version_ = config_->get_rtc_dtls_version(ruc->req_->vhost_);
// Whether enabled.
bool server_enabled = _srs_config->get_rtc_server_enabled();
bool rtc_enabled = _srs_config->get_rtc_enabled(ruc->req_->vhost_);
bool edge = _srs_config->get_vhost_is_edge(ruc->req_->vhost_);
bool server_enabled = config_->get_rtc_server_enabled();
bool rtc_enabled = config_->get_rtc_enabled(ruc->req_->vhost_);
bool edge = config_->get_vhost_is_edge(ruc->req_->vhost_);
if (rtc_enabled && edge) {
rtc_enabled = false;
@ -224,19 +236,19 @@ srs_error_t SrsGoApiRtcPlay::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa
// Whether RTC stream is active.
bool is_rtc_stream_active = false;
if (true) {
SrsSharedPtr<SrsRtcSource> source = _srs_rtc_sources->fetch(ruc->req_);
SrsSharedPtr<SrsRtcSource> source = rtc_sources_->fetch(ruc->req_);
is_rtc_stream_active = (source.get() && !source->can_publish());
}
// For RTMP to RTC, fail if disabled and RTMP is active, see https://github.com/ossrs/srs/issues/2728
bool rtmp_to_rtc = _srs_config->get_rtc_from_rtmp(ruc->req_->vhost_);
bool rtmp_to_rtc = config_->get_rtc_from_rtmp(ruc->req_->vhost_);
if (rtmp_to_rtc && edge) {
rtmp_to_rtc = false;
srs_warn("disable RTMP to WebRTC for edge vhost=%s", ruc->req_->vhost_.c_str());
}
if (!is_rtc_stream_active && !rtmp_to_rtc) {
SrsSharedPtr<SrsLiveSource> live_source = _srs_sources->fetch(ruc->req_);
SrsSharedPtr<SrsLiveSource> live_source = live_sources_->fetch(ruc->req_);
if (live_source.get() && !live_source->inactive()) {
return srs_error_new(ERROR_RTC_DISABLED, "Disabled rtmp_to_rtc of %s, see #2728", ruc->req_->vhost_.c_str());
}
@ -310,7 +322,7 @@ srs_error_t SrsGoApiRtcPlay::http_hooks_on_play(ISrsRequest *req)
{
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;
}
@ -320,7 +332,7 @@ srs_error_t SrsGoApiRtcPlay::http_hooks_on_play(ISrsRequest *req)
vector<string> hooks;
if (true) {
SrsConfDirective *conf = _srs_config->get_vhost_on_play(req->vhost_);
SrsConfDirective *conf = config_->get_vhost_on_play(req->vhost_);
if (!conf) {
return err;
@ -331,7 +343,7 @@ srs_error_t SrsGoApiRtcPlay::http_hooks_on_play(ISrsRequest *req)
for (int i = 0; i < (int)hooks.size(); i++) {
std::string url = hooks.at(i);
if ((err = _srs_hooks->on_play(url, req)) != srs_success) {
if ((err = hooks_->on_play(url, req)) != srs_success) {
return srs_error_wrap(err, "on_play %s", url.c_str());
}
}
@ -339,15 +351,23 @@ srs_error_t SrsGoApiRtcPlay::http_hooks_on_play(ISrsRequest *req)
return err;
}
SrsGoApiRtcPublish::SrsGoApiRtcPublish(SrsServer *server)
SrsGoApiRtcPublish::SrsGoApiRtcPublish(ISrsRtcApiServer *server)
{
server_ = server;
security_ = new SrsSecurity();
config_ = _srs_config;
stat_ = _srs_stat;
hooks_ = _srs_hooks;
}
SrsGoApiRtcPublish::~SrsGoApiRtcPublish()
{
srs_freep(security_);
config_ = NULL;
stat_ = NULL;
hooks_ = NULL;
}
// Request:
@ -446,7 +466,7 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter *w, ISrsHtt
ruc.req_->param_ = srs_strings_trim_start(ruc.req_->param_ + "&upstream=rtc", "&");
// discovery vhost, resolve the vhost from config
SrsConfDirective *parsed_vhost = _srs_config->get_vhost(ruc.req_->vhost_);
SrsConfDirective *parsed_vhost = config_->get_vhost(ruc.req_->vhost_);
if (parsed_vhost) {
ruc.req_->vhost_ = parsed_vhost->arg0();
}
@ -478,9 +498,9 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter *w, ISrsHtt
}
res->set("code", SrsJsonAny::integer(ERROR_SUCCESS));
res->set("server", SrsJsonAny::str(_srs_stat->server_id().c_str()));
res->set("service", SrsJsonAny::str(_srs_stat->service_id().c_str()));
res->set("pid", SrsJsonAny::str(_srs_stat->service_pid().c_str()));
res->set("server", SrsJsonAny::str(stat_->server_id().c_str()));
res->set("service", SrsJsonAny::str(stat_->service_id().c_str()));
res->set("pid", SrsJsonAny::str(stat_->service_pid().c_str()));
// TODO: add candidates in response json?
res->set("sdp", SrsJsonAny::str(ruc.local_sdp_str_.c_str()));
@ -501,13 +521,13 @@ srs_error_t SrsGoApiRtcPublish::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
// TODO: FIXME: move to create_session.
// Config for SDP and session.
local_sdp.session_config_.dtls_role_ = _srs_config->get_rtc_dtls_role(ruc->req_->vhost_);
local_sdp.session_config_.dtls_version_ = _srs_config->get_rtc_dtls_version(ruc->req_->vhost_);
local_sdp.session_config_.dtls_role_ = config_->get_rtc_dtls_role(ruc->req_->vhost_);
local_sdp.session_config_.dtls_version_ = config_->get_rtc_dtls_version(ruc->req_->vhost_);
// Whether enabled.
bool server_enabled = _srs_config->get_rtc_server_enabled();
bool rtc_enabled = _srs_config->get_rtc_enabled(ruc->req_->vhost_);
bool edge = _srs_config->get_vhost_is_edge(ruc->req_->vhost_);
bool server_enabled = config_->get_rtc_server_enabled();
bool rtc_enabled = config_->get_rtc_enabled(ruc->req_->vhost_);
bool edge = config_->get_vhost_is_edge(ruc->req_->vhost_);
if (rtc_enabled && edge) {
rtc_enabled = false;
@ -592,7 +612,7 @@ srs_error_t SrsGoApiRtcPublish::http_hooks_on_publish(ISrsRequest *req)
{
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;
}
@ -602,7 +622,7 @@ srs_error_t SrsGoApiRtcPublish::http_hooks_on_publish(ISrsRequest *req)
vector<string> hooks;
if (true) {
SrsConfDirective *conf = _srs_config->get_vhost_on_publish(req->vhost_);
SrsConfDirective *conf = config_->get_vhost_on_publish(req->vhost_);
if (!conf) {
return err;
}
@ -611,7 +631,7 @@ srs_error_t SrsGoApiRtcPublish::http_hooks_on_publish(ISrsRequest *req)
for (int i = 0; i < (int)hooks.size(); i++) {
std::string url = hooks.at(i);
if ((err = _srs_hooks->on_publish(url, req)) != srs_success) {
if ((err = hooks_->on_publish(url, req)) != srs_success) {
return srs_error_wrap(err, "rtmp on_publish %s", url.c_str());
}
}
@ -619,17 +639,21 @@ srs_error_t SrsGoApiRtcPublish::http_hooks_on_publish(ISrsRequest *req)
return err;
}
SrsGoApiRtcWhip::SrsGoApiRtcWhip(SrsServer *server)
SrsGoApiRtcWhip::SrsGoApiRtcWhip(ISrsRtcApiServer *server)
{
server_ = server;
publish_ = new SrsGoApiRtcPublish(server);
play_ = new SrsGoApiRtcPlay(server);
config_ = _srs_config;
}
SrsGoApiRtcWhip::~SrsGoApiRtcWhip()
{
srs_freep(publish_);
srs_freep(play_);
config_ = NULL;
}
srs_error_t SrsGoApiRtcWhip::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessage *r)
@ -740,7 +764,7 @@ srs_error_t SrsGoApiRtcWhip::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
}
// discovery vhost, resolve the vhost from config
SrsConfDirective *parsed_vhost = _srs_config->get_vhost(ruc->req_->vhost_);
SrsConfDirective *parsed_vhost = config_->get_vhost(ruc->req_->vhost_);
if (parsed_vhost) {
ruc->req_->vhost_ = parsed_vhost->arg0();
}
@ -761,7 +785,7 @@ srs_error_t SrsGoApiRtcWhip::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
// For client to specifies whether encrypt by SRTP.
ruc->dtls_ = (dtls != "false");
if (srtp.empty()) {
ruc->srtp_ = _srs_config->get_rtc_server_encrypt();
ruc->srtp_ = config_->get_rtc_server_encrypt();
} else {
ruc->srtp_ = (srtp != "false");
}
@ -780,7 +804,7 @@ srs_error_t SrsGoApiRtcWhip::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
return err;
}
SrsGoApiRtcNACK::SrsGoApiRtcNACK(SrsServer *server)
SrsGoApiRtcNACK::SrsGoApiRtcNACK(ISrsRtcApiServer *server)
{
server_ = server;
}

View File

@ -15,15 +15,29 @@ class SrsServer;
class ISrsRequest;
class SrsSdp;
class SrsRtcUserConfig;
class ISrsRtcApiServer;
class ISrsSecurity;
class ISrsAppConfig;
class ISrsStatistic;
class ISrsRtcSourceManager;
class ISrsLiveSourceManager;
class ISrsHttpHooks;
class SrsGoApiRtcPlay : public ISrsHttpHandler
{
private:
SrsServer *server_;
SrsSecurity *security_;
ISrsAppConfig *config_;
ISrsStatistic *stat_;
ISrsRtcSourceManager *rtc_sources_;
ISrsLiveSourceManager *live_sources_;
ISrsHttpHooks *hooks_;
private:
ISrsRtcApiServer *server_;
ISrsSecurity *security_;
public:
SrsGoApiRtcPlay(SrsServer *server);
SrsGoApiRtcPlay(ISrsRtcApiServer *server);
virtual ~SrsGoApiRtcPlay();
public:
@ -45,11 +59,16 @@ private:
class SrsGoApiRtcPublish : public ISrsHttpHandler
{
private:
SrsServer *server_;
SrsSecurity *security_;
ISrsAppConfig *config_;
ISrsStatistic *stat_;
ISrsHttpHooks *hooks_;
private:
ISrsRtcApiServer *server_;
ISrsSecurity *security_;
public:
SrsGoApiRtcPublish(SrsServer *server);
SrsGoApiRtcPublish(ISrsRtcApiServer *server);
virtual ~SrsGoApiRtcPublish();
public:
@ -72,12 +91,15 @@ private:
class SrsGoApiRtcWhip : public ISrsHttpHandler
{
private:
SrsServer *server_;
ISrsAppConfig *config_;
private:
ISrsRtcApiServer *server_;
SrsGoApiRtcPublish *publish_;
SrsGoApiRtcPlay *play_;
public:
SrsGoApiRtcWhip(SrsServer *server);
SrsGoApiRtcWhip(ISrsRtcApiServer *server);
virtual ~SrsGoApiRtcWhip();
public:
@ -90,10 +112,10 @@ private:
class SrsGoApiRtcNACK : public ISrsHttpHandler
{
private:
SrsServer *server_;
ISrsRtcApiServer *server_;
public:
SrsGoApiRtcNACK(SrsServer *server);
SrsGoApiRtcNACK(ISrsRtcApiServer *server);
virtual ~SrsGoApiRtcNACK();
public:

View File

@ -158,6 +158,14 @@ ISrsApiServerOwner::~ISrsApiServerOwner()
{
}
ISrsRtcApiServer::ISrsRtcApiServer()
{
}
ISrsRtcApiServer::~ISrsRtcApiServer()
{
}
SrsServer::SrsServer()
{
signal_reload_ = false;

View File

@ -95,6 +95,18 @@ public:
virtual ISrsHttpServeMux *api_server() = 0;
};
// The RTC API server owner interface.
class ISrsRtcApiServer
{
public:
ISrsRtcApiServer();
virtual ~ISrsRtcApiServer();
public:
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession) = 0;
virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag) = 0;
};
// SrsServer is the main server class of SRS (Simple Realtime Server) that provides comprehensive
// streaming media server functionality. It serves as the central orchestrator for all streaming
// protocols and services in a single-threaded, coroutine-based architecture.
@ -105,7 +117,8 @@ class SrsServer : public ISrsReloadHandler, // Reload framework for permormance
public ISrsSrtClientHandler,
public ISrsUdpMuxHandler,
public ISrsSignalHandler,
public ISrsApiServerOwner
public ISrsApiServerOwner,
public ISrsRtcApiServer
{
private:
ISrsAppConfig *config_;

View File

@ -6990,6 +6990,14 @@ SrsMp4ObjectType SrsMp4Encoder::get_audio_object_type()
}
}
ISrsMp4M2tsInitEncoder::ISrsMp4M2tsInitEncoder()
{
}
ISrsMp4M2tsInitEncoder::~ISrsMp4M2tsInitEncoder()
{
}
SrsMp4M2tsInitEncoder::SrsMp4M2tsInitEncoder()
{
writer_ = NULL;
@ -7580,6 +7588,14 @@ srs_error_t SrsMp4M2tsInitEncoder::config_sample_description_encryption(SrsMp4Sa
return err;
}
ISrsMp4M2tsSegmentEncoder::ISrsMp4M2tsSegmentEncoder()
{
}
ISrsMp4M2tsSegmentEncoder::~ISrsMp4M2tsSegmentEncoder()
{
}
SrsMp4M2tsSegmentEncoder::SrsMp4M2tsSegmentEncoder()
{
writer_ = NULL;

View File

@ -2721,9 +2721,23 @@ private:
virtual SrsMp4ObjectType get_audio_object_type();
};
// The fMP4 init encoder interface.
class ISrsMp4M2tsInitEncoder
{
public:
ISrsMp4M2tsInitEncoder();
virtual ~ISrsMp4M2tsInitEncoder();
public:
// Initialize the encoder with a writer w.
virtual srs_error_t initialize(ISrsWriter *w) = 0;
// Write the sequence header.
virtual srs_error_t write(SrsFormat *format, bool video, int tid) = 0;
};
// A fMP4 encoder, to write the init.mp4 with sequence header.
// TODO: What the M2ts short for?
class SrsMp4M2tsInitEncoder
class SrsMp4M2tsInitEncoder : public ISrsMp4M2tsInitEncoder
{
private:
ISrsWriter *writer_;
@ -2781,10 +2795,34 @@ private:
virtual srs_error_t config_sample_description_encryption(SrsMp4SampleEntry *box);
};
// The fMP4 segment encoder interface.
class ISrsMp4M2tsSegmentEncoder
{
public:
ISrsMp4M2tsSegmentEncoder();
virtual ~ISrsMp4M2tsSegmentEncoder();
public:
// Initialize the encoder with a writer w.
virtual srs_error_t initialize(ISrsWriter *w, uint32_t sequence, srs_utime_t basetime, uint32_t tid) = 0;
// Cache a sample.
// @param ht, The sample handler type, audio/soun or video/vide.
// @param ft, The frame type. For video, it's SrsVideoAvcFrameType.
// @param dts The output dts in milliseconds.
// @param pts The output pts in milliseconds.
// @param sample The output payload, user must free it.
// @param nb_sample The output size of payload.
// @remark All samples are RAW AAC/AVC data, because sequence header is writen to init.mp4.
virtual srs_error_t write_sample(SrsMp4HandlerType ht, uint16_t ft,
uint32_t dts, uint32_t pts, uint8_t *sample, uint32_t nb_sample) = 0;
// Flush the encoder, to write the moof and mdat.
virtual srs_error_t flush(uint64_t &dts) = 0;
};
// A fMP4 encoder, to cache segments then flush to disk, because the fMP4 should write
// trun box before mdat.
// TODO: fmp4 support package more than one tracks.
class SrsMp4M2tsSegmentEncoder
class SrsMp4M2tsSegmentEncoder : public ISrsMp4M2tsSegmentEncoder
{
private:
ISrsWriter *writer_;

View File

@ -3219,6 +3219,21 @@ ISrsGbSession *MockDvrAppFactory::create_gb_session()
return NULL;
}
ISrsInitMp4 *MockDvrAppFactory::create_init_mp4()
{
return NULL;
}
ISrsFragmentWindow *MockDvrAppFactory::create_fragment_window()
{
return NULL;
}
ISrsFragmentedMp4 *MockDvrAppFactory::create_fragmented_mp4()
{
return NULL;
}
VOID TEST(DvrSegmenterTest, OpenTypicalScenario)
{
srs_error_t err;

View File

@ -637,6 +637,9 @@ public:
virtual ISrsDvrSegmenter *create_dvr_mp4_segmenter();
virtual ISrsGbMediaTcpConn *create_gb_media_tcp_conn();
virtual ISrsGbSession *create_gb_session();
virtual ISrsInitMp4 *create_init_mp4();
virtual ISrsFragmentWindow *create_fragment_window();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
};
// Mock ISrsDvrSegmenter for testing SrsDvrPlan

View File

@ -2378,6 +2378,21 @@ ISrsGbSession *MockAppFactoryForGbPublish::create_gb_session()
return session;
}
ISrsInitMp4 *MockAppFactoryForGbPublish::create_init_mp4()
{
return NULL;
}
ISrsFragmentWindow *MockAppFactoryForGbPublish::create_fragment_window()
{
return NULL;
}
ISrsFragmentedMp4 *MockAppFactoryForGbPublish::create_fragmented_mp4()
{
return NULL;
}
void MockAppFactoryForGbPublish::reset()
{
srs_freep(mock_gb_session_);
@ -3553,7 +3568,8 @@ srs_error_t MockProtocolReadWriterForTcpNetwork::read_fully(void *buf, size_t si
memcpy(buf, read_data_.data() + read_pos_, size);
read_pos_ += size;
if (nread) *nread = size;
if (nread)
*nread = size;
recv_bytes_ += size;
return srs_success;
@ -4154,8 +4170,8 @@ VOID TEST(RtcTcpConnTest, ReadPacketSuccess)
// Prepare test packet data: 2-byte length header + packet body
// Length = 100 bytes (0x0064 in big-endian)
std::string test_data;
test_data.push_back(0x00); // Length high byte
test_data.push_back(0x64); // Length low byte (100 in decimal)
test_data.push_back(0x00); // Length high byte
test_data.push_back(0x64); // Length low byte (100 in decimal)
// Add 100 bytes of packet data
for (int i = 0; i < 100; i++) {
@ -4244,8 +4260,8 @@ VOID TEST(RtcTcpConnTest, OnTcpPktRouting)
// RTP packets have version bits (10) in first byte, and payload type < 64
char rtp_pkt[100];
memset(rtp_pkt, 0, sizeof(rtp_pkt));
rtp_pkt[0] = 0x80; // Version 2 (10xxxxxx)
rtp_pkt[1] = 0x08; // Payload type 8 (PCMA)
rtp_pkt[0] = 0x80; // Version 2 (10xxxxxx)
rtp_pkt[1] = 0x08; // Payload type 8 (PCMA)
HELPER_EXPECT_SUCCESS(tcp_conn->on_tcp_pkt(rtp_pkt, 100));
@ -4253,8 +4269,8 @@ VOID TEST(RtcTcpConnTest, OnTcpPktRouting)
// RTCP packets have version bits (10) and payload type in range [64, 95]
char rtcp_pkt[100];
memset(rtcp_pkt, 0, sizeof(rtcp_pkt));
rtcp_pkt[0] = 0x80; // Version 2 (10xxxxxx)
rtcp_pkt[1] = 0xC8; // Payload type 200 (SR - Sender Report)
rtcp_pkt[0] = 0x80; // Version 2 (10xxxxxx)
rtcp_pkt[1] = 0xC8; // Payload type 200 (SR - Sender Report)
HELPER_EXPECT_SUCCESS(tcp_conn->on_tcp_pkt(rtcp_pkt, 100));
@ -4262,8 +4278,8 @@ VOID TEST(RtcTcpConnTest, OnTcpPktRouting)
// DTLS packets have content type in range [20, 63]
char dtls_pkt[100];
memset(dtls_pkt, 0, sizeof(dtls_pkt));
dtls_pkt[0] = 0x16; // Content type: handshake (22)
dtls_pkt[1] = 0xFE; // DTLS version 1.0 (0xFEFF)
dtls_pkt[0] = 0x16; // Content type: handshake (22)
dtls_pkt[1] = 0xFE; // DTLS version 1.0 (0xFEFF)
dtls_pkt[2] = 0xFF;
HELPER_EXPECT_SUCCESS(tcp_conn->on_tcp_pkt(dtls_pkt, 100));

View File

@ -600,6 +600,9 @@ public:
virtual SrsDvrMp4Segmenter *create_dvr_mp4_segmenter();
virtual ISrsGbMediaTcpConn *create_gb_media_tcp_conn();
virtual ISrsGbSession *create_gb_session();
virtual ISrsInitMp4 *create_init_mp4();
virtual ISrsFragmentWindow *create_fragment_window();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
void reset();
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,452 @@
//
// Copyright (c) 2013-2025 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#ifndef SRS_UTEST_APP15_HPP
#define SRS_UTEST_APP15_HPP
/*
#include <srs_utest_app15.hpp>
*/
#include <srs_utest.hpp>
#include <srs_app_dash.hpp>
#include <srs_app_factory.hpp>
#include <srs_app_fragment.hpp>
#include <srs_app_rtc_api.hpp>
#include <srs_app_server.hpp>
#include <srs_app_statistic.hpp>
#include <srs_kernel_file.hpp>
#include <srs_kernel_mp4.hpp>
#include <srs_protocol_http_conn.hpp>
#include <srs_protocol_http_stack.hpp>
#include <srs_protocol_sdp.hpp>
#include <srs_utest_app10.hpp>
#include <srs_utest_app11.hpp>
#include <srs_utest_app6.hpp>
// Mock ISrsMpdWriter for testing MPD fragment generation
class MockMpdWriter : public ISrsMpdWriter
{
public:
std::string file_home_;
std::string file_name_;
int64_t sequence_number_;
bool get_fragment_called_;
public:
MockMpdWriter();
virtual ~MockMpdWriter();
public:
virtual srs_error_t get_fragment(bool video, std::string &home, std::string &filename, int64_t time, int64_t &sn);
// Stub implementations for other ISrsMpdWriter methods
virtual void dispose() {}
virtual srs_error_t initialize(ISrsRequest *r) { return srs_success; }
virtual srs_error_t on_publish() { return srs_success; }
virtual void on_unpublish() {}
virtual srs_error_t write(SrsFormat *format, ISrsFragmentWindow *afragments, ISrsFragmentWindow *vfragments) { return srs_success; }
virtual void set_availability_start_time(srs_utime_t t) {}
virtual srs_utime_t get_availability_start_time() { return 0; }
};
// Mock ISrsMp4M2tsSegmentEncoder for testing MP4 encoding
class MockMp4SegmentEncoder : public ISrsMp4M2tsSegmentEncoder
{
public:
bool initialize_called_;
bool write_sample_called_;
bool flush_called_;
uint32_t last_sequence_;
srs_utime_t last_basetime_;
uint32_t last_tid_;
SrsMp4HandlerType last_handler_type_;
uint32_t last_dts_;
uint32_t last_pts_;
uint32_t last_sample_size_;
public:
MockMp4SegmentEncoder();
virtual ~MockMp4SegmentEncoder();
public:
virtual srs_error_t initialize(ISrsWriter *w, uint32_t sequence, srs_utime_t basetime, uint32_t tid);
virtual srs_error_t write_sample(SrsMp4HandlerType ht, uint16_t ft, uint32_t dts, uint32_t pts, uint8_t *sample, uint32_t nb_sample);
virtual srs_error_t flush(uint64_t &dts);
};
// Mock ISrsFragment for testing SrsInitMp4 delegation
class MockFragment : public ISrsFragment
{
public:
std::string path_;
std::string tmppath_;
uint64_t number_;
srs_utime_t duration_;
srs_utime_t start_dts_;
bool set_path_called_;
bool tmppath_called_;
bool rename_called_;
bool append_called_;
bool create_dir_called_;
bool set_number_called_;
bool number_called_;
bool duration_called_;
bool unlink_tmpfile_called_;
bool get_start_dts_called_;
bool unlink_file_called_;
int64_t append_dts_;
public:
MockFragment();
virtual ~MockFragment();
public:
virtual void set_path(std::string v);
virtual std::string tmppath();
virtual srs_error_t rename();
virtual void append(int64_t dts);
virtual srs_error_t create_dir();
virtual void set_number(uint64_t n);
virtual uint64_t number();
virtual srs_utime_t duration();
virtual srs_error_t unlink_tmpfile();
virtual srs_utime_t get_start_dts();
virtual srs_error_t unlink_file();
};
// Mock ISrsFragmentWindow for testing SrsDashController
class MockFragmentWindow : public ISrsFragmentWindow
{
public:
bool dispose_called_;
bool append_called_;
bool shrink_called_;
bool clear_expired_called_;
public:
MockFragmentWindow();
virtual ~MockFragmentWindow();
public:
virtual void dispose();
virtual void append(ISrsFragment *fragment);
virtual void shrink(srs_utime_t window);
virtual void clear_expired(bool delete_files);
virtual srs_utime_t max_duration();
virtual bool empty();
virtual ISrsFragment *first();
virtual int size();
virtual ISrsFragment *at(int index);
};
// Mock ISrsFragmentedMp4 for testing SrsDashController
class MockFragmentedMp4 : public ISrsFragmentedMp4
{
public:
bool initialize_called_;
bool write_called_;
bool reap_called_;
bool unlink_tmpfile_called_;
srs_error_t unlink_tmpfile_error_;
srs_utime_t duration_;
public:
MockFragmentedMp4();
virtual ~MockFragmentedMp4();
public:
virtual srs_error_t initialize(ISrsRequest *r, bool video, int64_t time, ISrsMpdWriter *mpd, uint32_t tid);
virtual srs_error_t write(SrsMediaPacket *shared_msg, SrsFormat *format);
virtual srs_error_t reap(uint64_t &dts);
public:
// ISrsFragment interface implementations
virtual void set_path(std::string v);
virtual std::string tmppath();
virtual srs_error_t rename();
virtual void append(int64_t dts);
virtual srs_error_t create_dir();
virtual void set_number(uint64_t n);
virtual uint64_t number();
virtual srs_utime_t duration();
virtual srs_error_t unlink_tmpfile();
virtual srs_utime_t get_start_dts();
virtual srs_error_t unlink_file();
};
// Forward declaration
class MockDashAppFactory;
// Mock ISrsInitMp4 for testing SrsDashController refresh_init_mp4
class MockInitMp4 : public ISrsInitMp4
{
public:
bool set_path_called_;
bool write_called_;
bool rename_called_;
std::string path_;
bool video_;
int tid_;
MockDashAppFactory *factory_; // Reference to factory to copy state on destruction
public:
MockInitMp4(MockDashAppFactory *factory);
virtual ~MockInitMp4();
public:
virtual srs_error_t write(SrsFormat *format, bool video, int tid);
public:
// ISrsFragment interface implementations
virtual void set_path(std::string v);
virtual std::string tmppath();
virtual srs_error_t rename();
virtual void append(int64_t dts);
virtual srs_error_t create_dir();
virtual void set_number(uint64_t n);
virtual uint64_t number();
virtual srs_utime_t duration();
virtual srs_error_t unlink_tmpfile();
virtual srs_utime_t get_start_dts();
virtual srs_error_t unlink_file();
};
// Mock ISrsAppFactory for testing SrsDashController
class MockDashAppFactory : public SrsAppFactory
{
public:
// Track the last created init mp4 state (before it's deleted)
bool last_set_path_called_;
bool last_write_called_;
bool last_rename_called_;
std::string last_path_;
bool last_video_;
int last_tid_;
public:
MockDashAppFactory();
virtual ~MockDashAppFactory();
public:
virtual ISrsInitMp4 *create_init_mp4();
};
// Mock ISrsDashController for testing SrsDash lifecycle
class MockDashController : public ISrsDashController
{
public:
bool initialize_called_;
bool on_publish_called_;
bool on_unpublish_called_;
bool dispose_called_;
public:
MockDashController();
virtual ~MockDashController();
public:
virtual void dispose();
virtual srs_error_t initialize(ISrsRequest *r);
virtual srs_error_t on_publish();
virtual void on_unpublish();
virtual srs_error_t on_audio(SrsMediaPacket *shared_audio, SrsFormat *format);
virtual srs_error_t on_video(SrsMediaPacket *shared_video, SrsFormat *format);
};
// Mock SrsRtcConnection for testing NACK API
class MockRtcConnectionForNackApi
{
public:
int simulate_nack_drop_value_;
bool simulate_nack_drop_called_;
public:
MockRtcConnectionForNackApi();
~MockRtcConnectionForNackApi();
public:
void simulate_nack_drop(int nn);
};
// Mock ISrsRtcApiServer for testing RTC API
class MockRtcApiServer : public ISrsRtcApiServer
{
public:
bool create_session_called_;
std::string session_id_;
std::string local_sdp_str_;
MockRtcConnectionForNackApi *mock_connection_;
std::string find_username_;
public:
MockRtcApiServer();
virtual ~MockRtcApiServer();
public:
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession);
virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
};
// Mock ISrsStatistic for testing RTC API
class MockStatisticForRtcApi : public ISrsStatistic
{
public:
std::string server_id_;
std::string service_id_;
std::string service_pid_;
public:
MockStatisticForRtcApi();
virtual ~MockStatisticForRtcApi();
public:
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);
virtual srs_error_t on_video_info(ISrsRequest *req, SrsVideoCodecId vcodec, int avc_profile, int avc_level, int width, int height);
virtual srs_error_t on_audio_info(ISrsRequest *req, SrsAudioCodecId acodec, SrsAudioSampleRate asample_rate,
SrsAudioChannels asound_type, SrsAacObjectType aac_object);
virtual void on_stream_publish(ISrsRequest *req, std::string publisher_id);
virtual void on_stream_close(ISrsRequest *req);
virtual void kbps_add_delta(std::string id, ISrsKbpsDelta *delta);
virtual void kbps_sample();
virtual srs_error_t on_video_frames(ISrsRequest *req, int nb_frames);
virtual std::string server_id();
virtual std::string service_id();
virtual std::string service_pid();
virtual SrsStatisticVhost *find_vhost_by_id(std::string vid);
virtual SrsStatisticStream *find_stream(std::string sid);
virtual SrsStatisticClient *find_client(std::string client_id);
virtual srs_error_t dumps_vhosts(SrsJsonArray *arr);
virtual srs_error_t dumps_streams(SrsJsonArray *arr, int start, int count);
virtual srs_error_t dumps_clients(SrsJsonArray *arr, int start, int count);
virtual srs_error_t dumps_metrics(int64_t &send_bytes, int64_t &recv_bytes, int64_t &nstreams, int64_t &nclients, int64_t &total_nclients, int64_t &nerrs);
};
// Mock ISrsHttpMessage for testing RTC API
class MockHttpMessageForRtcApi : public SrsHttpMessage
{
public:
MockHttpConn *mock_conn_;
std::string body_content_;
std::map<std::string, std::string> query_params_;
uint8_t method_;
public:
MockHttpMessageForRtcApi();
virtual ~MockHttpMessageForRtcApi();
public:
virtual srs_error_t body_read_all(std::string &body);
virtual std::string query_get(std::string key);
virtual uint8_t method();
void set_method(uint8_t method);
};
// Mock ISrsAppConfig for testing SrsGoApiRtcPlay::serve_http()
class MockAppConfigForRtcPlay : public MockAppConfig
{
public:
std::string dtls_role_;
std::string dtls_version_;
bool rtc_server_enabled_;
bool rtc_enabled_;
bool vhost_is_edge_;
bool rtc_from_rtmp_;
bool http_hooks_enabled_;
SrsConfDirective *on_play_directive_;
public:
MockAppConfigForRtcPlay();
virtual ~MockAppConfigForRtcPlay();
public:
virtual std::string get_rtc_dtls_role(std::string vhost);
virtual std::string get_rtc_dtls_version(std::string vhost);
virtual bool get_rtc_server_enabled();
virtual bool get_rtc_enabled(std::string vhost);
virtual bool get_vhost_is_edge(std::string vhost);
virtual bool get_rtc_from_rtmp(std::string vhost);
virtual bool get_vhost_http_hooks_enabled(std::string vhost);
virtual SrsConfDirective *get_vhost_on_play(std::string vhost);
};
// Mock ISrsHttpHooks for testing SrsGoApiRtcPlay::serve_http()
class MockHttpHooksForRtcPlay : public ISrsHttpHooks
{
public:
int on_play_count_;
std::vector<std::pair<std::string, ISrsRequest *> > on_play_calls_;
public:
MockHttpHooksForRtcPlay();
virtual ~MockHttpHooksForRtcPlay();
public:
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);
};
// Mock ISrsSecurity for testing SrsGoApiRtcPlay::serve_http()
class MockSecurityForRtcPlay : public ISrsSecurity
{
public:
srs_error_t check_error_;
int check_count_;
public:
MockSecurityForRtcPlay();
virtual ~MockSecurityForRtcPlay();
public:
virtual srs_error_t check(SrsRtmpConnType type, std::string ip, ISrsRequest *req);
};
// Mock SrsRtcConnection for testing SrsGoApiRtcPlay::serve_http()
class MockRtcConnectionForPlay
{
public:
std::string username_;
std::string token_;
public:
MockRtcConnectionForPlay();
~MockRtcConnectionForPlay();
public:
std::string username();
std::string token();
};
// Mock ISrsRtcApiServer for testing SrsGoApiRtcPlay::serve_http()
class MockRtcApiServerForPlay : public ISrsRtcApiServer
{
public:
bool create_session_called_;
MockRtcConnectionForPlay *mock_connection_;
public:
MockRtcApiServerForPlay();
virtual ~MockRtcApiServerForPlay();
public:
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession);
virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
};
#endif

View File

@ -2181,6 +2181,8 @@ MockAppConfig::MockAppConfig()
rtc_twcc_enabled_ = true;
srt_enabled_ = false;
rtc_to_rtmp_ = false;
dash_dispose_ = 0;
dash_enabled_ = false;
}
MockAppConfig::~MockAppConfig()
@ -2284,6 +2286,16 @@ bool MockAppConfig::get_rtc_stun_strict_check(std::string vhost)
return false; // Default to non-strict mode
}
std::string MockAppConfig::get_rtc_dtls_role(std::string vhost)
{
return "passive"; // Default DTLS role
}
std::string MockAppConfig::get_rtc_dtls_version(std::string vhost)
{
return "auto"; // Default DTLS version
}
SrsConfDirective *MockAppConfig::get_vhost_on_hls(std::string vhost)
{
return NULL;

View File

@ -242,6 +242,8 @@ public:
bool rtc_twcc_enabled_;
bool srt_enabled_;
bool rtc_to_rtmp_;
srs_utime_t dash_dispose_;
bool dash_enabled_;
public:
MockAppConfig();
@ -291,6 +293,7 @@ public:
virtual std::string get_rtc_server_protocol() { return "udp"; }
virtual std::vector<std::string> get_rtc_server_listens() { return std::vector<std::string>(); }
virtual int get_rtc_server_reuseport() { return 1; }
virtual bool get_rtc_server_encrypt() { return false; }
virtual bool get_rtsp_server_enabled() { return false; }
virtual std::vector<std::string> get_rtsp_server_listens() { return std::vector<std::string>(); }
virtual std::vector<std::string> get_srt_listens() { return std::vector<std::string>(); }
@ -357,6 +360,8 @@ public:
virtual bool get_rtc_to_rtmp(std::string vhost);
virtual srs_utime_t get_rtc_stun_timeout(std::string vhost);
virtual bool get_rtc_stun_strict_check(std::string vhost);
virtual std::string get_rtc_dtls_role(std::string vhost);
virtual std::string get_rtc_dtls_version(std::string vhost);
virtual SrsConfDirective *get_vhost_on_hls(std::string vhost);
virtual SrsConfDirective *get_vhost_on_hls_notify(std::string vhost);
// HLS methods
@ -420,6 +425,17 @@ public:
virtual std::string get_vhost_edge_protocol(std::string vhost) { return "rtmp"; }
virtual bool get_vhost_edge_follow_client(std::string vhost) { return false; }
virtual std::string get_vhost_edge_transform_vhost(std::string vhost) { return ""; }
// DASH methods
virtual bool get_dash_enabled(std::string vhost) { return dash_enabled_; }
virtual bool get_dash_enabled(SrsConfDirective *vhost) { return dash_enabled_; }
virtual srs_utime_t get_dash_fragment(std::string vhost) { return 30 * SRS_UTIME_SECONDS; }
virtual srs_utime_t get_dash_update_period(std::string vhost) { return 30 * SRS_UTIME_SECONDS; }
virtual srs_utime_t get_dash_timeshift(std::string vhost) { return 300 * SRS_UTIME_SECONDS; }
virtual std::string get_dash_path(std::string vhost) { return "./[vhost]/[app]/[stream]/"; }
virtual std::string get_dash_mpd_file(std::string vhost) { return "[stream].mpd"; }
virtual int get_dash_window_size(std::string vhost) { return 10; }
virtual bool get_dash_cleanup(std::string vhost) { return true; }
virtual srs_utime_t get_dash_dispose(std::string vhost) { return dash_dispose_; }
void set_http_hooks_enabled(bool enabled);
void set_on_stop_urls(const std::vector<std::string> &urls);
void clear_on_stop_directive();

View File

@ -242,6 +242,7 @@ MockAppFactory::MockAppFactory()
real_writer_ = NULL;
real_file_ = NULL;
real_reader_ = NULL;
real_fragmented_mp4_ = NULL;
create_file_writer_count_ = 0;
create_file_reader_count_ = 0;
}
@ -253,6 +254,8 @@ MockAppFactory::~MockAppFactory()
// real_file_ is also not owned - it's part of real_writer_
// real_reader_ is owned by this factory
srs_freep(real_reader_);
// Note: real_fragmented_mp4_ is NOT owned by this factory - it's freed by the caller
real_fragmented_mp4_ = NULL;
}
ISrsFileWriter *MockAppFactory::create_file_writer()
@ -271,6 +274,15 @@ ISrsFileReader *MockAppFactory::create_file_reader()
return real_reader_;
}
ISrsFragmentedMp4 *MockAppFactory::create_fragmented_mp4()
{
// Return the mock fragmented mp4 that was set up for testing
// The caller takes ownership of this object
ISrsFragmentedMp4 *result = real_fragmented_mp4_;
real_fragmented_mp4_ = NULL; // Clear reference after returning
return result;
}
void MockAppFactory::reset()
{
create_file_writer_count_ = 0;
@ -280,6 +292,8 @@ void MockAppFactory::reset()
srs_freep(real_file_);
real_file_ = NULL;
// Note: Don't free real_reader_ here as it may still be in use
// Note: real_fragmented_mp4_ should be NULL after being returned by create_fragmented_mp4()
real_fragmented_mp4_ = NULL;
}
// Mock HLS muxer implementation

View File

@ -101,6 +101,7 @@ public:
MockSrsFileWriter *real_writer_;
MockSrsFile *real_file_;
MockSrsFileReader *real_reader_;
ISrsFragmentedMp4 *real_fragmented_mp4_;
int create_file_writer_count_;
int create_file_reader_count_;
@ -109,6 +110,7 @@ public:
virtual ~MockAppFactory();
virtual ISrsFileWriter *create_file_writer();
virtual ISrsFileReader *create_file_reader();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
void reset();
};

View File

@ -1406,7 +1406,7 @@ MockDashForOriginHub::~MockDashForOriginHub()
srs_freep(initialize_error_);
}
srs_error_t MockDashForOriginHub::initialize(SrsOriginHub *h, ISrsRequest *r)
srs_error_t MockDashForOriginHub::initialize(ISrsOriginHub *h, ISrsRequest *r)
{
initialize_count_++;
return srs_error_copy(initialize_error_);

View File

@ -115,7 +115,7 @@ public:
public:
MockDashForOriginHub();
virtual ~MockDashForOriginHub();
virtual srs_error_t initialize(SrsOriginHub *h, ISrsRequest *r);
virtual srs_error_t initialize(ISrsOriginHub *h, ISrsRequest *r);
virtual srs_error_t on_publish();
virtual srs_error_t on_audio(SrsMediaPacket *shared_audio, SrsFormat *format);
virtual srs_error_t on_video(SrsMediaPacket *shared_video, SrsFormat *format);