1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

enhanced hls, support deviation for duration. 2.0.151.

This commit is contained in:
winlin 2015-03-31 15:39:47 +08:00
parent cd682ae4be
commit f6e135943f
7 changed files with 127 additions and 18 deletions

View file

@ -562,6 +562,7 @@ Supported operating systems and hardware:
### SRS 2.0 history ### SRS 2.0 history
* v2.0, 2015-03-31, enhanced hls, support deviation for duration. 2.0.151.
* v2.0, 2015-03-30, for [#351](https://github.com/winlinvip/simple-rtmp-server/issues/351), support config the m3u8/ts path for hls. 2.0.149. * v2.0, 2015-03-30, for [#351](https://github.com/winlinvip/simple-rtmp-server/issues/351), support config the m3u8/ts path for hls. 2.0.149.
* v2.0, 2015-03-17, for [#155](https://github.com/winlinvip/simple-rtmp-server/issues/155), osx(darwin) support demo with nginx and ffmpeg. 2.0.143. * v2.0, 2015-03-17, for [#155](https://github.com/winlinvip/simple-rtmp-server/issues/155), osx(darwin) support demo with nginx and ffmpeg. 2.0.143.
* v2.0, 2015-03-15, start [2.0release branch](https://github.com/winlinvip/simple-rtmp-server/tree/2.0release), 80773 lines. * v2.0, 2015-03-15, start [2.0release branch](https://github.com/winlinvip/simple-rtmp-server/tree/2.0release), 80773 lines.

View file

@ -560,6 +560,11 @@ vhost with-hls.srs.com {
# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DeliveryHLS#hls-config # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DeliveryHLS#hls-config
# default: [app]/[stream]-[seq].ts # default: [app]/[stream]-[seq].ts
hls_ts_file [app]/[stream]-[seq].ts; hls_ts_file [app]/[stream]-[seq].ts;
# whether use floor for the hls_ts_file path generation.
# if on, use floor(timestamp/hls_fragment) as the variable [timestamp],
# and use enahanced algorithm to calc deviation for segment.
# default: off
hls_ts_floor off;
# the hls entry prefix, which is base url of ts url. # the hls entry prefix, which is base url of ts url.
# if specified, the ts path in m3u8 will be like: # if specified, the ts path in m3u8 will be like:
# http://your-server/live/livestream-0.ts # http://your-server/live/livestream-0.ts
@ -588,6 +593,11 @@ vhost with-hls.srs.com {
# h264, vn # h264, vn
# default: h264 # default: h264
hls_vcodec h264; hls_vcodec h264;
# on_hls, never config in here, should config in http_hooks.
# for the hls http callback, @see http_hooks.on_hls of vhost hooks.callback.srs.com
# @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DeliveryHLS#http-callback
# @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DeliveryHLS#http-callback
} }
} }
# the vhost with hls disabled. # the vhost with hls disabled.
@ -722,6 +732,20 @@ vhost hooks.callback.srs.com {
# an int value specifies the error code(0 corresponding to success): # an int value specifies the error code(0 corresponding to success):
# 0 # 0
on_dvr http://127.0.0.1:8085/api/v1/dvrs http://localhost:8085/api/v1/dvrs; on_dvr http://127.0.0.1:8085/api/v1/dvrs http://localhost:8085/api/v1/dvrs;
# when srs reap a ts file of hls, call the hook,
# the request in the POST data string is a object encode by json:
# {
# "action": "on_hls",
# "client_id": 1985,
# "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
# "stream": "livestream",
# "cwd": "/usr/local/srs",
# "file": "./objs/nginx/html/live/livestream.1420254068776-100.ts"
# }
# if valid, the hook must return HTTP code 200(Stauts OK) and response
# an int value specifies the error code(0 corresponding to success):
# 0
on_hls http://127.0.0.1:8085/api/v1/hls http://localhost:8085/api/v1/hls;
} }
} }

View file

@ -1482,7 +1482,7 @@ int SrsConfig::check_config()
string m = conf->at(j)->name.c_str(); string m = conf->at(j)->name.c_str();
if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error" if (m != "enabled" && m != "hls_entry_prefix" && m != "hls_path" && m != "hls_fragment" && m != "hls_window" && m != "hls_on_error"
&& m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec" && m != "hls_storage" && m != "hls_mount" && m != "hls_td_ratio" && m != "hls_aof_ratio" && m != "hls_acodec" && m != "hls_vcodec"
&& m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_m3u8_file" && m != "hls_ts_file" && m != "hls_ts_floor"
) { ) {
ret = ERROR_SYSTEM_CONFIG_INVALID; ret = ERROR_SYSTEM_CONFIG_INVALID;
srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret); srs_error("unsupported vhost hls directive %s, ret=%d", m.c_str(), ret);
@ -3207,6 +3207,23 @@ string SrsConfig::get_hls_ts_file(string vhost)
return conf->arg0(); return conf->arg0();
} }
bool SrsConfig::get_hls_ts_floor(string vhost)
{
SrsConfDirective* hls = get_hls(vhost);
if (!hls) {
return SRS_CONF_DEFAULT_HLS_TS_FLOOR;
}
SrsConfDirective* conf = hls->get("hls_ts_floor");
if (!conf || conf->arg0() != "on") {
return SRS_CONF_DEFAULT_HLS_TS_FLOOR;
}
return true;
}
double SrsConfig::get_hls_fragment(string vhost) double SrsConfig::get_hls_fragment(string vhost)
{ {
SrsConfDirective* hls = get_hls(vhost); SrsConfDirective* hls = get_hls(vhost);

View file

@ -48,6 +48,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html" #define SRS_CONF_DEFAULT_HLS_PATH "./objs/nginx/html"
#define SRS_CONF_DEFAULT_HLS_M3U8_FILE "[app]/[stream].m3u8" #define SRS_CONF_DEFAULT_HLS_M3U8_FILE "[app]/[stream].m3u8"
#define SRS_CONF_DEFAULT_HLS_TS_FILE "[app]/[stream]-[seq].ts" #define SRS_CONF_DEFAULT_HLS_TS_FILE "[app]/[stream]-[seq].ts"
#define SRS_CONF_DEFAULT_HLS_TS_FLOOR "off"
#define SRS_CONF_DEFAULT_HLS_FRAGMENT 10 #define SRS_CONF_DEFAULT_HLS_FRAGMENT 10
#define SRS_CONF_DEFAULT_HLS_TD_RATIO 1.5 #define SRS_CONF_DEFAULT_HLS_TD_RATIO 1.5
#define SRS_CONF_DEFAULT_HLS_AOF_RATIO 2.0 #define SRS_CONF_DEFAULT_HLS_AOF_RATIO 2.0
@ -884,6 +885,10 @@ public:
* get the HLS ts file path template. * get the HLS ts file path template.
*/ */
virtual std::string get_hls_ts_file(std::string vhost); virtual std::string get_hls_ts_file(std::string vhost);
/**
* whether enable the floor(timestamp/hls_fragment) for variable timestamp.
*/
virtual bool get_hls_ts_floor(std::string vhost);
/** /**
* get the hls fragment time, in seconds. * get the hls fragment time, in seconds.
*/ */

View file

@ -58,6 +58,11 @@ using namespace std;
// drop the segment when duration of ts too small. // drop the segment when duration of ts too small.
#define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100 #define SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS 100
// startup piece, the first piece, fragment percent to reap.
#define SRS_HLS_FLOOR_STARTUP_PERCENT 0.1
// fragment plus the deviation percent.
#define SRS_HLS_FLOOR_REAP_PERCENT 0.9
ISrsHlsHandler::ISrsHlsHandler() ISrsHlsHandler::ISrsHlsHandler()
{ {
} }
@ -170,6 +175,8 @@ SrsHlsMuxer::SrsHlsMuxer()
handler = NULL; handler = NULL;
hls_fragment = hls_window = 0; hls_fragment = hls_window = 0;
hls_aof_ratio = 1.0; hls_aof_ratio = 1.0;
hls_fragment_deviation = 0;
hls_ts_floor = false;
target_duration = 0; target_duration = 0;
_sequence_no = 0; _sequence_no = 0;
current = NULL; current = NULL;
@ -205,8 +212,24 @@ int SrsHlsMuxer::sequence_no()
return _sequence_no; return _sequence_no;
} }
string SrsHlsMuxer::ts_url()
{
return current? current->uri:"";
}
double SrsHlsMuxer::duration()
{
return current? current->duration:0;
}
double SrsHlsMuxer::deviation()
{
return hls_fragment_deviation;
}
int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix, int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
string path, string m3u8_file, string ts_file, int fragment, int window, double aof_ratio string path, string m3u8_file, string ts_file, double fragment, double window,
bool ts_floor, double aof_ratio
) { ) {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
@ -218,7 +241,11 @@ int SrsHlsMuxer::update_config(SrsRequest* r, string entry_prefix,
hls_ts_file = ts_file; hls_ts_file = ts_file;
hls_fragment = fragment; hls_fragment = fragment;
hls_aof_ratio = aof_ratio; hls_aof_ratio = aof_ratio;
hls_ts_floor = ts_floor;
hls_window = window; hls_window = window;
// for the first time, we set to -N% of fragment,
// that is, the first piece always smaller.
hls_fragment_deviation = -1 * (fragment * SRS_HLS_FLOOR_STARTUP_PERCENT);
// generate the m3u8 dir and path. // generate the m3u8 dir and path.
m3u8 = path + "/" + m3u8_file; m3u8 = path + "/" + m3u8_file;
@ -301,6 +328,11 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
// generate filename. // generate filename.
std::string ts_file = hls_ts_file; std::string ts_file = hls_ts_file;
ts_file = srs_path_build_stream(ts_file, req->vhost, req->app, req->stream); ts_file = srs_path_build_stream(ts_file, req->vhost, req->app, req->stream);
if (hls_ts_floor) {
std::stringstream ts_floor;
ts_floor << (int64_t)(srs_get_system_time_ms() / (1000 * hls_fragment));
ts_file = srs_string_replace(ts_file, "[timestamp]", ts_floor.str());
}
ts_file = srs_path_build_timestamp(ts_file); ts_file = srs_path_build_timestamp(ts_file);
if (true) { if (true) {
std::stringstream ss; std::stringstream ss;
@ -308,6 +340,7 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
ts_file = srs_string_replace(ts_file, "[seq]", ss.str()); ts_file = srs_string_replace(ts_file, "[seq]", ss.str());
} }
current->full_path = hls_path + "/" + ts_file; current->full_path = hls_path + "/" + ts_file;
srs_info("hls: generate ts path %s, tmpl=%s, floor=%d", ts_file.c_str(), hls_ts_file.c_str(), hls_ts_floor);
// the ts url, relative or absolute url. // the ts url, relative or absolute url.
std::string ts_url = current->full_path; std::string ts_url = current->full_path;
@ -363,14 +396,22 @@ int SrsHlsMuxer::on_sequence_header()
bool SrsHlsMuxer::is_segment_overflow() bool SrsHlsMuxer::is_segment_overflow()
{ {
srs_assert(current); srs_assert(current);
return current->duration >= hls_fragment;
// use N% deviation, to smoother.
double deviation = hls_ts_floor? SRS_HLS_FLOOR_REAP_PERCENT * hls_fragment_deviation : 0.0;
return current->duration >= hls_fragment + deviation;
} }
bool SrsHlsMuxer::is_segment_absolutely_overflow() bool SrsHlsMuxer::is_segment_absolutely_overflow()
{ {
// @see https://github.com/winlinvip/simple-rtmp-server/issues/151#issuecomment-83553950 // @see https://github.com/winlinvip/simple-rtmp-server/issues/151#issuecomment-83553950
srs_assert(current); srs_assert(current);
return current->duration >= hls_aof_ratio * hls_fragment;
// use N% deviation, to smoother.
double deviation = hls_ts_floor? SRS_HLS_FLOOR_REAP_PERCENT * hls_fragment_deviation : 0.0;
return current->duration >= hls_aof_ratio * (hls_fragment + deviation);
} }
int SrsHlsMuxer::update_acodec(SrsCodecAudio ac) int SrsHlsMuxer::update_acodec(SrsCodecAudio ac)
@ -458,9 +499,14 @@ int SrsHlsMuxer::segment_close(string log_desc)
if (current->duration * 1000 >= SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) { if (current->duration * 1000 >= SRS_AUTO_HLS_SEGMENT_MIN_DURATION_MS) {
segments.push_back(current); segments.push_back(current);
srs_info("%s reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64"", // when reap ts, adjust the deviation.
if (hls_ts_floor) {
hls_fragment_deviation += (double)(hls_fragment - current->duration);
}
srs_info("%s reap ts segment, sequence_no=%d, uri=%s, duration=%.2f, start=%"PRId64", deviation=%.2f",
log_desc.c_str(), current->sequence_no, current->uri.c_str(), current->duration, log_desc.c_str(), current->sequence_no, current->uri.c_str(), current->duration,
current->segment_start_dts); current->segment_start_dts, hls_fragment_deviation);
// notify handler for update ts. // notify handler for update ts.
srs_assert(current->writer); srs_assert(current->writer);
@ -505,7 +551,7 @@ int SrsHlsMuxer::segment_close(string log_desc)
// shrink the segments. // shrink the segments.
double duration = 0; double duration = 0;
int remove_index = -1; int remove_index = -1;
for (int i = segments.size() - 1; i >= 0; i--) { for (int i = (int)segments.size() - 1; i >= 0; i--) {
SrsHlsSegment* segment = segments[i]; SrsHlsSegment* segment = segments[i];
duration += segment->duration; duration += segment->duration;
@ -662,8 +708,8 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment
std::string stream = req->stream; std::string stream = req->stream;
std::string app = req->app; std::string app = req->app;
int hls_fragment = (int)_srs_config->get_hls_fragment(vhost); double hls_fragment = _srs_config->get_hls_fragment(vhost);
int hls_window = (int)_srs_config->get_hls_window(vhost); double hls_window = _srs_config->get_hls_window(vhost);
// get the hls m3u8 ts list entry prefix config // get the hls m3u8 ts list entry prefix config
std::string entry_prefix = _srs_config->get_hls_entry_prefix(vhost); std::string entry_prefix = _srs_config->get_hls_entry_prefix(vhost);
@ -673,12 +719,16 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment
std::string ts_file = _srs_config->get_hls_ts_file(vhost); std::string ts_file = _srs_config->get_hls_ts_file(vhost);
// the audio overflow, for pure audio to reap segment. // the audio overflow, for pure audio to reap segment.
double hls_aof_ratio = _srs_config->get_hls_aof_ratio(vhost); double hls_aof_ratio = _srs_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);
// TODO: FIXME: support load exists m3u8, to continue publish stream. // TODO: FIXME: support load exists m3u8, to continue publish stream.
// for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase. // for the HLS donot requires the EXT-X-MEDIA-SEQUENCE be monotonically increase.
// open muxer // open muxer
if ((ret = muxer->update_config(req, entry_prefix, path, m3u8_file, ts_file, hls_fragment, hls_window, hls_aof_ratio)) != ERROR_SUCCESS) { if ((ret = muxer->update_config(req, entry_prefix,
path, m3u8_file, ts_file, hls_fragment, hls_window, ts_floor, hls_aof_ratio)) != ERROR_SUCCESS
) {
srs_error("m3u8 muxer update config failed. ret=%d", ret); srs_error("m3u8 muxer update config failed. ret=%d", ret);
return ret; return ret;
} }
@ -687,6 +737,9 @@ int SrsHlsCache::on_publish(SrsHlsMuxer* muxer, SrsRequest* req, int64_t segment
srs_error("m3u8 muxer open segment failed. ret=%d", ret); srs_error("m3u8 muxer open segment failed. ret=%d", ret);
return ret; return ret;
} }
srs_trace("hls: win=%.2f, frag=%.2f, prefix=%s, path=%s, m3u8=%s, ts=%s, aof=%.2f, floor=%d",
hls_window, hls_fragment, entry_prefix.c_str(), path.c_str(), m3u8_file.c_str(),
ts_file.c_str(), hls_aof_ratio, ts_floor);
return ret; return ret;
} }
@ -1057,9 +1110,9 @@ void SrsHls::hls_show_mux_log()
// the run time is not equals to stream time, // the run time is not equals to stream time,
// @see: https://github.com/winlinvip/simple-rtmp-server/issues/81#issuecomment-48100994 // @see: https://github.com/winlinvip/simple-rtmp-server/issues/81#issuecomment-48100994
// it's ok. // it's ok.
srs_trace("-> "SRS_CONSTS_LOG_HLS srs_trace("-> "SRS_CONSTS_LOG_HLS" time=%"PRId64", stream dts=%"PRId64"(%"PRId64"ms), sequence_no=%d, ts=%s, duration=%.2f, deviation=%.2f",
" time=%"PRId64", stream dts=%"PRId64"(%"PRId64"ms), sequence_no=%d", pprint->age(), stream_dts, stream_dts / 90, muxer->sequence_no(), muxer->ts_url().c_str(),
pprint->age(), stream_dts, stream_dts / 90, muxer->sequence_no()); muxer->duration(), muxer->deviation());
} }
} }

View file

@ -172,8 +172,14 @@ private:
std::string hls_ts_file; std::string hls_ts_file;
std::string m3u8_dir; std::string m3u8_dir;
double hls_aof_ratio; double hls_aof_ratio;
int hls_fragment; double hls_fragment;
int hls_window; double hls_window;
private:
// whether use floor algorithm for timestamp.
bool hls_ts_floor;
// the deviation in seconds to adjust the fragment to be more
// bigger or smaller.
double hls_fragment_deviation;
private: private:
int _sequence_no; int _sequence_no;
int target_duration; int target_duration;
@ -203,6 +209,9 @@ public:
virtual ~SrsHlsMuxer(); virtual ~SrsHlsMuxer();
public: public:
virtual int sequence_no(); virtual int sequence_no();
virtual std::string ts_url();
virtual double duration();
virtual double deviation();
public: public:
/** /**
* initialize the hls muxer. * initialize the hls muxer.
@ -213,7 +222,7 @@ public:
*/ */
virtual int update_config(SrsRequest* r, std::string entry_prefix, virtual int update_config(SrsRequest* r, std::string entry_prefix,
std::string path, std::string m3u8_file, std::string ts_file, std::string path, std::string m3u8_file, std::string ts_file,
int fragment, int window, double aof_ratio); double fragment, double window, bool ts_floor, double aof_ratio);
/** /**
* open a new segment(a new ts file), * open a new segment(a new ts file),
* @param segment_start_dts use to calc the segment duration, * @param segment_start_dts use to calc the segment duration,

View file

@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// current release version // current release version
#define VERSION_MAJOR 2 #define VERSION_MAJOR 2
#define VERSION_MINOR 0 #define VERSION_MINOR 0
#define VERSION_REVISION 149 #define VERSION_REVISION 151
// server info. // server info.
#define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_KEY "SRS"