From 8d679a6f9ac99637a6048b6d6e4317ecd558a07c Mon Sep 17 00:00:00 2001 From: winlin Date: Sat, 18 Mar 2017 21:41:01 +0800 Subject: [PATCH] For #299, use fragment for dvr FLV/MP4 --- trunk/src/app/srs_app_dvr.cpp | 61 ++++++++++-------------------- trunk/src/app/srs_app_dvr.hpp | 20 +++------- trunk/src/app/srs_app_fragment.cpp | 16 ++++++++ trunk/src/app/srs_app_fragment.hpp | 2 + trunk/src/app/srs_app_hls.cpp | 5 +-- 5 files changed, 44 insertions(+), 60 deletions(-) diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index e1111c224..04d4bcefc 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -41,16 +41,15 @@ using namespace std; #include #include #include +#include SrsDvrSegmenter::SrsDvrSegmenter() { req = NULL; jitter = NULL; plan = NULL; - duration = 0; - stream_previous_pkt_time = -1; - path = ""; + fragment = new SrsFragment(); fs = new SrsFileWriter(); jitter_algorithm = SrsRtmpJitterAlgorithmOFF; @@ -61,6 +60,7 @@ SrsDvrSegmenter::~SrsDvrSegmenter() { _srs_config->unsubscribe(this); + srs_freep(fragment); srs_freep(jitter); srs_freep(fs); } @@ -78,14 +78,9 @@ int SrsDvrSegmenter::initialize(SrsDvrPlan* p, SrsRequest* r) return ret; } -string SrsDvrSegmenter::get_path() +SrsFragment* SrsDvrSegmenter::current() { - return path; -} - -int64_t SrsDvrSegmenter::get_duration() -{ - return duration; + return fragment; } int SrsDvrSegmenter::open() @@ -97,32 +92,25 @@ int SrsDvrSegmenter::open() return ret; } - path = generate_path(); + string path = generate_path(); if (srs_path_exists(path)) { ret = ERROR_DVR_CANNOT_APPEND; srs_error("DVR can't append to exists path=%s. ret=%d", path.c_str(), ret); return ret; } - - tmp_dvr_file = path + ".tmp"; + fragment->set_path(path); // create dir first. - std::string dir = srs_path_dirname(path); - if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) { - srs_error("create dir=%s failed. ret=%d", dir.c_str(), ret); + if ((ret = fragment->create_dir()) != ERROR_SUCCESS) { return ret; } - srs_info("create dir=%s ok", dir.c_str()); // create jitter. srs_freep(jitter); jitter = new SrsRtmpJitter(); - duration = 0; - - // fresh stream starting. - stream_previous_pkt_time = -1; // open file writer, in append or create mode. + string tmp_dvr_file = fragment->tmppath(); if ((ret = fs->open(tmp_dvr_file)) != ERROR_SUCCESS) { srs_error("open file stream for file %s failed. ret=%d", path.c_str(), ret); return ret; @@ -205,13 +193,8 @@ int SrsDvrSegmenter::close() fs->close(); // when tmp flv file exists, reap it. - if (tmp_dvr_file != path) { - if (rename(tmp_dvr_file.c_str(), path.c_str()) < 0) { - ret = ERROR_SYSTEM_FILE_RENAME; - srs_error("rename flv file failed, %s => %s. ret=%d", - tmp_dvr_file.c_str(), path.c_str(), ret); - return ret; - } + if ((ret = fragment->rename()) != ERROR_SUCCESS) { + return ret; } // TODO: FIXME: the http callback is async, which will trigger thread switch, @@ -228,16 +211,7 @@ int SrsDvrSegmenter::on_update_duration(SrsSharedPtrMessage* msg) { int ret = ERROR_SUCCESS; - // no previous packet or timestamp overflow. - if (stream_previous_pkt_time < 0 || stream_previous_pkt_time > msg->timestamp) { - stream_previous_pkt_time = msg->timestamp; - } - - // collect segment and stream duration, timestamp overflow is ok. - duration += msg->timestamp - stream_previous_pkt_time; - - // update previous packet time - stream_previous_pkt_time = msg->timestamp; + fragment->append(msg->timestamp); return ret; } @@ -326,7 +300,7 @@ int SrsDvrFlvSegmenter::refresh_metadata() } // duration to buf - SrsAmf0Any* dur = SrsAmf0Any::number((double)duration / 1000.0); + SrsAmf0Any* dur = SrsAmf0Any::number((double)fragment->duration() / 1000.0); SrsAutoFree(SrsAmf0Any, dur); stream.skip(-1 * stream.pos()); @@ -747,7 +721,11 @@ int SrsDvrPlan::on_reap_segment() int ret = ERROR_SUCCESS; int cid = _srs_context->get_id(); - if ((ret = async->execute(new SrsDvrAsyncCallOnDvr(cid, req, segment->get_path()))) != ERROR_SUCCESS) { + + SrsFragment* fragment = segment->current(); + string fullpath = fragment->fullpath(); + + if ((ret = async->execute(new SrsDvrAsyncCallOnDvr(cid, req, fullpath))) != ERROR_SUCCESS) { return ret; } @@ -916,7 +894,8 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) srs_assert(segment); // ignore if duration ok. - if (cduration <= 0 || segment->get_duration() < cduration) { + SrsFragment* fragment = segment->current(); + if (cduration <= 0 || fragment->duration() < cduration) { return ret; } diff --git a/trunk/src/app/srs_app_dvr.hpp b/trunk/src/app/srs_app_dvr.hpp index f821724ce..211cf48c7 100644 --- a/trunk/src/app/srs_app_dvr.hpp +++ b/trunk/src/app/srs_app_dvr.hpp @@ -45,6 +45,7 @@ class SrsJsonAny; class SrsJsonObject; class SrsThread; class SrsMp4Encoder; +class SrsFragment; #include #include @@ -60,33 +61,22 @@ protected: SrsFileWriter* fs; // Whether wait keyframe to reap segment. bool wait_keyframe; - // The duration in ms of current segment. - int64_t duration; -private: - // The path of current segment flv file path. - std::string path; - std::string tmp_dvr_file; + // The FLV/MP4 fragment file. + SrsFragment* fragment; private: SrsRequest* req; SrsDvrPlan* plan; private: SrsRtmpJitter* jitter; SrsRtmpJitterAlgorithm jitter_algorithm; -private: - // The previous stream RTMP pkt time in ms, used to calc the duration. - // for the RTMP timestamp will overflow. - // TODO: FIXME: Use utility object to calc it. - int64_t stream_previous_pkt_time; public: SrsDvrSegmenter(); virtual ~SrsDvrSegmenter(); public: // Initialize the segment. virtual int initialize(SrsDvrPlan* p, SrsRequest* r); - // Get the current dvr path. - virtual std::string get_path(); - // Get the duration in ms of segment. - virtual int64_t get_duration(); + // Get the current framgnet. + virtual SrsFragment* current(); // Open new segment file. // @param use_tmp_file Whether use tmp file for DVR, and rename when close. // @remark Ignore when file is already open. diff --git a/trunk/src/app/srs_app_fragment.cpp b/trunk/src/app/srs_app_fragment.cpp index b2225709a..833033be8 100644 --- a/trunk/src/app/srs_app_fragment.cpp +++ b/trunk/src/app/srs_app_fragment.cpp @@ -90,6 +90,22 @@ int SrsFragment::unlink_file() return ret; } +int SrsFragment::create_dir() +{ + int ret = ERROR_SUCCESS; + + std::string segment_dir = srs_path_dirname(filepath); + + if ((ret = srs_create_dir_recursively(segment_dir)) != ERROR_SUCCESS) { + srs_error("Create dir %s failed. ret=%d", segment_dir.c_str(), ret); + return ret; + } + + srs_info("Create dir %s ok", segment_dir.c_str()); + + return ret; +} + string SrsFragment::tmppath() { return filepath + ".tmp"; diff --git a/trunk/src/app/srs_app_fragment.hpp b/trunk/src/app/srs_app_fragment.hpp index 020d2fd14..b80392020 100644 --- a/trunk/src/app/srs_app_fragment.hpp +++ b/trunk/src/app/srs_app_fragment.hpp @@ -67,6 +67,8 @@ public: // Unlink the fragment, to delete the file. // @remark Ignore any error. virtual int unlink_file(); + // Create the dir for file recursively. + virtual int create_dir(); public: // Get the temporary path for file. virtual std::string tmppath(); diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index ba9df5366..462443185 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -423,12 +423,9 @@ int SrsHlsMuxer::segment_open() current->uri += ts_url; // create dir recursively for hls. - std::string ts_dir = srs_path_dirname(current->fullpath()); - if ((ret = srs_create_dir_recursively(ts_dir)) != ERROR_SUCCESS) { - srs_error("create app dir %s failed. ret=%d", ts_dir.c_str(), ret); + if ((ret = current->create_dir()) != ERROR_SUCCESS) { return ret; } - srs_info("create ts dir %s ok", ts_dir.c_str()); // open temp ts file. std::string tmp_file = current->tmppath();