AI: Improve the coverage for app hls module.
This commit is contained in:
parent
df3c776580
commit
3f876d324e
5
trunk/configure
vendored
5
trunk/configure
vendored
|
|
@ -377,12 +377,13 @@ fi
|
|||
if [[ $SRS_UTEST == YES ]]; then
|
||||
MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_kernel" "srs_utest_core"
|
||||
"srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_avc" "srs_utest_reload"
|
||||
"srs_utest_mp4" "srs_utest_service" "srs_utest_app" "srs_utest_app2" "srs_utest_rtc" "srs_utest_config2"
|
||||
"srs_utest_mp4" "srs_utest_service" "srs_utest_app_rtc2rtmp" "srs_utest_rtc" "srs_utest_config2"
|
||||
"srs_utest_config3" "srs_utest_config4" "srs_utest_protocol" "srs_utest_protocol2" "srs_utest_kernel2"
|
||||
"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_coworkers" "srs_utest_pithy_print" "srs_utest_kernel3" "srs_utest_protocol4"
|
||||
"srs_utest_protocol3" "srs_utest_app3" "srs_utest_app4" "srs_utest_app5" "srs_utest_app6" "srs_utest_app7" "srs_utest_app_rtc2rtmp")
|
||||
"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")
|
||||
# Always include SRT utest
|
||||
MODULE_FILES+=("srs_utest_srt")
|
||||
if [[ $SRS_GB28181 == YES ]]; then
|
||||
|
|
|
|||
|
|
@ -296,6 +296,36 @@ 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 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;
|
||||
virtual bool get_hls_enabled(SrsConfDirective *vhost) = 0;
|
||||
virtual bool get_hls_use_fmp4(std::string vhost) = 0;
|
||||
virtual std::string get_hls_entry_prefix(std::string vhost) = 0;
|
||||
virtual std::string get_hls_path(std::string vhost) = 0;
|
||||
virtual std::string get_hls_m3u8_file(std::string vhost) = 0;
|
||||
virtual std::string get_hls_ts_file(std::string vhost) = 0;
|
||||
virtual std::string get_hls_fmp4_file(std::string vhost) = 0;
|
||||
virtual std::string get_hls_init_file(std::string vhost) = 0;
|
||||
virtual bool get_hls_ts_floor(std::string vhost) = 0;
|
||||
virtual srs_utime_t get_hls_fragment(std::string vhost) = 0;
|
||||
virtual double get_hls_td_ratio(std::string vhost) = 0;
|
||||
virtual double get_hls_aof_ratio(std::string vhost) = 0;
|
||||
virtual srs_utime_t get_hls_window(std::string vhost) = 0;
|
||||
virtual std::string get_hls_on_error(std::string vhost) = 0;
|
||||
virtual bool get_hls_cleanup(std::string vhost) = 0;
|
||||
virtual srs_utime_t get_hls_dispose(std::string vhost) = 0;
|
||||
virtual bool get_hls_wait_keyframe(std::string vhost) = 0;
|
||||
virtual bool get_hls_keys(std::string vhost) = 0;
|
||||
virtual int get_hls_fragments_per_key(std::string vhost) = 0;
|
||||
virtual std::string get_hls_key_file(std::string vhost) = 0;
|
||||
virtual std::string get_hls_key_file_path(std::string vhost) = 0;
|
||||
virtual std::string get_hls_key_url(std::string vhost) = 0;
|
||||
virtual int get_vhost_hls_nb_notify(std::string vhost) = 0;
|
||||
virtual bool get_vhost_hls_dts_directly(std::string vhost) = 0;
|
||||
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;
|
||||
};
|
||||
|
||||
// The config service provider.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,38 @@
|
|||
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_app_st.hpp>
|
||||
#include <srs_kernel_file.hpp>
|
||||
#include <srs_protocol_st.hpp>
|
||||
#include <srs_kernel_ts.hpp>
|
||||
#include <srs_kernel_utility.hpp>
|
||||
|
||||
SrsAppFactory::SrsAppFactory()
|
||||
{
|
||||
}
|
||||
|
||||
SrsAppFactory::~SrsAppFactory()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsFileWriter *SrsAppFactory::create_file_writer()
|
||||
{
|
||||
return new SrsFileWriter();
|
||||
}
|
||||
|
||||
ISrsFileWriter *SrsAppFactory::create_enc_file_writer()
|
||||
{
|
||||
return new SrsEncFileWriter();
|
||||
}
|
||||
|
||||
ISrsFileReader *SrsAppFactory::create_file_reader()
|
||||
{
|
||||
return new SrsFileReader();
|
||||
}
|
||||
|
||||
SrsPath *SrsAppFactory::create_path()
|
||||
{
|
||||
return new SrsPath();
|
||||
}
|
||||
|
||||
SrsFinalFactory::SrsFinalFactory()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,26 @@
|
|||
|
||||
#include <srs_kernel_factory.hpp>
|
||||
|
||||
class ISrsFileWriter;
|
||||
class ISrsFileReader;
|
||||
class SrsPath;
|
||||
|
||||
// The factory to create app objects.
|
||||
class SrsAppFactory
|
||||
{
|
||||
public:
|
||||
SrsAppFactory();
|
||||
virtual ~SrsAppFactory();
|
||||
|
||||
public:
|
||||
virtual ISrsFileWriter *create_file_writer();
|
||||
virtual ISrsFileWriter *create_enc_file_writer();
|
||||
virtual ISrsFileReader *create_file_reader();
|
||||
virtual SrsPath *create_path();
|
||||
};
|
||||
|
||||
extern SrsAppFactory *_srs_app_factory;
|
||||
|
||||
// The factory to create kernel objects.
|
||||
class SrsFinalFactory : public ISrsKernelFactory
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ using namespace std;
|
|||
|
||||
#include <openssl/rand.h>
|
||||
#include <srs_app_config.hpp>
|
||||
#include <srs_app_factory.hpp>
|
||||
#include <srs_app_http_hooks.hpp>
|
||||
#include <srs_app_rtmp_source.hpp>
|
||||
#include <srs_app_utility.hpp>
|
||||
|
|
@ -44,7 +45,7 @@ using namespace std;
|
|||
// reset the piece id when deviation overflow this.
|
||||
#define SRS_JUMP_WHEN_PIECE_DEVIATION 20
|
||||
|
||||
SrsHlsSegment::SrsHlsSegment(SrsTsContext *c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter *w)
|
||||
SrsHlsSegment::SrsHlsSegment(SrsTsContext *c, SrsAudioCodecId ac, SrsVideoCodecId vc, ISrsFileWriter *w)
|
||||
{
|
||||
sequence_no_ = 0;
|
||||
writer_ = w;
|
||||
|
|
@ -60,7 +61,8 @@ void SrsHlsSegment::config_cipher(unsigned char *key, unsigned char *iv)
|
|||
{
|
||||
memcpy(this->iv_, iv, 16);
|
||||
|
||||
SrsEncFileWriter *fw = (SrsEncFileWriter *)writer_;
|
||||
SrsEncFileWriter *fw = dynamic_cast<SrsEncFileWriter *>(writer_);
|
||||
srs_assert(fw);
|
||||
fw->config_cipher(key, iv);
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +77,7 @@ srs_error_t SrsHlsSegment::rename()
|
|||
return SrsFragment::rename();
|
||||
}
|
||||
|
||||
SrsInitMp4Segment::SrsInitMp4Segment(SrsFileWriter *fw)
|
||||
SrsInitMp4Segment::SrsInitMp4Segment(ISrsFileWriter *fw)
|
||||
{
|
||||
fw_ = fw;
|
||||
const_iv_size_ = 0;
|
||||
|
|
@ -165,7 +167,7 @@ srs_error_t SrsInitMp4Segment::init_encoder()
|
|||
return err;
|
||||
}
|
||||
|
||||
SrsHlsM4sSegment::SrsHlsM4sSegment(SrsFileWriter *fw)
|
||||
SrsHlsM4sSegment::SrsHlsM4sSegment(ISrsFileWriter *fw)
|
||||
{
|
||||
fw_ = fw;
|
||||
}
|
||||
|
|
@ -264,18 +266,24 @@ SrsDvrAsyncCallOnHls::SrsDvrAsyncCallOnHls(SrsContextId c, ISrsRequest *r, strin
|
|||
m3u8_url_ = mu;
|
||||
seq_no_ = s;
|
||||
duration_ = d;
|
||||
|
||||
config_ = _srs_config;
|
||||
hooks_ = _srs_hooks;
|
||||
}
|
||||
|
||||
SrsDvrAsyncCallOnHls::~SrsDvrAsyncCallOnHls()
|
||||
{
|
||||
srs_freep(req_);
|
||||
|
||||
config_ = NULL;
|
||||
hooks_ = NULL;
|
||||
}
|
||||
|
||||
srs_error_t SrsDvrAsyncCallOnHls::call()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -285,7 +293,7 @@ srs_error_t SrsDvrAsyncCallOnHls::call()
|
|||
vector<string> hooks;
|
||||
|
||||
if (true) {
|
||||
SrsConfDirective *conf = _srs_config->get_vhost_on_hls(req_->vhost_);
|
||||
SrsConfDirective *conf = config_->get_vhost_on_hls(req_->vhost_);
|
||||
|
||||
if (!conf) {
|
||||
return err;
|
||||
|
|
@ -296,7 +304,7 @@ srs_error_t SrsDvrAsyncCallOnHls::call()
|
|||
|
||||
for (int i = 0; i < (int)hooks.size(); i++) {
|
||||
std::string url = hooks.at(i);
|
||||
if ((err = _srs_hooks->on_hls(cid_, url, req_, path_, ts_url_, m3u8_, m3u8_url_, seq_no_, duration_)) != srs_success) {
|
||||
if ((err = hooks_->on_hls(cid_, url, req_, path_, ts_url_, m3u8_, m3u8_url_, seq_no_, duration_)) != srs_success) {
|
||||
return srs_error_wrap(err, "callback on_hls %s", url.c_str());
|
||||
}
|
||||
}
|
||||
|
|
@ -314,18 +322,24 @@ SrsDvrAsyncCallOnHlsNotify::SrsDvrAsyncCallOnHlsNotify(SrsContextId c, ISrsReque
|
|||
cid_ = c;
|
||||
req_ = r->copy();
|
||||
ts_url_ = u;
|
||||
|
||||
config_ = _srs_config;
|
||||
hooks_ = _srs_hooks;
|
||||
}
|
||||
|
||||
SrsDvrAsyncCallOnHlsNotify::~SrsDvrAsyncCallOnHlsNotify()
|
||||
{
|
||||
srs_freep(req_);
|
||||
|
||||
config_ = NULL;
|
||||
hooks_ = NULL;
|
||||
}
|
||||
|
||||
srs_error_t SrsDvrAsyncCallOnHlsNotify::call()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -335,7 +349,7 @@ srs_error_t SrsDvrAsyncCallOnHlsNotify::call()
|
|||
vector<string> hooks;
|
||||
|
||||
if (true) {
|
||||
SrsConfDirective *conf = _srs_config->get_vhost_on_hls_notify(req_->vhost_);
|
||||
SrsConfDirective *conf = config_->get_vhost_on_hls_notify(req_->vhost_);
|
||||
|
||||
if (!conf) {
|
||||
return err;
|
||||
|
|
@ -344,10 +358,10 @@ srs_error_t SrsDvrAsyncCallOnHlsNotify::call()
|
|||
hooks = conf->args_;
|
||||
}
|
||||
|
||||
int nb_notify = _srs_config->get_vhost_hls_nb_notify(req_->vhost_);
|
||||
int nb_notify = config_->get_vhost_hls_nb_notify(req_->vhost_);
|
||||
for (int i = 0; i < (int)hooks.size(); i++) {
|
||||
std::string url = hooks.at(i);
|
||||
if ((err = _srs_hooks->on_hls_notify(cid_, url, req_, ts_url_, nb_notify)) != srs_success) {
|
||||
if ((err = hooks_->on_hls_notify(cid_, url, req_, ts_url_, nb_notify)) != srs_success) {
|
||||
return srs_error_wrap(err, "callback on_hls_notify %s", url.c_str());
|
||||
}
|
||||
}
|
||||
|
|
@ -388,6 +402,9 @@ SrsHlsFmp4Muxer::SrsHlsFmp4Muxer()
|
|||
|
||||
memset(key_, 0, 16);
|
||||
memset(iv_, 0, 16);
|
||||
|
||||
config_ = _srs_config;
|
||||
app_factory_ = _srs_app_factory;
|
||||
}
|
||||
|
||||
SrsHlsFmp4Muxer::~SrsHlsFmp4Muxer()
|
||||
|
|
@ -397,6 +414,9 @@ SrsHlsFmp4Muxer::~SrsHlsFmp4Muxer()
|
|||
srs_freep(req_);
|
||||
srs_freep(async_);
|
||||
srs_freep(writer_);
|
||||
|
||||
config_ = NULL;
|
||||
app_factory_ = NULL;
|
||||
}
|
||||
|
||||
void SrsHlsFmp4Muxer::dispose()
|
||||
|
|
@ -413,8 +433,12 @@ void SrsHlsFmp4Muxer::dispose()
|
|||
srs_freep(current_);
|
||||
}
|
||||
|
||||
if (unlink(m3u8_.c_str()) < 0) {
|
||||
srs_warn("dispose unlink path failed. file=%s", m3u8_.c_str());
|
||||
SrsUniquePtr<SrsPath> path(app_factory_->create_path());
|
||||
if (path->exists(m3u8_)) {
|
||||
if ((err = path->unlink(m3u8_)) != srs_success) {
|
||||
srs_warn("dispose: ignore remove m3u8 failed, %s", srs_error_desc(err).c_str());
|
||||
srs_freep(err);
|
||||
}
|
||||
}
|
||||
|
||||
srs_trace("gracefully dispose hls %s", req_ ? req_->get_stream_url().c_str() : "");
|
||||
|
|
@ -499,10 +523,10 @@ srs_error_t SrsHlsFmp4Muxer::write_init_mp4(SrsFormat *format, bool has_video, b
|
|||
std::string app = req_->app_;
|
||||
|
||||
// Get init.mp4 file template from configuration
|
||||
std::string init_file = _srs_config->get_hls_init_file(vhost);
|
||||
std::string init_file = config_->get_hls_init_file(vhost);
|
||||
init_file = srs_path_build_stream(init_file, vhost, app, stream);
|
||||
|
||||
std::string hls_path = _srs_config->get_hls_path(vhost);
|
||||
std::string hls_path = config_->get_hls_path(vhost);
|
||||
std::string path = hls_path + "/" + init_file;
|
||||
|
||||
// Create directory for the init file
|
||||
|
|
@ -649,28 +673,28 @@ srs_error_t SrsHlsFmp4Muxer::update_config(ISrsRequest *r)
|
|||
std::string stream = req_->stream_;
|
||||
std::string app = req_->app_;
|
||||
|
||||
hls_fragment_ = _srs_config->get_hls_fragment(vhost);
|
||||
double hls_td_ratio = _srs_config->get_hls_td_ratio(vhost);
|
||||
hls_window_ = _srs_config->get_hls_window(vhost);
|
||||
hls_fragment_ = config_->get_hls_fragment(vhost);
|
||||
double hls_td_ratio = config_->get_hls_td_ratio(vhost);
|
||||
hls_window_ = config_->get_hls_window(vhost);
|
||||
|
||||
// get the hls m3u8 ts list entry prefix config
|
||||
hls_entry_prefix_ = _srs_config->get_hls_entry_prefix(vhost);
|
||||
hls_entry_prefix_ = config_->get_hls_entry_prefix(vhost);
|
||||
// get the hls path config
|
||||
hls_path_ = _srs_config->get_hls_path(vhost);
|
||||
m3u8_url_ = _srs_config->get_hls_m3u8_file(vhost);
|
||||
hls_m4s_file_ = _srs_config->get_hls_fmp4_file(vhost);
|
||||
hls_cleanup_ = _srs_config->get_hls_cleanup(vhost);
|
||||
hls_wait_keyframe_ = _srs_config->get_hls_wait_keyframe(vhost);
|
||||
hls_path_ = config_->get_hls_path(vhost);
|
||||
m3u8_url_ = config_->get_hls_m3u8_file(vhost);
|
||||
hls_m4s_file_ = config_->get_hls_fmp4_file(vhost);
|
||||
hls_cleanup_ = config_->get_hls_cleanup(vhost);
|
||||
hls_wait_keyframe_ = config_->get_hls_wait_keyframe(vhost);
|
||||
// the audio overflow, for pure audio to reap segment.
|
||||
hls_aof_ratio_ = _srs_config->get_hls_aof_ratio(vhost);
|
||||
hls_aof_ratio_ = config_->get_hls_aof_ratio(vhost);
|
||||
// whether use floor(timestamp/hls_fragment) for variable timestamp
|
||||
hls_ts_floor_ = _srs_config->get_hls_ts_floor(vhost);
|
||||
hls_ts_floor_ = config_->get_hls_ts_floor(vhost);
|
||||
|
||||
hls_keys_ = _srs_config->get_hls_keys(vhost);
|
||||
hls_fragments_per_key_ = _srs_config->get_hls_fragments_per_key(vhost);
|
||||
hls_key_file_ = _srs_config->get_hls_key_file(vhost);
|
||||
hls_key_file_path_ = _srs_config->get_hls_key_file_path(vhost);
|
||||
hls_key_url_ = _srs_config->get_hls_key_url(vhost);
|
||||
hls_keys_ = config_->get_hls_keys(vhost);
|
||||
hls_fragments_per_key_ = config_->get_hls_fragments_per_key(vhost);
|
||||
hls_key_file_ = config_->get_hls_key_file(vhost);
|
||||
hls_key_file_path_ = config_->get_hls_key_file_path(vhost);
|
||||
hls_key_url_ = config_->get_hls_key_url(vhost);
|
||||
|
||||
previous_floor_ts_ = 0;
|
||||
accept_floor_ts_ = 0;
|
||||
|
|
@ -698,7 +722,7 @@ srs_error_t SrsHlsFmp4Muxer::update_config(ISrsRequest *r)
|
|||
}
|
||||
}
|
||||
|
||||
writer_ = new SrsFileWriter();
|
||||
writer_ = app_factory_->create_file_writer();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -907,13 +931,13 @@ srs_error_t SrsHlsFmp4Muxer::write_hls_key()
|
|||
key_file = srs_strings_replace(key_file, "[seq]", srs_strconv_format_int(current_->sequence_no_));
|
||||
string key_url = hls_key_file_path_ + "/" + key_file;
|
||||
|
||||
SrsFileWriter fw;
|
||||
if ((err = fw.open(key_url)) != srs_success) {
|
||||
SrsUniquePtr<ISrsFileWriter> fw(app_factory_->create_file_writer());
|
||||
if ((err = fw->open(key_url)) != srs_success) {
|
||||
return srs_error_wrap(err, "open file %s", key_url.c_str());
|
||||
}
|
||||
|
||||
err = fw.write(key_, 16, NULL);
|
||||
fw.close();
|
||||
err = fw->write(key_, 16, NULL);
|
||||
fw->close();
|
||||
|
||||
if (err != srs_success) {
|
||||
return srs_error_wrap(err, "write key");
|
||||
|
|
@ -937,23 +961,25 @@ srs_error_t SrsHlsFmp4Muxer::refresh_m3u8()
|
|||
}
|
||||
|
||||
std::string temp_m3u8 = m3u8_ + ".temp";
|
||||
if ((err = _refresh_m3u8(temp_m3u8)) == srs_success) {
|
||||
if ((err = do_refresh_m3u8(temp_m3u8)) == srs_success) {
|
||||
if (rename(temp_m3u8.c_str(), m3u8_.c_str()) < 0) {
|
||||
err = srs_error_new(ERROR_HLS_WRITE_FAILED, "hls: rename m3u8 file failed. %s => %s", temp_m3u8.c_str(), m3u8_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// remove the temp file.
|
||||
if (srs_path_exists(temp_m3u8)) {
|
||||
if (unlink(temp_m3u8.c_str()) < 0) {
|
||||
srs_warn("ignore remove m3u8 failed, %s", temp_m3u8.c_str());
|
||||
SrsUniquePtr<SrsPath> path(app_factory_->create_path());
|
||||
if (path->exists(temp_m3u8)) {
|
||||
if ((err = path->unlink(temp_m3u8)) != srs_success) {
|
||||
srs_warn("refresh: ignore remove m3u8 failed, %s", srs_error_desc(err).c_str());
|
||||
srs_freep(err);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsHlsFmp4Muxer::_refresh_m3u8(std::string m3u8_file)
|
||||
srs_error_t SrsHlsFmp4Muxer::do_refresh_m3u8(std::string m3u8_file)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
|
|
@ -962,8 +988,8 @@ srs_error_t SrsHlsFmp4Muxer::_refresh_m3u8(std::string m3u8_file)
|
|||
return err;
|
||||
}
|
||||
|
||||
SrsFileWriter writer;
|
||||
if ((err = writer.open(m3u8_file)) != srs_success) {
|
||||
SrsUniquePtr<ISrsFileWriter> writer(app_factory_->create_file_writer());
|
||||
if ((err = writer->open(m3u8_file)) != srs_success) {
|
||||
return srs_error_wrap(err, "hls: open m3u8 file %s", m3u8_file.c_str());
|
||||
}
|
||||
|
||||
|
|
@ -1048,7 +1074,7 @@ srs_error_t SrsHlsFmp4Muxer::_refresh_m3u8(std::string m3u8_file)
|
|||
|
||||
// write m3u8 to writer.
|
||||
std::string m3u8 = ss.str();
|
||||
if ((err = writer.write((char *)m3u8.c_str(), (int)m3u8.length(), NULL)) != srs_success) {
|
||||
if ((err = writer->write((char *)m3u8.c_str(), (int)m3u8.length(), NULL)) != srs_success) {
|
||||
return srs_error_wrap(err, "hls: write m3u8");
|
||||
}
|
||||
|
||||
|
|
@ -1080,6 +1106,9 @@ SrsHlsMuxer::SrsHlsMuxer()
|
|||
|
||||
memset(key_, 0, 16);
|
||||
memset(iv_, 0, 16);
|
||||
|
||||
config_ = _srs_config;
|
||||
app_factory_ = _srs_app_factory;
|
||||
}
|
||||
|
||||
SrsHlsMuxer::~SrsHlsMuxer()
|
||||
|
|
@ -1090,6 +1119,9 @@ SrsHlsMuxer::~SrsHlsMuxer()
|
|||
srs_freep(async_);
|
||||
srs_freep(context_);
|
||||
srs_freep(writer_);
|
||||
|
||||
config_ = NULL;
|
||||
app_factory_ = NULL;
|
||||
}
|
||||
|
||||
void SrsHlsMuxer::dispose()
|
||||
|
|
@ -1106,8 +1138,12 @@ void SrsHlsMuxer::dispose()
|
|||
srs_freep(current_);
|
||||
}
|
||||
|
||||
if (unlink(m3u8_.c_str()) < 0) {
|
||||
srs_warn("dispose unlink path failed. file=%s", m3u8_.c_str());
|
||||
SrsUniquePtr<SrsPath> path(app_factory_->create_path());
|
||||
if (path->exists(m3u8_)) {
|
||||
if ((err = path->unlink(m3u8_)) != srs_success) {
|
||||
srs_warn("dispose: ignore remove m3u8 failed, %s", srs_error_desc(err).c_str());
|
||||
srs_freep(err);
|
||||
}
|
||||
}
|
||||
|
||||
srs_trace("gracefully dispose hls %s", req_ ? req_->get_stream_url().c_str() : "");
|
||||
|
|
@ -1240,7 +1276,7 @@ srs_error_t SrsHlsMuxer::update_config(ISrsRequest *r, string entry_prefix,
|
|||
m3u8_ = path + "/" + m3u8_url_;
|
||||
|
||||
// when update config, reset the history target duration.
|
||||
max_td_ = fragment * _srs_config->get_hls_td_ratio(r->vhost_);
|
||||
max_td_ = fragment * config_->get_hls_td_ratio(r->vhost_);
|
||||
|
||||
// create m3u8 dir once.
|
||||
m3u8_dir_ = srs_path_filepath_dir(m3u8_);
|
||||
|
|
@ -1258,9 +1294,9 @@ srs_error_t SrsHlsMuxer::update_config(ISrsRequest *r, string entry_prefix,
|
|||
}
|
||||
|
||||
if (hls_keys_) {
|
||||
writer_ = new SrsEncFileWriter();
|
||||
writer_ = app_factory_->create_enc_file_writer();
|
||||
} else {
|
||||
writer_ = new SrsFileWriter();
|
||||
writer_ = app_factory_->create_file_writer();
|
||||
}
|
||||
|
||||
return err;
|
||||
|
|
@ -1271,21 +1307,29 @@ srs_error_t SrsHlsMuxer::recover_hls()
|
|||
srs_error_t err = srs_success;
|
||||
|
||||
// exist the m3u8 file.
|
||||
if (!srs_path_exists(m3u8_)) {
|
||||
SrsUniquePtr<SrsPath> path(app_factory_->create_path());
|
||||
if (!path->exists(m3u8_)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return do_recover_hls();
|
||||
}
|
||||
|
||||
srs_error_t SrsHlsMuxer::do_recover_hls()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
srs_trace("hls: recover stream m3u8=%s, m3u8_url=%s, hls_path=%s",
|
||||
m3u8_.c_str(), m3u8_url_.c_str(), hls_path_.c_str());
|
||||
|
||||
// read whole m3u8 file content as a string
|
||||
SrsFileReader fr;
|
||||
if ((err = fr.open(m3u8_)) != srs_success) {
|
||||
SrsUniquePtr<ISrsFileReader> fr(app_factory_->create_file_reader());
|
||||
if ((err = fr->open(m3u8_)) != srs_success) {
|
||||
return srs_error_wrap(err, "open file");
|
||||
}
|
||||
|
||||
std::string body;
|
||||
if ((err = srs_io_readall(&fr, body)) != srs_success) {
|
||||
if ((err = srs_io_readall(fr.get(), body)) != srs_success) {
|
||||
return srs_error_wrap(err, "read data");
|
||||
}
|
||||
if (body.empty()) {
|
||||
|
|
@ -1745,13 +1789,13 @@ srs_error_t SrsHlsMuxer::write_hls_key()
|
|||
key_file = srs_strings_replace(key_file, "[seq]", srs_strconv_format_int(current_->sequence_no_));
|
||||
string key_url = hls_key_file_path_ + "/" + key_file;
|
||||
|
||||
SrsFileWriter fw;
|
||||
if ((err = fw.open(key_url)) != srs_success) {
|
||||
SrsUniquePtr<ISrsFileWriter> fw(app_factory_->create_file_writer());
|
||||
if ((err = fw->open(key_url)) != srs_success) {
|
||||
return srs_error_wrap(err, "open file %s", key_url.c_str());
|
||||
}
|
||||
|
||||
err = fw.write(key_, 16, NULL);
|
||||
fw.close();
|
||||
err = fw->write(key_, 16, NULL);
|
||||
fw->close();
|
||||
|
||||
if (err != srs_success) {
|
||||
return srs_error_wrap(err, "write key");
|
||||
|
|
@ -1775,23 +1819,25 @@ srs_error_t SrsHlsMuxer::refresh_m3u8()
|
|||
}
|
||||
|
||||
std::string temp_m3u8 = m3u8_ + ".temp";
|
||||
if ((err = _refresh_m3u8(temp_m3u8)) == srs_success) {
|
||||
if ((err = do_refresh_m3u8(temp_m3u8)) == srs_success) {
|
||||
if (rename(temp_m3u8.c_str(), m3u8_.c_str()) < 0) {
|
||||
err = srs_error_new(ERROR_HLS_WRITE_FAILED, "hls: rename m3u8 file failed. %s => %s", temp_m3u8.c_str(), m3u8_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// remove the temp file.
|
||||
if (srs_path_exists(temp_m3u8)) {
|
||||
if (unlink(temp_m3u8.c_str()) < 0) {
|
||||
srs_warn("ignore remove m3u8 failed, %s", temp_m3u8.c_str());
|
||||
SrsUniquePtr<SrsPath> path(app_factory_->create_path());
|
||||
if (path->exists(temp_m3u8)) {
|
||||
if ((err = path->unlink(temp_m3u8)) != srs_success) {
|
||||
srs_warn("refresh: ignore remove m3u8 failed, %s", srs_error_desc(err).c_str());
|
||||
srs_freep(err);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
|
||||
srs_error_t SrsHlsMuxer::do_refresh_m3u8(string m3u8_file)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
|
|
@ -1800,8 +1846,8 @@ srs_error_t SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
|
|||
return err;
|
||||
}
|
||||
|
||||
SrsFileWriter writer;
|
||||
if ((err = writer.open(m3u8_file)) != srs_success) {
|
||||
SrsUniquePtr<ISrsFileWriter> writer(app_factory_->create_file_writer());
|
||||
if ((err = writer->open(m3u8_file)) != srs_success) {
|
||||
return srs_error_wrap(err, "hls: open m3u8 file %s", m3u8_file.c_str());
|
||||
}
|
||||
|
||||
|
|
@ -1882,7 +1928,7 @@ srs_error_t SrsHlsMuxer::_refresh_m3u8(string m3u8_file)
|
|||
|
||||
// write m3u8 to writer.
|
||||
std::string m3u8 = ss.str();
|
||||
if ((err = writer.write((char *)m3u8.c_str(), (int)m3u8.length(), NULL)) != srs_success) {
|
||||
if ((err = writer->write((char *)m3u8.c_str(), (int)m3u8.length(), NULL)) != srs_success) {
|
||||
return srs_error_wrap(err, "hls: write m3u8");
|
||||
}
|
||||
|
||||
|
|
@ -1905,12 +1951,16 @@ SrsHlsController::SrsHlsController()
|
|||
hls_dts_directly_ = false;
|
||||
previous_audio_dts_ = 0;
|
||||
aac_samples_ = 0;
|
||||
|
||||
config_ = _srs_config;
|
||||
}
|
||||
|
||||
SrsHlsController::~SrsHlsController()
|
||||
{
|
||||
srs_freep(muxer_);
|
||||
srs_freep(tsmc_);
|
||||
|
||||
config_ = NULL;
|
||||
}
|
||||
|
||||
// CRITICAL: This method is called AFTER the source has been added to the source pool
|
||||
|
|
@ -1961,34 +2011,34 @@ srs_error_t SrsHlsController::on_publish(ISrsRequest *req)
|
|||
std::string stream = req->stream_;
|
||||
std::string app = req->app_;
|
||||
|
||||
srs_utime_t hls_fragment = _srs_config->get_hls_fragment(vhost);
|
||||
double hls_td_ratio = _srs_config->get_hls_td_ratio(vhost);
|
||||
srs_utime_t hls_window = _srs_config->get_hls_window(vhost);
|
||||
srs_utime_t hls_fragment = config_->get_hls_fragment(vhost);
|
||||
double hls_td_ratio = config_->get_hls_td_ratio(vhost);
|
||||
srs_utime_t hls_window = config_->get_hls_window(vhost);
|
||||
|
||||
// get the hls m3u8 ts list entry prefix config
|
||||
std::string entry_prefix = _srs_config->get_hls_entry_prefix(vhost);
|
||||
std::string entry_prefix = config_->get_hls_entry_prefix(vhost);
|
||||
// get the hls path config
|
||||
std::string path = _srs_config->get_hls_path(vhost);
|
||||
std::string m3u8_file = _srs_config->get_hls_m3u8_file(vhost);
|
||||
std::string ts_file = _srs_config->get_hls_ts_file(vhost);
|
||||
bool cleanup = _srs_config->get_hls_cleanup(vhost);
|
||||
bool wait_keyframe = _srs_config->get_hls_wait_keyframe(vhost);
|
||||
std::string path = config_->get_hls_path(vhost);
|
||||
std::string m3u8_file = config_->get_hls_m3u8_file(vhost);
|
||||
std::string ts_file = config_->get_hls_ts_file(vhost);
|
||||
bool cleanup = config_->get_hls_cleanup(vhost);
|
||||
bool wait_keyframe = config_->get_hls_wait_keyframe(vhost);
|
||||
// the audio overflow, for pure audio to reap segment.
|
||||
double hls_aof_ratio = _srs_config->get_hls_aof_ratio(vhost);
|
||||
double hls_aof_ratio = config_->get_hls_aof_ratio(vhost);
|
||||
// whether use floor(timestamp/hls_fragment) for variable timestamp
|
||||
bool ts_floor = _srs_config->get_hls_ts_floor(vhost);
|
||||
bool ts_floor = config_->get_hls_ts_floor(vhost);
|
||||
// the seconds to dispose the hls.
|
||||
srs_utime_t hls_dispose = _srs_config->get_hls_dispose(vhost);
|
||||
srs_utime_t hls_dispose = config_->get_hls_dispose(vhost);
|
||||
|
||||
bool hls_keys = _srs_config->get_hls_keys(vhost);
|
||||
int hls_fragments_per_key = _srs_config->get_hls_fragments_per_key(vhost);
|
||||
string hls_key_file = _srs_config->get_hls_key_file(vhost);
|
||||
string hls_key_file_path = _srs_config->get_hls_key_file_path(vhost);
|
||||
string hls_key_url = _srs_config->get_hls_key_url(vhost);
|
||||
bool hls_keys = config_->get_hls_keys(vhost);
|
||||
int hls_fragments_per_key = config_->get_hls_fragments_per_key(vhost);
|
||||
string hls_key_file = config_->get_hls_key_file(vhost);
|
||||
string hls_key_file_path = config_->get_hls_key_file_path(vhost);
|
||||
string hls_key_url = config_->get_hls_key_url(vhost);
|
||||
|
||||
// TODO: FIXME: support load exists m3u8, to recover publish stream.
|
||||
// for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase.
|
||||
bool recover = _srs_config->get_hls_recover(vhost);
|
||||
bool recover = config_->get_hls_recover(vhost);
|
||||
|
||||
if ((err = muxer_->on_publish(req)) != srs_success) {
|
||||
return srs_error_wrap(err, "muxer publish");
|
||||
|
|
@ -2011,7 +2061,7 @@ srs_error_t SrsHlsController::on_publish(ISrsRequest *req)
|
|||
// This config item is used in SrsHls, we just log its value here.
|
||||
// If enabled, directly turn FLV timestamp to TS DTS.
|
||||
// @remark It'll be reloaded automatically, because the origin hub will republish while reloading.
|
||||
hls_dts_directly_ = _srs_config->get_vhost_hls_dts_directly(req->vhost_);
|
||||
hls_dts_directly_ = config_->get_vhost_hls_dts_directly(req->vhost_);
|
||||
|
||||
srs_trace("hls: win=%dms, frag=%dms, prefix=%s, path=%s, m3u8=%s, ts=%s, tdr=%.2f, aof=%.2f, floor=%d, clean=%d, waitk=%d, dispose=%dms, dts_directly=%d",
|
||||
srsu2msi(hls_window), srsu2msi(hls_fragment), entry_prefix.c_str(), path.c_str(), m3u8_file.c_str(), ts_file.c_str(),
|
||||
|
|
@ -2235,11 +2285,15 @@ SrsHlsMp4Controller::SrsHlsMp4Controller()
|
|||
|
||||
req_ = NULL;
|
||||
muxer_ = new SrsHlsFmp4Muxer();
|
||||
|
||||
config_ = _srs_config;
|
||||
}
|
||||
|
||||
SrsHlsMp4Controller::~SrsHlsMp4Controller()
|
||||
{
|
||||
srs_freep(muxer_);
|
||||
|
||||
config_ = NULL;
|
||||
}
|
||||
|
||||
// CRITICAL: This method is called AFTER the source has been added to the source pool
|
||||
|
|
@ -2273,11 +2327,11 @@ srs_error_t SrsHlsMp4Controller::on_publish(ISrsRequest *req)
|
|||
std::string app = req->app_;
|
||||
|
||||
// get the hls m3u8 ts list entry prefix config
|
||||
std::string entry_prefix = _srs_config->get_hls_entry_prefix(vhost);
|
||||
std::string entry_prefix = config_->get_hls_entry_prefix(vhost);
|
||||
// get the hls path config
|
||||
std::string path = _srs_config->get_hls_path(vhost);
|
||||
std::string m3u8_file = _srs_config->get_hls_m3u8_file(vhost);
|
||||
std::string ts_file = _srs_config->get_hls_ts_file(vhost);
|
||||
std::string path = config_->get_hls_path(vhost);
|
||||
std::string m3u8_file = config_->get_hls_m3u8_file(vhost);
|
||||
std::string ts_file = config_->get_hls_ts_file(vhost);
|
||||
|
||||
if ((err = muxer_->on_publish(req)) != srs_success) {
|
||||
return srs_error_wrap(err, "muxer publish");
|
||||
|
|
@ -2417,6 +2471,8 @@ SrsHls::SrsHls()
|
|||
controller_ = NULL;
|
||||
|
||||
pprint_ = SrsPithyPrint::create_hls();
|
||||
|
||||
config_ = _srs_config;
|
||||
}
|
||||
|
||||
SrsHls::~SrsHls()
|
||||
|
|
@ -2424,6 +2480,8 @@ SrsHls::~SrsHls()
|
|||
srs_freep(jitter_);
|
||||
srs_freep(controller_);
|
||||
srs_freep(pprint_);
|
||||
|
||||
config_ = NULL;
|
||||
}
|
||||
|
||||
void SrsHls::async_reload()
|
||||
|
|
@ -2483,7 +2541,7 @@ void SrsHls::dispose()
|
|||
|
||||
// Ignore when hls_dispose disabled.
|
||||
// @see https://github.com/ossrs/srs/issues/865
|
||||
srs_utime_t hls_dispose = _srs_config->get_hls_dispose(req_->vhost_);
|
||||
srs_utime_t hls_dispose = config_->get_hls_dispose(req_->vhost_);
|
||||
if (!hls_dispose) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -2512,7 +2570,7 @@ srs_error_t SrsHls::cycle()
|
|||
return err;
|
||||
|
||||
// If not unpublishing and not reloading, try to dispose HLS stream.
|
||||
srs_utime_t hls_dispose = _srs_config->get_hls_dispose(req_->vhost_);
|
||||
srs_utime_t hls_dispose = config_->get_hls_dispose(req_->vhost_);
|
||||
if (hls_dispose <= 0) {
|
||||
return err;
|
||||
}
|
||||
|
|
@ -2535,7 +2593,7 @@ srs_error_t SrsHls::cycle()
|
|||
srs_utime_t SrsHls::cleanup_delay()
|
||||
{
|
||||
// We use larger timeout to cleanup the HLS, after disposed it if required.
|
||||
return _srs_config->get_hls_dispose(req_->vhost_) * 1.1;
|
||||
return config_->get_hls_dispose(req_->vhost_) * 1.1;
|
||||
}
|
||||
|
||||
// CRITICAL: This method is called AFTER the source has been added to the source pool
|
||||
|
|
@ -2551,7 +2609,7 @@ srs_error_t SrsHls::initialize(SrsOriginHub *h, ISrsRequest *r)
|
|||
hub_ = h;
|
||||
req_ = r;
|
||||
|
||||
bool is_fmp4_enabled = _srs_config->get_hls_use_fmp4(r->vhost_);
|
||||
bool is_fmp4_enabled = config_->get_hls_use_fmp4(r->vhost_);
|
||||
|
||||
if (!controller_) {
|
||||
if (is_fmp4_enabled) {
|
||||
|
|
@ -2580,7 +2638,7 @@ srs_error_t SrsHls::on_publish()
|
|||
return err;
|
||||
}
|
||||
|
||||
if (!_srs_config->get_hls_enabled(req_->vhost_)) {
|
||||
if (!config_->get_hls_enabled(req_->vhost_)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,13 +27,18 @@ class ISrsRequest;
|
|||
class SrsPithyPrint;
|
||||
class SrsLiveSource;
|
||||
class SrsOriginHub;
|
||||
class SrsFileWriter;
|
||||
class ISrsFileWriter;
|
||||
class ISrsAppConfig;
|
||||
class ISrsHttpHooks;
|
||||
class SrsSimpleStream;
|
||||
class SrsTsAacJitter;
|
||||
class SrsTsMessageCache;
|
||||
class SrsHlsSegment;
|
||||
class SrsTsContext;
|
||||
class SrsFmp4SegmentEncoder;
|
||||
class ISrsHttpHooks;
|
||||
class ISrsAppConfig;
|
||||
class SrsAppFactory;
|
||||
|
||||
// The wrapper of m3u8 segment from specification:
|
||||
//
|
||||
|
|
@ -48,7 +53,7 @@ public:
|
|||
// ts uri in m3u8.
|
||||
std::string uri_;
|
||||
// The underlayer file writer.
|
||||
SrsFileWriter *writer_;
|
||||
ISrsFileWriter *writer_;
|
||||
// The TS context writer to write TS to file.
|
||||
SrsTsContextWriter *tscw_;
|
||||
// Will be saved in m3u8 file.
|
||||
|
|
@ -57,7 +62,7 @@ public:
|
|||
std::string keypath_;
|
||||
|
||||
public:
|
||||
SrsHlsSegment(SrsTsContext *c, SrsAudioCodecId ac, SrsVideoCodecId vc, SrsFileWriter *w);
|
||||
SrsHlsSegment(SrsTsContext *c, SrsAudioCodecId ac, SrsVideoCodecId vc, ISrsFileWriter *w);
|
||||
virtual ~SrsHlsSegment();
|
||||
|
||||
public:
|
||||
|
|
@ -69,7 +74,7 @@ public:
|
|||
class SrsInitMp4Segment : public SrsFragment
|
||||
{
|
||||
private:
|
||||
SrsFileWriter *fw_;
|
||||
ISrsFileWriter *fw_;
|
||||
SrsMp4M2tsInitEncoder init_;
|
||||
|
||||
private:
|
||||
|
|
@ -81,7 +86,7 @@ private:
|
|||
uint8_t const_iv_size_;
|
||||
|
||||
public:
|
||||
SrsInitMp4Segment(SrsFileWriter *fw);
|
||||
SrsInitMp4Segment(ISrsFileWriter *fw);
|
||||
virtual ~SrsInitMp4Segment();
|
||||
|
||||
public:
|
||||
|
|
@ -99,7 +104,7 @@ private:
|
|||
class SrsHlsM4sSegment : public SrsFragment
|
||||
{
|
||||
private:
|
||||
SrsFileWriter *fw_;
|
||||
ISrsFileWriter *fw_;
|
||||
SrsFmp4SegmentEncoder enc_;
|
||||
|
||||
public:
|
||||
|
|
@ -111,7 +116,7 @@ public:
|
|||
unsigned char iv_[16];
|
||||
|
||||
public:
|
||||
SrsHlsM4sSegment(SrsFileWriter *fw);
|
||||
SrsHlsM4sSegment(ISrsFileWriter *fw);
|
||||
virtual ~SrsHlsM4sSegment();
|
||||
|
||||
public:
|
||||
|
|
@ -125,6 +130,10 @@ public:
|
|||
// The hls async call: on_hls
|
||||
class SrsDvrAsyncCallOnHls : public ISrsAsyncCallTask
|
||||
{
|
||||
private:
|
||||
ISrsAppConfig *config_;
|
||||
ISrsHttpHooks *hooks_;
|
||||
|
||||
private:
|
||||
SrsContextId cid_;
|
||||
std::string path_;
|
||||
|
|
@ -148,6 +157,10 @@ public:
|
|||
// The hls async call: on_hls_notify
|
||||
class SrsDvrAsyncCallOnHlsNotify : public ISrsAsyncCallTask
|
||||
{
|
||||
private:
|
||||
ISrsAppConfig *config_;
|
||||
ISrsHttpHooks *hooks_;
|
||||
|
||||
private:
|
||||
SrsContextId cid_;
|
||||
std::string ts_url_;
|
||||
|
|
@ -171,6 +184,10 @@ public:
|
|||
// TODO: Rename to SrsHlsTsMuxer, for TS file only.
|
||||
class SrsHlsMuxer
|
||||
{
|
||||
private:
|
||||
ISrsAppConfig *config_;
|
||||
SrsAppFactory *app_factory_;
|
||||
|
||||
private:
|
||||
ISrsRequest *req_;
|
||||
|
||||
|
|
@ -212,7 +229,7 @@ private:
|
|||
unsigned char key_[16];
|
||||
unsigned char iv_[16];
|
||||
// The underlayer file writer.
|
||||
SrsFileWriter *writer_;
|
||||
ISrsFileWriter *writer_;
|
||||
|
||||
private:
|
||||
int sequence_no_;
|
||||
|
|
@ -293,13 +310,15 @@ private:
|
|||
virtual srs_error_t do_segment_close();
|
||||
virtual srs_error_t write_hls_key();
|
||||
virtual srs_error_t refresh_m3u8();
|
||||
virtual srs_error_t _refresh_m3u8(std::string m3u8_file);
|
||||
virtual srs_error_t do_refresh_m3u8(std::string m3u8_file);
|
||||
// Check if a segment with the given URI already exists in the segments list.
|
||||
virtual bool segment_exists(const std::string &ts_url);
|
||||
|
||||
public:
|
||||
// HLS recover mode.
|
||||
srs_error_t recover_hls();
|
||||
private:
|
||||
virtual srs_error_t do_recover_hls();
|
||||
};
|
||||
|
||||
// Mux the HLS stream(m3u8 and m4s files).
|
||||
|
|
@ -307,6 +326,10 @@ public:
|
|||
// to flush video/audio, without any mechenisms.
|
||||
class SrsHlsFmp4Muxer
|
||||
{
|
||||
private:
|
||||
ISrsAppConfig *config_;
|
||||
SrsAppFactory *app_factory_;
|
||||
|
||||
private:
|
||||
ISrsRequest *req_;
|
||||
|
||||
|
|
@ -359,7 +382,7 @@ private:
|
|||
unsigned char kid_[16];
|
||||
unsigned char iv_[16];
|
||||
// The underlayer file writer.
|
||||
SrsFileWriter *writer_;
|
||||
ISrsFileWriter *writer_;
|
||||
|
||||
private:
|
||||
int sequence_no_;
|
||||
|
|
@ -441,7 +464,7 @@ private:
|
|||
virtual srs_error_t do_segment_close();
|
||||
virtual srs_error_t write_hls_key();
|
||||
virtual srs_error_t refresh_m3u8();
|
||||
virtual srs_error_t _refresh_m3u8(std::string m3u8_file);
|
||||
virtual srs_error_t do_refresh_m3u8(std::string m3u8_file);
|
||||
};
|
||||
|
||||
// The base class for HLS controller
|
||||
|
|
@ -489,6 +512,9 @@ public:
|
|||
// TODO: Rename to SrsHlsTsController, for TS file only.
|
||||
class SrsHlsController : public ISrsHlsController
|
||||
{
|
||||
private:
|
||||
ISrsAppConfig *config_;
|
||||
|
||||
private:
|
||||
// The HLS muxer to reap ts and m3u8.
|
||||
// The TS is cached to SrsTsMessageCache then flush to ts segment.
|
||||
|
|
@ -542,6 +568,9 @@ private:
|
|||
// Direct sample processing without caching, simpler than TS controller.
|
||||
class SrsHlsMp4Controller : public ISrsHlsController
|
||||
{
|
||||
private:
|
||||
ISrsAppConfig *config_;
|
||||
|
||||
private:
|
||||
bool has_video_sh_;
|
||||
bool has_audio_sh_;
|
||||
|
|
@ -587,6 +616,9 @@ public:
|
|||
// TODO: FIXME: add utest for hls.
|
||||
class SrsHls
|
||||
{
|
||||
private:
|
||||
ISrsAppConfig *config_;
|
||||
|
||||
private:
|
||||
ISrsHlsController *controller_;
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,8 @@
|
|||
XX(ERROR_NO_SOURCE, 1097, "NoSource", "No source found") \
|
||||
XX(ERROR_STREAM_DISPOSING, 1098, "StreamDisposing", "Stream is disposing") \
|
||||
XX(ERROR_NOT_IMPLEMENTED, 1099, "NotImplemented", "Feature is not implemented") \
|
||||
XX(ERROR_NOT_SUPPORTED, 1100, "NotSupported", "Feature is not supported")
|
||||
XX(ERROR_NOT_SUPPORTED, 1100, "NotSupported", "Feature is not supported") \
|
||||
XX(ERROR_SYSTEM_FILE_UNLINK, 1101, "FileUnlink", "Failed to unlink file")
|
||||
|
||||
/**************************************************/
|
||||
/* RTMP protocol error. */
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@ srs_fclose_t _srs_fclose_fn = ::fclose;
|
|||
srs_ftell_t _srs_ftell_fn = ::ftell;
|
||||
srs_setvbuf_t _srs_setvbuf_fn = ::setvbuf;
|
||||
|
||||
ISrsFileWriter::ISrsFileWriter()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsFileWriter::~ISrsFileWriter()
|
||||
{
|
||||
}
|
||||
|
||||
SrsFileWriter::SrsFileWriter()
|
||||
{
|
||||
fp_ = NULL;
|
||||
|
|
@ -199,6 +207,14 @@ SrsFileReader *ISrsFileReaderFactory::create_file_reader()
|
|||
return new SrsFileReader();
|
||||
}
|
||||
|
||||
ISrsFileReader::ISrsFileReader()
|
||||
{
|
||||
}
|
||||
|
||||
ISrsFileReader::~ISrsFileReader()
|
||||
{
|
||||
}
|
||||
|
||||
SrsFileReader::SrsFileReader()
|
||||
{
|
||||
fd_ = -1;
|
||||
|
|
|
|||
|
|
@ -19,10 +19,20 @@
|
|||
|
||||
class SrsFileReader;
|
||||
|
||||
/**
|
||||
* file writer, to write to file.
|
||||
*/
|
||||
class SrsFileWriter : public ISrsWriteSeeker
|
||||
// The file writer factory.
|
||||
class ISrsFileWriter : public ISrsWriteSeeker
|
||||
{
|
||||
public:
|
||||
ISrsFileWriter();
|
||||
virtual ~ISrsFileWriter();
|
||||
|
||||
public:
|
||||
virtual srs_error_t open(std::string p) = 0;
|
||||
virtual void close() = 0;
|
||||
};
|
||||
|
||||
// file writer, to write to file.
|
||||
class SrsFileWriter : public ISrsFileWriter
|
||||
{
|
||||
private:
|
||||
std::string path_;
|
||||
|
|
@ -34,25 +44,17 @@ public:
|
|||
virtual ~SrsFileWriter();
|
||||
|
||||
public:
|
||||
/**
|
||||
* set io buf size
|
||||
*/
|
||||
// set io buf size
|
||||
virtual srs_error_t set_iobuf_size(int size);
|
||||
|
||||
/**
|
||||
* open file writer, in truncate mode.
|
||||
* @param p a string indicates the path of file to open.
|
||||
*/
|
||||
// open file writer, in truncate mode.
|
||||
// @param p a string indicates the path of file to open.
|
||||
virtual srs_error_t open(std::string p);
|
||||
/**
|
||||
* open file writer, in append mode.
|
||||
* @param p a string indicates the path of file to open.
|
||||
*/
|
||||
// open file writer, in append mode.
|
||||
// @param p a string indicates the path of file to open.
|
||||
virtual srs_error_t open_append(std::string p);
|
||||
/**
|
||||
* close current writer.
|
||||
* @remark user can reopen again.
|
||||
*/
|
||||
// close current writer.
|
||||
// @remark user can reopen again.
|
||||
virtual void close();
|
||||
|
||||
public:
|
||||
|
|
@ -77,10 +79,22 @@ public:
|
|||
virtual SrsFileReader *create_file_reader();
|
||||
};
|
||||
|
||||
// The file reader.
|
||||
class ISrsFileReader : public ISrsReadSeeker
|
||||
{
|
||||
public:
|
||||
ISrsFileReader();
|
||||
virtual ~ISrsFileReader();
|
||||
|
||||
public:
|
||||
virtual srs_error_t open(std::string p) = 0;
|
||||
virtual void close() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* file reader, to read from file.
|
||||
*/
|
||||
class SrsFileReader : public ISrsReadSeeker
|
||||
class SrsFileReader : public ISrsFileReader
|
||||
{
|
||||
private:
|
||||
std::string path_;
|
||||
|
|
|
|||
|
|
@ -527,6 +527,28 @@ srs_error_t srs_os_mkdir_all(string dir)
|
|||
return srs_error_new(ret, "create dir %s", dir.c_str());
|
||||
}
|
||||
|
||||
SrsPath::SrsPath()
|
||||
{
|
||||
}
|
||||
|
||||
SrsPath::~SrsPath()
|
||||
{
|
||||
}
|
||||
|
||||
bool SrsPath::exists(std::string path)
|
||||
{
|
||||
return srs_path_exists(path);
|
||||
}
|
||||
|
||||
srs_error_t SrsPath::unlink(std::string path)
|
||||
{
|
||||
if (::unlink(path.c_str()) < 0) {
|
||||
return srs_error_new(ERROR_SYSTEM_FILE_UNLINK, "unlink %s", path.c_str());
|
||||
}
|
||||
|
||||
return srs_success;
|
||||
}
|
||||
|
||||
bool srs_path_exists(std::string path)
|
||||
{
|
||||
struct stat st;
|
||||
|
|
|
|||
|
|
@ -129,6 +129,20 @@ extern bool srs_bytes_equal(void *pa, void *pb, int size);
|
|||
// Create dir recursively
|
||||
extern srs_error_t srs_os_mkdir_all(std::string dir);
|
||||
|
||||
// The path utility.
|
||||
class SrsPath
|
||||
{
|
||||
public:
|
||||
SrsPath();
|
||||
virtual ~SrsPath();
|
||||
|
||||
public:
|
||||
// Whether path exists.
|
||||
virtual bool exists(std::string path);
|
||||
// Remove or unlink file.
|
||||
virtual srs_error_t unlink(std::string path);
|
||||
};
|
||||
|
||||
// Whether path exists.
|
||||
extern bool srs_path_exists(std::string path);
|
||||
// Get the dirname of path, for instance, dirname("/live/livestream")="/live"
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ SrsConfig *_srs_config = NULL;
|
|||
|
||||
// @global kernel factory.
|
||||
ISrsKernelFactory *_srs_kernel_factory = new SrsFinalFactory();
|
||||
SrsAppFactory *_srs_app_factory = new SrsAppFactory();
|
||||
|
||||
// @global version of srs, which can grep keyword "XCORE"
|
||||
extern const char *_srs_version;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ bool _srs_config_by_env = false;
|
|||
|
||||
// @global kernel factory.
|
||||
ISrsKernelFactory *_srs_kernel_factory = new SrsFinalFactory();
|
||||
SrsAppFactory *_srs_app_factory = new SrsAppFactory();
|
||||
|
||||
// The binary name of SRS.
|
||||
const char *_srs_binary = NULL;
|
||||
|
|
|
|||
|
|
@ -2240,6 +2240,156 @@ bool MockAppConfig::get_rtc_stun_strict_check(std::string vhost)
|
|||
return false; // Default to non-strict mode
|
||||
}
|
||||
|
||||
SrsConfDirective *MockAppConfig::get_vhost_on_hls(std::string vhost)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SrsConfDirective *MockAppConfig::get_vhost_on_hls_notify(std::string vhost)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_hls_enabled(std::string vhost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_hls_enabled(SrsConfDirective *vhost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_hls_use_fmp4(std::string vhost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string MockAppConfig::get_hls_entry_prefix(std::string vhost)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string MockAppConfig::get_hls_path(std::string vhost)
|
||||
{
|
||||
return "./objs/nginx/html";
|
||||
}
|
||||
|
||||
std::string MockAppConfig::get_hls_m3u8_file(std::string vhost)
|
||||
{
|
||||
return "[app]/[stream].m3u8";
|
||||
}
|
||||
|
||||
std::string MockAppConfig::get_hls_ts_file(std::string vhost)
|
||||
{
|
||||
return "[app]/[stream]-[seq].ts";
|
||||
}
|
||||
|
||||
std::string MockAppConfig::get_hls_fmp4_file(std::string vhost)
|
||||
{
|
||||
return "[app]/[stream]-[seq].m4s";
|
||||
}
|
||||
|
||||
std::string MockAppConfig::get_hls_init_file(std::string vhost)
|
||||
{
|
||||
return "[app]/[stream]/init.mp4";
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_hls_ts_floor(std::string vhost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
srs_utime_t MockAppConfig::get_hls_fragment(std::string vhost)
|
||||
{
|
||||
return 10 * SRS_UTIME_SECONDS;
|
||||
}
|
||||
|
||||
double MockAppConfig::get_hls_td_ratio(std::string vhost)
|
||||
{
|
||||
return 1.5;
|
||||
}
|
||||
|
||||
double MockAppConfig::get_hls_aof_ratio(std::string vhost)
|
||||
{
|
||||
return 2.0;
|
||||
}
|
||||
|
||||
srs_utime_t MockAppConfig::get_hls_window(std::string vhost)
|
||||
{
|
||||
return 60 * SRS_UTIME_SECONDS;
|
||||
}
|
||||
|
||||
std::string MockAppConfig::get_hls_on_error(std::string vhost)
|
||||
{
|
||||
return "continue";
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_hls_cleanup(std::string vhost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
srs_utime_t MockAppConfig::get_hls_dispose(std::string vhost)
|
||||
{
|
||||
return 120 * SRS_UTIME_SECONDS;
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_hls_wait_keyframe(std::string vhost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_hls_keys(std::string vhost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int MockAppConfig::get_hls_fragments_per_key(std::string vhost)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
std::string MockAppConfig::get_hls_key_file(std::string vhost)
|
||||
{
|
||||
return "[app]/[stream]-[seq].key";
|
||||
}
|
||||
|
||||
std::string MockAppConfig::get_hls_key_file_path(std::string vhost)
|
||||
{
|
||||
return "./objs/nginx/html";
|
||||
}
|
||||
|
||||
std::string MockAppConfig::get_hls_key_url(std::string vhost)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
int MockAppConfig::get_vhost_hls_nb_notify(std::string vhost)
|
||||
{
|
||||
return 64;
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_vhost_hls_dts_directly(std::string vhost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_hls_ctx_enabled(std::string vhost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_hls_ts_ctx_enabled(std::string vhost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MockAppConfig::get_hls_recover(std::string vhost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void MockAppConfig::set_http_hooks_enabled(bool enabled)
|
||||
{
|
||||
http_hooks_enabled_ = enabled;
|
||||
|
|
|
|||
|
|
@ -258,6 +258,37 @@ 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 SrsConfDirective *get_vhost_on_hls(std::string vhost);
|
||||
virtual SrsConfDirective *get_vhost_on_hls_notify(std::string vhost);
|
||||
// HLS methods
|
||||
virtual bool get_hls_enabled(std::string vhost);
|
||||
virtual bool get_hls_enabled(SrsConfDirective *vhost);
|
||||
virtual bool get_hls_use_fmp4(std::string vhost);
|
||||
virtual std::string get_hls_entry_prefix(std::string vhost);
|
||||
virtual std::string get_hls_path(std::string vhost);
|
||||
virtual std::string get_hls_m3u8_file(std::string vhost);
|
||||
virtual std::string get_hls_ts_file(std::string vhost);
|
||||
virtual std::string get_hls_fmp4_file(std::string vhost);
|
||||
virtual std::string get_hls_init_file(std::string vhost);
|
||||
virtual bool get_hls_ts_floor(std::string vhost);
|
||||
virtual srs_utime_t get_hls_fragment(std::string vhost);
|
||||
virtual double get_hls_td_ratio(std::string vhost);
|
||||
virtual double get_hls_aof_ratio(std::string vhost);
|
||||
virtual srs_utime_t get_hls_window(std::string vhost);
|
||||
virtual std::string get_hls_on_error(std::string vhost);
|
||||
virtual bool get_hls_cleanup(std::string vhost);
|
||||
virtual srs_utime_t get_hls_dispose(std::string vhost);
|
||||
virtual bool get_hls_wait_keyframe(std::string vhost);
|
||||
virtual bool get_hls_keys(std::string vhost);
|
||||
virtual int get_hls_fragments_per_key(std::string vhost);
|
||||
virtual std::string get_hls_key_file(std::string vhost);
|
||||
virtual std::string get_hls_key_file_path(std::string vhost);
|
||||
virtual std::string get_hls_key_url(std::string vhost);
|
||||
virtual int get_vhost_hls_nb_notify(std::string vhost);
|
||||
virtual bool get_vhost_hls_dts_directly(std::string vhost);
|
||||
virtual bool get_hls_ctx_enabled(std::string vhost);
|
||||
virtual bool get_hls_ts_ctx_enabled(std::string vhost);
|
||||
virtual bool get_hls_recover(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();
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ using namespace std;
|
|||
#include <srs_app_stream_token.hpp>
|
||||
#include <srs_kernel_error.hpp>
|
||||
#include <srs_kernel_rtc_rtp.hpp>
|
||||
#include <srs_protocol_sdp.hpp>
|
||||
#include <srs_protocol_rtc_stun.hpp>
|
||||
#include <srs_protocol_sdp.hpp>
|
||||
#include <srs_utest_app5.hpp>
|
||||
#include <srs_utest_app6.hpp>
|
||||
|
||||
|
|
@ -1172,10 +1172,6 @@ ISrsRequest *MockRtcConnectionRequest::as_http()
|
|||
return copy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
VOID TEST(SrsRtcConnectionTest, OnDtlsHandshakeDoneTypicalScenario)
|
||||
{
|
||||
srs_error_t err;
|
||||
|
|
@ -1347,8 +1343,8 @@ VOID TEST(SrsRtcConnectionTest, CreatePublisherTypicalScenario)
|
|||
SrsRtcTrackDescription *audio_desc = new SrsRtcTrackDescription();
|
||||
audio_desc->type_ = "audio";
|
||||
audio_desc->ssrc_ = 0x12345678;
|
||||
audio_desc->fec_ssrc_ = 0x12345679; // Different FEC SSRC
|
||||
audio_desc->rtx_ssrc_ = 0x1234567A; // Different RTX SSRC
|
||||
audio_desc->fec_ssrc_ = 0x12345679; // Different FEC SSRC
|
||||
audio_desc->rtx_ssrc_ = 0x1234567A; // Different RTX SSRC
|
||||
audio_desc->id_ = "test-audio-track";
|
||||
audio_desc->is_active_ = true;
|
||||
audio_desc->direction_ = "sendrecv";
|
||||
|
|
@ -1358,8 +1354,8 @@ VOID TEST(SrsRtcConnectionTest, CreatePublisherTypicalScenario)
|
|||
SrsRtcTrackDescription *video_desc = new SrsRtcTrackDescription();
|
||||
video_desc->type_ = "video";
|
||||
video_desc->ssrc_ = 0x87654321;
|
||||
video_desc->fec_ssrc_ = 0x87654322; // Different FEC SSRC
|
||||
video_desc->rtx_ssrc_ = 0x87654323; // Different RTX SSRC
|
||||
video_desc->fec_ssrc_ = 0x87654322; // Different FEC SSRC
|
||||
video_desc->rtx_ssrc_ = 0x87654323; // Different RTX SSRC
|
||||
video_desc->id_ = "test-video-track";
|
||||
video_desc->is_active_ = true;
|
||||
video_desc->direction_ = "sendrecv";
|
||||
|
|
@ -1380,7 +1376,7 @@ VOID TEST(SrsRtcConnectionTest, CreatePublisherTypicalScenario)
|
|||
|
||||
// Test the early return logic for existing publisher
|
||||
HELPER_EXPECT_SUCCESS(conn->create_publisher(req.get(), stream_desc.get()));
|
||||
EXPECT_EQ(1, (int)conn->publishers_.size()); // Should still be 1
|
||||
EXPECT_EQ(1, (int)conn->publishers_.size()); // Should still be 1
|
||||
EXPECT_EQ(existing_publisher, conn->publishers_[req->get_stream_url()]); // Should be the same publisher
|
||||
|
||||
// Test scenario 3: Test duplicate SSRC error detection
|
||||
|
|
@ -1446,7 +1442,7 @@ VOID TEST(SrsRtcConnectionTest, CreatePublisherTypicalScenario)
|
|||
}
|
||||
|
||||
// Verify all SSRC mappings were created correctly
|
||||
EXPECT_EQ(6, (int)conn->publishers_ssrc_map_.size()); // 3 audio + 3 video SSRCs
|
||||
EXPECT_EQ(6, (int)conn->publishers_ssrc_map_.size()); // 3 audio + 3 video SSRCs
|
||||
EXPECT_EQ(test_publisher, conn->publishers_ssrc_map_[0x12345678]); // Audio main
|
||||
EXPECT_EQ(test_publisher, conn->publishers_ssrc_map_[0x12345679]); // Audio FEC
|
||||
EXPECT_EQ(test_publisher, conn->publishers_ssrc_map_[0x1234567A]); // Audio RTX
|
||||
|
|
@ -1647,7 +1643,7 @@ VOID TEST(SrsRtcConnectionTest, SendRtcpRrTypicalScenario)
|
|||
|
||||
// Set up test parameters for typical RTCP RR scenario
|
||||
uint32_t test_ssrc = 0x12345678;
|
||||
uint64_t last_send_systime = 1000000; // 1 second in microseconds
|
||||
uint64_t last_send_systime = 1000000; // 1 second in microseconds
|
||||
SrsNtp last_send_ntp = SrsNtp::from_time_ms(1000); // 1 second in milliseconds
|
||||
|
||||
// Test typical send_rtcp_rr scenario - should create and send RTCP RR packet
|
||||
|
|
@ -1664,9 +1660,9 @@ VOID TEST(SrsRtcConnectionTest, SendRtcpRrTypicalScenario)
|
|||
|
||||
// Verify RTCP header: V=2, P=0, RC=1, PT=201(RR), length=7
|
||||
EXPECT_EQ(0x81, (unsigned char)rtcp_data[0]); // V=2, P=0, RC=1
|
||||
EXPECT_EQ(201, (unsigned char)rtcp_data[1]); // PT=201 (RR)
|
||||
EXPECT_EQ(0x00, (unsigned char)rtcp_data[2]); // Length high byte
|
||||
EXPECT_EQ(0x07, (unsigned char)rtcp_data[3]); // Length low byte (7 words = 32 bytes)
|
||||
EXPECT_EQ(201, (unsigned char)rtcp_data[1]); // PT=201 (RR)
|
||||
EXPECT_EQ(0x00, (unsigned char)rtcp_data[2]); // Length high byte
|
||||
EXPECT_EQ(0x07, (unsigned char)rtcp_data[3]); // Length low byte (7 words = 32 bytes)
|
||||
}
|
||||
|
||||
// Test error handling scenario
|
||||
|
|
@ -1790,7 +1786,7 @@ VOID TEST(SrsRtcConnectionTest, AddPublisherTypicalScenario)
|
|||
SrsUniquePtr<MockRtcSourceManager> mock_rtc_sources(new MockRtcSourceManager());
|
||||
|
||||
// Create a mock RTC source that can publish
|
||||
SrsRtcSource* raw_source = new SrsRtcSource();
|
||||
SrsRtcSource *raw_source = new SrsRtcSource();
|
||||
mock_rtc_sources->mock_source_ = SrsSharedPtr<SrsRtcSource>(raw_source);
|
||||
|
||||
// Replace the default rtc_sources_ with our mock
|
||||
|
|
@ -1814,13 +1810,13 @@ VOID TEST(SrsRtcConnectionTest, AddPublisherTypicalScenario)
|
|||
|
||||
// Create a simple remote SDP that will fail negotiation (testing error handling)
|
||||
ruc->remote_sdp_str_ = "v=0\r\n"
|
||||
"o=- 123456 654321 IN IP4 127.0.0.1\r\n"
|
||||
"s=-\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=video 9 UDP/TLS/RTP/SAVPF 96\r\n"
|
||||
"a=rtpmap:96 H264/90000\r\n"
|
||||
"a=fmtp:96 profile-level-id=42e01f\r\n"
|
||||
"a=sendonly\r\n";
|
||||
"o=- 123456 654321 IN IP4 127.0.0.1\r\n"
|
||||
"s=-\r\n"
|
||||
"t=0 0\r\n"
|
||||
"m=video 9 UDP/TLS/RTP/SAVPF 96\r\n"
|
||||
"a=rtpmap:96 H264/90000\r\n"
|
||||
"a=fmtp:96 profile-level-id=42e01f\r\n"
|
||||
"a=sendonly\r\n";
|
||||
|
||||
// Parse the remote SDP
|
||||
HELPER_EXPECT_SUCCESS(ruc->remote_sdp_.parse(ruc->remote_sdp_str_));
|
||||
|
|
@ -1868,8 +1864,7 @@ VOID TEST(SrsRtcConnectionTest, OnRtpCipherTypicalScenario)
|
|||
0x56, 0x78, 0x9A, 0xBC, // timestamp
|
||||
0x12, 0x34, 0x56, 0x78, // SSRC = 0x12345678
|
||||
// RTP payload (sample data)
|
||||
0x01, 0x02, 0x03, 0x04
|
||||
};
|
||||
0x01, 0x02, 0x03, 0x04};
|
||||
|
||||
// Add the publish stream to the connection's publishers map
|
||||
string stream_url = "/live/test";
|
||||
|
|
@ -1980,8 +1975,10 @@ VOID TEST(SrsRtcPublisherNegotiatorTest, TypicalUseScenario)
|
|||
// Find audio and video media descriptions
|
||||
bool has_audio = false, has_video = false;
|
||||
for (size_t i = 0; i < local_sdp.media_descs_.size(); i++) {
|
||||
if (local_sdp.media_descs_[i].type_ == "audio") has_audio = true;
|
||||
if (local_sdp.media_descs_[i].type_ == "video") has_video = true;
|
||||
if (local_sdp.media_descs_[i].type_ == "audio")
|
||||
has_audio = true;
|
||||
if (local_sdp.media_descs_[i].type_ == "video")
|
||||
has_video = true;
|
||||
}
|
||||
EXPECT_TRUE(has_audio);
|
||||
EXPECT_TRUE(has_video);
|
||||
|
|
@ -2223,8 +2220,7 @@ VOID TEST(SrsRtcConnectionTest, OnRtpPlaintextTypicalScenario)
|
|||
0x56, 0x78, 0x9A, 0xBC, // timestamp
|
||||
0x12, 0x34, 0x56, 0x78, // SSRC = 0x12345678
|
||||
// RTP payload (sample data)
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
|
||||
};
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
||||
|
||||
// Create video track with matching SSRC for the RTP packet using helper function
|
||||
SrsUniquePtr<SrsRtcTrackDescription> video_desc(create_video_track_description_with_codec("H264", test_ssrc));
|
||||
|
|
@ -2521,8 +2517,6 @@ VOID TEST(SrsRtcConnectionTest, DoCheckSendNacksTypicalScenario)
|
|||
conn->publishers_.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
VOID TEST(SdpUtilityTest, SrsSDPHasH264ProfilePayloadTypeTypicalScenario)
|
||||
{
|
||||
// Test srs_sdp_has_h264_profile with SrsMediaPayloadType - typical scenario
|
||||
|
|
@ -2661,7 +2655,7 @@ VOID TEST(SrsRtcPlayerNegotiatorTest, TypicalUseScenario)
|
|||
SrsUniquePtr<SrsRtcSourceDescription> stream_desc(new SrsRtcSourceDescription());
|
||||
|
||||
// Create audio track description (managed by stream_desc)
|
||||
SrsRtcTrackDescription* audio_track = new SrsRtcTrackDescription();
|
||||
SrsRtcTrackDescription *audio_track = new SrsRtcTrackDescription();
|
||||
audio_track->type_ = "audio";
|
||||
audio_track->id_ = "audio_track_id";
|
||||
audio_track->ssrc_ = 12345;
|
||||
|
|
@ -2673,7 +2667,7 @@ VOID TEST(SrsRtcPlayerNegotiatorTest, TypicalUseScenario)
|
|||
audio_track->media_ = new SrsAudioPayload(111, "opus", 48000, 2);
|
||||
|
||||
// Create video track description (managed by stream_desc)
|
||||
SrsRtcTrackDescription* video_track = new SrsRtcTrackDescription();
|
||||
SrsRtcTrackDescription *video_track = new SrsRtcTrackDescription();
|
||||
video_track->type_ = "video";
|
||||
video_track->id_ = "video_track_id";
|
||||
video_track->ssrc_ = 67890;
|
||||
|
|
@ -2694,9 +2688,9 @@ VOID TEST(SrsRtcPlayerNegotiatorTest, TypicalUseScenario)
|
|||
mock_request.get(),
|
||||
local_sdp,
|
||||
stream_desc.get(),
|
||||
true, // unified_plan
|
||||
true // audio_before_video
|
||||
));
|
||||
true, // unified_plan
|
||||
true // audio_before_video
|
||||
));
|
||||
|
||||
// Verify the generated local SDP has the expected structure
|
||||
EXPECT_EQ(2, (int)local_sdp.media_descs_.size()); // Should have audio and video
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@
|
|||
#include <srs_app_rtc_conn.hpp>
|
||||
#include <srs_app_rtc_source.hpp>
|
||||
#include <srs_kernel_rtc_queue.hpp>
|
||||
#include <srs_utest_app6.hpp>
|
||||
#include <srs_utest_app2.hpp>
|
||||
#include <srs_utest_service.hpp>
|
||||
#include <srs_utest_app6.hpp>
|
||||
#include <srs_utest_kernel3.hpp>
|
||||
#include <srs_utest_service.hpp>
|
||||
|
||||
// Mock video recv track for testing check_send_nacks
|
||||
class MockRtcVideoRecvTrackForNack : public SrsRtcVideoRecvTrack
|
||||
|
|
@ -157,6 +157,4 @@ public:
|
|||
virtual ISrsRequest *as_http();
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
|||
2037
trunk/src/utest/srs_utest_app8.cpp
Normal file
2037
trunk/src/utest/srs_utest_app8.cpp
Normal file
File diff suppressed because it is too large
Load Diff
192
trunk/src/utest/srs_utest_app8.hpp
Normal file
192
trunk/src/utest/srs_utest_app8.hpp
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
//
|
||||
// Copyright (c) 2013-2025 The SRS Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
|
||||
#ifndef SRS_UTEST_APP8_HPP
|
||||
#define SRS_UTEST_APP8_HPP
|
||||
|
||||
#include <srs_utest.hpp>
|
||||
|
||||
#include <srs_app_factory.hpp>
|
||||
#include <srs_app_hls.hpp>
|
||||
#include <srs_app_rtmp_source.hpp>
|
||||
#include <srs_utest_app6.hpp>
|
||||
#include <srs_utest_kernel.hpp>
|
||||
|
||||
// Extended mock app config for HLS notify testing
|
||||
class MockAppConfigForHlsNotify : public MockAppConfig
|
||||
{
|
||||
public:
|
||||
SrsConfDirective *on_hls_notify_directive_;
|
||||
int hls_nb_notify_;
|
||||
bool hls_enabled_;
|
||||
|
||||
public:
|
||||
MockAppConfigForHlsNotify();
|
||||
virtual ~MockAppConfigForHlsNotify();
|
||||
virtual SrsConfDirective *get_vhost_on_hls_notify(std::string vhost);
|
||||
virtual int get_vhost_hls_nb_notify(std::string vhost);
|
||||
virtual bool get_hls_enabled(std::string vhost);
|
||||
void set_on_hls_notify_urls(const std::vector<std::string> &urls);
|
||||
void clear_on_hls_notify_directive();
|
||||
void set_hls_nb_notify(int nb_notify);
|
||||
void set_hls_enabled(bool enabled);
|
||||
};
|
||||
|
||||
// Extended mock HTTP hooks for HLS notify testing
|
||||
class MockHttpHooksForHlsNotify : public MockHttpHooks
|
||||
{
|
||||
public:
|
||||
std::vector<std::string> on_hls_notify_urls_;
|
||||
std::vector<std::string> on_hls_notify_ts_urls_;
|
||||
std::vector<int> on_hls_notify_nb_notifies_;
|
||||
int on_hls_notify_count_;
|
||||
srs_error_t on_hls_notify_error_;
|
||||
|
||||
public:
|
||||
MockHttpHooksForHlsNotify();
|
||||
virtual ~MockHttpHooksForHlsNotify();
|
||||
virtual srs_error_t on_hls_notify(SrsContextId cid, std::string url, ISrsRequest *req, std::string ts_url, int nb_notify);
|
||||
void set_on_hls_notify_error(srs_error_t err);
|
||||
void reset();
|
||||
};
|
||||
|
||||
// Mock request class for HLS testing
|
||||
class MockHlsRequest : public ISrsRequest
|
||||
{
|
||||
public:
|
||||
MockHlsRequest(std::string vhost = "__defaultVhost__", std::string app = "live", std::string stream = "test");
|
||||
virtual ~MockHlsRequest();
|
||||
virtual ISrsRequest *copy();
|
||||
virtual std::string get_stream_url();
|
||||
virtual void update_auth(ISrsRequest *req);
|
||||
virtual void strip();
|
||||
virtual ISrsRequest *as_http();
|
||||
};
|
||||
|
||||
// Proxy file writer that wraps MockSrsFileWriter for testing
|
||||
class MockFileWriterProxy : public ISrsFileWriter
|
||||
{
|
||||
public:
|
||||
MockSrsFileWriter *real_writer_;
|
||||
|
||||
public:
|
||||
MockFileWriterProxy(MockSrsFileWriter *writer);
|
||||
virtual ~MockFileWriterProxy();
|
||||
virtual srs_error_t open(std::string file);
|
||||
virtual srs_error_t open_append(std::string file);
|
||||
virtual void close();
|
||||
virtual bool is_open();
|
||||
virtual void seek2(int64_t offset);
|
||||
virtual int64_t tellg();
|
||||
virtual srs_error_t write(void *buf, size_t count, ssize_t *pnwrite);
|
||||
virtual srs_error_t writev(const iovec *iov, int iovcnt, ssize_t *pnwrite);
|
||||
virtual srs_error_t lseek(off_t offset, int whence, off_t *seeked);
|
||||
};
|
||||
|
||||
// Mock app factory for HLS testing
|
||||
// Mock encrypted file writer for HLS encryption testing
|
||||
class MockEncFileWriter : public SrsEncFileWriter
|
||||
{
|
||||
public:
|
||||
MockEncFileWriter();
|
||||
virtual ~MockEncFileWriter();
|
||||
};
|
||||
|
||||
class MockAppFactory : public SrsAppFactory
|
||||
{
|
||||
public:
|
||||
MockSrsFileWriter *real_writer_;
|
||||
MockSrsFile *real_file_;
|
||||
MockSrsFileReader *real_reader_;
|
||||
int create_file_writer_count_;
|
||||
int create_file_reader_count_;
|
||||
|
||||
public:
|
||||
MockAppFactory();
|
||||
virtual ~MockAppFactory();
|
||||
virtual ISrsFileWriter *create_file_writer();
|
||||
virtual ISrsFileReader *create_file_reader();
|
||||
void reset();
|
||||
};
|
||||
|
||||
// Mock HLS muxer for testing SrsHlsController::reap_segment
|
||||
class MockHlsMuxer
|
||||
{
|
||||
public:
|
||||
int segment_close_count_;
|
||||
int segment_open_count_;
|
||||
int flush_video_count_;
|
||||
int flush_audio_count_;
|
||||
srs_error_t segment_close_error_;
|
||||
srs_error_t segment_open_error_;
|
||||
srs_error_t flush_video_error_;
|
||||
srs_error_t flush_audio_error_;
|
||||
|
||||
public:
|
||||
MockHlsMuxer();
|
||||
virtual ~MockHlsMuxer();
|
||||
srs_error_t segment_close();
|
||||
srs_error_t segment_open();
|
||||
srs_error_t flush_video(SrsTsMessageCache *cache);
|
||||
srs_error_t flush_audio(SrsTsMessageCache *cache);
|
||||
void reset();
|
||||
void set_segment_close_error(srs_error_t err);
|
||||
void set_segment_open_error(srs_error_t err);
|
||||
void set_flush_video_error(srs_error_t err);
|
||||
void set_flush_audio_error(srs_error_t err);
|
||||
};
|
||||
|
||||
// Mock HLS controller for testing SrsHls::reload
|
||||
class MockHlsController : public ISrsHlsController
|
||||
{
|
||||
public:
|
||||
int initialize_count_;
|
||||
int dispose_count_;
|
||||
int on_publish_count_;
|
||||
int on_unpublish_count_;
|
||||
int write_audio_count_;
|
||||
int write_video_count_;
|
||||
int on_sequence_header_count_;
|
||||
srs_error_t initialize_error_;
|
||||
srs_error_t on_publish_error_;
|
||||
srs_error_t on_unpublish_error_;
|
||||
|
||||
public:
|
||||
MockHlsController();
|
||||
virtual ~MockHlsController();
|
||||
virtual srs_error_t initialize();
|
||||
virtual void dispose();
|
||||
virtual srs_error_t on_publish(ISrsRequest *req);
|
||||
virtual srs_error_t on_unpublish();
|
||||
virtual srs_error_t write_audio(SrsMediaPacket *shared_audio, SrsFormat *format);
|
||||
virtual srs_error_t write_video(SrsMediaPacket *shared_video, SrsFormat *format);
|
||||
virtual srs_error_t on_sequence_header(SrsMediaPacket *msg, SrsFormat *format);
|
||||
virtual int sequence_no();
|
||||
virtual std::string ts_url();
|
||||
virtual srs_utime_t duration();
|
||||
virtual int deviation();
|
||||
void reset();
|
||||
void set_initialize_error(srs_error_t err);
|
||||
void set_on_publish_error(srs_error_t err);
|
||||
void set_on_unpublish_error(srs_error_t err);
|
||||
};
|
||||
|
||||
// Mock origin hub for testing SrsHls::reload
|
||||
class MockOriginHub : public SrsOriginHub
|
||||
{
|
||||
public:
|
||||
int on_hls_request_sh_count_;
|
||||
srs_error_t on_hls_request_sh_error_;
|
||||
|
||||
public:
|
||||
MockOriginHub();
|
||||
virtual ~MockOriginHub();
|
||||
virtual srs_error_t on_hls_request_sh();
|
||||
void reset();
|
||||
void set_on_hls_request_sh_error(srs_error_t err);
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user