From 35f3a932392fd13c8df5c4a6f11744fc377f0360 Mon Sep 17 00:00:00 2001 From: winlin Date: Thu, 24 Apr 2014 12:22:36 +0800 Subject: [PATCH] support dvr to hss. change to 0.9.74 --- trunk/src/app/srs_app_config.hpp | 2 + trunk/src/app/srs_app_dvr.cpp | 154 ++++++++++++++++++++++++--- trunk/src/app/srs_app_dvr.hpp | 46 ++++++-- trunk/src/app/srs_app_http_hooks.cpp | 5 +- trunk/src/core/srs_core.hpp | 2 +- 5 files changed, 185 insertions(+), 24 deletions(-) diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 5754226e9..5fecd9d75 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -47,6 +47,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html" #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session" #define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment" +// chnvideo hss +#define SRS_CONF_DEFAULT_DVR_PLAN_HSS "hss" #define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION #define SRS_CONF_DEFAULT_DVR_DURATION 30 // in ms, for HLS aac sync time. diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 34d21218f..b904245f3 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -299,18 +299,20 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s SrsFlvSegment::SrsFlvSegment() { - current_flv_path = ""; - segment_has_keyframe = false; + path = ""; + has_keyframe = false; duration = 0; starttime = -1; stream_starttime = 0; + stream_previous_pkt_time = -1; + stream_duration = 0; } void SrsFlvSegment::reset() { - segment_has_keyframe = false; - duration = 0; + has_keyframe = false; starttime = -1; + duration = 0; } SrsDvrPlan::SrsDvrPlan() @@ -357,7 +359,7 @@ int SrsDvrPlan::on_publish() return ret; } - // jitter. + // jitter when publish, ensure whole stream start from 0. srs_freep(jitter); jitter = new SrsRtmpJitter(); @@ -365,7 +367,9 @@ int SrsDvrPlan::on_publish() srs_update_system_time_ms(); // when republish, stream starting. + segment->stream_previous_pkt_time = -1; segment->stream_starttime = srs_get_system_time_ms(); + segment->stream_duration = 0; if ((ret = open_new_segment()) != ERROR_SUCCESS) { return ret; @@ -470,7 +474,7 @@ int SrsDvrPlan::on_video(SrsSharedPtrMessage* video) #ifdef SRS_AUTO_HTTP_CALLBACK bool is_key_frame = SrsCodec::video_is_keyframe((int8_t*)payload, size); if (is_key_frame) { - segment->segment_has_keyframe = true; + segment->has_keyframe = true; } srs_verbose("dvr video is key: %d", is_key_frame); #endif @@ -504,7 +508,7 @@ int SrsDvrPlan::flv_open(string stream, string path) return ret; } - segment->current_flv_path = path; + segment->path = path; srs_trace("dvr stream %s to file %s", stream.c_str(), path.c_str()); return ret; @@ -518,16 +522,16 @@ int SrsDvrPlan::flv_close() return ret; } - std::string tmp_file = segment->current_flv_path + ".tmp"; - if (rename(tmp_file.c_str(), segment->current_flv_path.c_str()) < 0) { + std::string tmp_file = segment->path + ".tmp"; + if (rename(tmp_file.c_str(), segment->path.c_str()) < 0) { ret = ERROR_SYSTEM_FILE_RENAME; srs_error("rename flv file failed, %s => %s. ret=%d", - tmp_file.c_str(), segment->current_flv_path.c_str(), ret); + tmp_file.c_str(), segment->path.c_str(), ret); return ret; } #ifdef SRS_AUTO_HTTP_CALLBACK - if (segment->segment_has_keyframe) { + if (segment->has_keyframe) { if ((ret = on_dvr_keyframe()) != ERROR_SUCCESS) { return ret; } @@ -540,13 +544,26 @@ int SrsDvrPlan::flv_close() int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg) { int ret = ERROR_SUCCESS; + + // we must assumpt that the stream timestamp is monotonically increase, + // that is, always use time jitter to correct the timestamp. - // foreach msg, collect the duration. - if (segment->starttime < 0 || segment->starttime > msg->header.timestamp) { + // set the segment starttime at first time + if (segment->starttime < 0) { segment->starttime = msg->header.timestamp; } - segment->duration += msg->header.timestamp - segment->starttime; - segment->starttime = msg->header.timestamp; + + // no previous packet or timestamp overflow. + if (segment->stream_previous_pkt_time < 0 || segment->stream_previous_pkt_time > msg->header.timestamp) { + segment->stream_previous_pkt_time = msg->header.timestamp; + } + + // collect segment and stream duration, timestamp overflow is ok. + segment->duration += msg->header.timestamp - segment->stream_previous_pkt_time; + segment->stream_duration += msg->header.timestamp - segment->stream_previous_pkt_time; + + // update previous packet time + segment->stream_previous_pkt_time = msg->header.timestamp; return ret; } @@ -579,6 +596,8 @@ SrsDvrPlan* SrsDvrPlan::create_plan(string vhost) return new SrsDvrSegmentPlan(); } else if (plan == SRS_CONF_DEFAULT_DVR_PLAN_SESSION) { return new SrsDvrSessionPlan(); + } else if (plan == SRS_CONF_DEFAULT_DVR_PLAN_HSS) { + return new SrsDvrHssPlan(); } else { return new SrsDvrSessionPlan(); } @@ -683,6 +702,111 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) return ret; } +SrsDvrHssPlan::SrsDvrHssPlan() +{ + segment_duration = -1; + start_deviation = 0; + expect_reap_time = 0; +} + +SrsDvrHssPlan::~SrsDvrHssPlan() +{ +} + +int SrsDvrHssPlan::initialize(SrsSource* source, SrsRequest* req) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsDvrPlan::initialize(source, req)) != ERROR_SUCCESS) { + return ret; + } + + // TODO: FIXME: support reload + segment_duration = _srs_config->get_dvr_duration(req->vhost); + // to ms + segment_duration *= 1000; + + return ret; +} + +int SrsDvrHssPlan::on_publish() +{ + int ret = ERROR_SUCCESS; + + // if already opened, continue to dvr. + // the segment plan maybe keep running longer than the encoder. + // for example, segment running, encoder restart, + // the segment plan will just continue going and donot open new segment. + if (fs->is_open()) { + dvr_enabled = true; + return ret; + } + + if ((ret = SrsDvrPlan::on_publish()) != ERROR_SUCCESS) { + return ret; + } + + // expect reap flv time + expect_reap_time = segment->stream_starttime + segment_duration; + // the start deviation used ensure the segment starttime in nature clock. + start_deviation = segment->stream_starttime % 1000; + + return ret; +} + +void SrsDvrHssPlan::on_unpublish() +{ + // support multiple publish. + if (!dvr_enabled) { + return; + } + dvr_enabled = false; +} + +int SrsDvrHssPlan::update_duration(SrsSharedPtrMessage* msg) +{ + int ret = ERROR_SUCCESS; + + if ((ret = SrsDvrPlan::update_duration(msg)) != ERROR_SUCCESS) { + return ret; + } + + srs_assert(segment); + + // if not initialized, ignore reap. + if (expect_reap_time <= 0 + || segment->stream_starttime <= 0 + || segment->stream_duration <= 0 + ) { + return ret; + } + + // reap if exceed atc expect time. + if (segment->stream_starttime + segment->stream_duration > expect_reap_time) { + srs_warn("hss reap start=%"PRId64", duration=%"PRId64", expect=%"PRId64 + ", segment(start=%"PRId64", adjust=%"PRId64", duration=%"PRId64", file=%s", + segment->stream_starttime, segment->stream_duration, expect_reap_time, + segment->stream_starttime + segment->starttime, + segment->stream_starttime + segment->starttime - start_deviation, + segment->duration, segment->path.c_str()); + + // update expect reap time + expect_reap_time += segment_duration; + + if ((ret = flv_close()) != ERROR_SUCCESS) { + segment->reset(); + return ret; + } + on_unpublish(); + + if ((ret = open_new_segment()) != ERROR_SUCCESS) { + return ret; + } + } + + return ret; +} + SrsDvr::SrsDvr(SrsSource* source) { _source = source; diff --git a/trunk/src/app/srs_app_dvr.hpp b/trunk/src/app/srs_app_dvr.hpp index 66615bcd4..be521d583 100644 --- a/trunk/src/app/srs_app_dvr.hpp +++ b/trunk/src/app/srs_app_dvr.hpp @@ -114,22 +114,34 @@ class SrsFlvSegment { public: /** - * current flv file path. + * current segment flv file path. */ - std::string current_flv_path; + std::string path; /** * whether current segment has keyframe. */ - bool segment_has_keyframe; + bool has_keyframe; /** - * current segment duration and starttime. + * current segment starttime, RTMP pkt time. */ - int64_t duration; int64_t starttime; /** - * stream start time, to generate atc pts. + * current segment duration + */ + int64_t duration; + /** + * stream start time, to generate atc pts. abs time. */ int64_t stream_starttime; + /** + * stream duration, to generate atc segment. + */ + int64_t stream_duration; + /** + * previous stream RTMP pkt time, used to calc the duration. + * for the RTMP timestamp will overflow. + */ + int64_t stream_previous_pkt_time; public: SrsFlvSegment(); virtual void reset(); @@ -213,6 +225,28 @@ private: virtual int update_duration(SrsSharedPtrMessage* msg); }; +/** +* hss plan: use atc time to reap flv segment +*/ +class SrsDvrHssPlan : public SrsDvrPlan +{ +private: + // in config, in ms + int segment_duration; + // the deviation of starttime of the nature clock time. + int start_deviation; + int64_t expect_reap_time; +public: + SrsDvrHssPlan(); + virtual ~SrsDvrHssPlan(); +public: + virtual int initialize(SrsSource* source, SrsRequest* req); + virtual int on_publish(); + virtual void on_unpublish(); +private: + virtual int update_duration(SrsSharedPtrMessage* msg); +}; + /** * dvr(digital video recorder) to record RTMP stream to flv file. * TODO: FIXME: add utest for it. diff --git a/trunk/src/app/srs_app_http_hooks.cpp b/trunk/src/app/srs_app_http_hooks.cpp index 4d327bd7f..e964e30fe 100644 --- a/trunk/src/app/srs_app_http_hooks.cpp +++ b/trunk/src/app/srs_app_http_hooks.cpp @@ -462,10 +462,11 @@ void SrsHttpHooks::on_dvr_keyframe(string url, SrsRequest* req, SrsFlvSegment* s { int ret = ERROR_SUCCESS; + srs_assert(segment); srs_trace("flv segment %s, atc_start=%"PRId64", " "has_key=%d, starttime=%"PRId64", duration=%d", - segment->current_flv_path.c_str(), segment->stream_starttime, - segment->segment_has_keyframe, segment->starttime, (int)segment->duration); + segment->path.c_str(), segment->stream_starttime, + segment->has_keyframe, segment->starttime, (int)segment->duration); SrsHttpUri uri; if ((ret = uri.initialize(url)) != ERROR_SUCCESS) { diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 0e100b24f..5952a86f3 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "0" #define VERSION_MINOR "9" -#define VERSION_REVISION "73" +#define VERSION_REVISION "74" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "srs"