diff --git a/README.md b/README.md index 5a15550c2..103ecff4b 100755 --- a/README.md +++ b/README.md @@ -501,7 +501,8 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History -* v2.0, 2015-01-02, hotfix [#211](https://github.com/winlinvip/simple-rtmp-server/issues/211), support security allow/deny publish/play all/ip. 2.0.86 +* v2.0, 2015-01-03, fix [#179](https://github.com/winlinvip/simple-rtmp-server/issues/179), dvr support custom filepath by variables. 2.0.87 +* v2.0, 2015-01-02, fix [#211](https://github.com/winlinvip/simple-rtmp-server/issues/211), support security allow/deny publish/play all/ip. 2.0.86 * v2.0, 2015-01-02, hotfix [#207](https://github.com/winlinvip/simple-rtmp-server/issues/207), trim the last 0 of log. 2.0.85 * v2.0, 2014-01-02, fix [#158](https://github.com/winlinvip/simple-rtmp-server/issues/158), http-callback check http status code ok(200). 2.0.84 * v2.0, 2015-01-02, hotfix [#216](https://github.com/winlinvip/simple-rtmp-server/issues/216), http-callback post in application/json content-type. 2.0.83 diff --git a/trunk/conf/dvr.path.conf b/trunk/conf/dvr.path.conf new file mode 100644 index 000000000..cb07d6612 --- /dev/null +++ b/trunk/conf/dvr.path.conf @@ -0,0 +1,16 @@ +# the config for srs to dvr in custom path. +# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#custom-path +# @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#custom-path +# @see full.conf for detail config. + +listen 1935; +max_connections 1000; +vhost __defaultVhost__ { + dvr { + enabled on; + dvr_path ./objs/nginx/html/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv; + dvr_plan segment; + dvr_duration 30; + dvr_wait_keyframe on; + } +} diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf old mode 100644 new mode 100755 index 1776a9d18..4efea0123 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -236,15 +236,38 @@ vhost dvr.srs.com { # default: off enabled on; # the dvr output path. - # the app dir is auto created under the dvr_path. - # for example, for rtmp stream: - # rtmp://127.0.0.1/live/livestream - # http://127.0.0.1/live/livestream.m3u8 - # where dvr_path is /dvr, srs will create the following files: - # /dvr/live the app dir for all streams. - # /dvr/live/livestream.{time}.flv the dvr flv file. - # @remark, the time use system timestamp in ms, user can use http callback to rename it. - # in a word, the dvr_path is for vhost. + # we supports some variables to generate the filename. + # [vhost], the vhost of stream. + # [app], the app of stream. + # [stream], the stream name of stream. + # [2006], replace this const to current year. + # [01], replace this const to current month. + # [02], replace this const to current date. + # [15], replace this const to current hour. + # [04], repleace this const to current minute. + # [05], repleace this const to current second. + # [999], repleace this const to current millisecond. + # [timestamp],replace this const to current UNIX timestamp in ms. + # @remark we use golang time format "2006-01-02 15:04:05.999" + # for example, for url rtmp://ossrs.net/live/livestream and time 2015-01-03 10:57:30.776 + # 1. No variables, the rule of SRS1.0(auto add [stream].[timestamp].flv as filename): + # dvr_path ./objs/nginx/html; + # => + # dvr_path ./objs/nginx/html/live/livestream.1420254068776.flv; + # 2. Use stream and date as dir name, time as filename: + # dvr_path /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv; + # => + # dvr_path /data/ossrs.net/live/livestream/2015/01/03/10.57.30.776.flv; + # 3. Use stream and year/month as dir name, date and time as filename: + # dvr_path /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]-[15].[04].[05].[999].flv; + # => + # dvr_path /data/ossrs.net/live/livestream/2015/01/03-10.57.30.776.flv; + # 4. Use vhost/app and year/month as dir name, stream/date/time as filename: + # dvr_path /data/[vhost]/[app]/[2006]/[01]/[stream]-[02]-[15].[04].[05].[999].flv; + # => + # dvr_path /data/ossrs.net/live/2015/01/livestream-03-10.57.30.776.flv; + # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#custom-path + # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#custom-path # default: ./objs/nginx/html dvr_path ./objs/nginx/html; # the dvr plan. canbe: diff --git a/trunk/src/app/srs_app_dvr.cpp b/trunk/src/app/srs_app_dvr.cpp index 06c70eaf9..c5aef39ea 100644 --- a/trunk/src/app/srs_app_dvr.cpp +++ b/trunk/src/app/srs_app_dvr.cpp @@ -27,6 +27,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include using namespace std; #include @@ -136,14 +137,97 @@ int SrsDvrPlan::open_new_segment() SrsRequest* req = _req; - // new flv file - std::stringstream path; + // the path in config, for example, + // /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv + std::string path_config = _srs_config->get_dvr_path(req->vhost); - path << _srs_config->get_dvr_path(req->vhost) - << "/" << req->app << "/" - << req->stream << "." << srs_get_system_time_ms() << ".flv"; + // add [stream].[timestamp].flv as filename for dir + if (path_config.find(".flv") != path_config.length() - 4) { + path_config += "/[stream].[timestamp].flv"; + } - if ((ret = flv_open(req->get_stream_url(), path.str())) != ERROR_SUCCESS) { + // the flv file path + std::string path = path_config; + + // variable [vhost] + path = srs_string_replace(path, "[vhost]", req->vhost); + // variable [app] + path = srs_string_replace(path, "[app]", req->app); + // variable [stream] + path = srs_string_replace(path, "[stream]", req->stream); + + // date and time substitude + // clock time + timeval tv; + if (gettimeofday(&tv, NULL) == -1) { + return ERROR_SYSTEM_TIME; + } + + // to calendar time + struct tm* tm; + if ((tm = localtime(&tv.tv_sec)) == NULL) { + return ERROR_SYSTEM_TIME; + } + + // the buffer to format the date and time. + char buf[64]; + + // [2006], replace with current year. + if (true) { + snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); + path = srs_string_replace(path, "[2006]", buf); + } + // [2006], replace with current year. + if (true) { + snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); + path = srs_string_replace(path, "[2006]", buf); + } + // [01], replace this const to current month. + if (true) { + snprintf(buf, sizeof(buf), "%d", 1 + tm->tm_mon); + path = srs_string_replace(path, "[01]", buf); + } + // [02], replace this const to current date. + if (true) { + snprintf(buf, sizeof(buf), "%d", tm->tm_mday); + path = srs_string_replace(path, "[02]", buf); + } + // [15], replace this const to current hour. + if (true) { + snprintf(buf, sizeof(buf), "%d", tm->tm_hour); + path = srs_string_replace(path, "[15]", buf); + } + // [04], repleace this const to current minute. + if (true) { + snprintf(buf, sizeof(buf), "%d", tm->tm_min); + path = srs_string_replace(path, "[04]", buf); + } + // [05], repleace this const to current second. + if (true) { + snprintf(buf, sizeof(buf), "%d", tm->tm_sec); + path = srs_string_replace(path, "[05]", buf); + } + // [999], repleace this const to current millisecond. + if (true) { + snprintf(buf, sizeof(buf), "%03d", (int)(tv.tv_usec / 1000)); + path = srs_string_replace(path, "[999]", buf); + } + // [timestamp],replace this const to current UNIX timestamp in ms. + if (true) { + int64_t now_us = ((int64_t)tv.tv_sec) * 1000 * 1000 + (int64_t)tv.tv_usec; + snprintf(buf, sizeof(buf), "%"PRId64, now_us / 1000); + path = srs_string_replace(path, "[timestamp]", buf); + } + + // create dir first. + std::string dir = path.substr(0, path.rfind("/")); + if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) { + srs_error("create dir=%s failed. ret=%d", dir.c_str(), ret); + return ret; + } + srs_info("create dir=%s ok", dir.c_str()); + + if ((ret = flv_open(req->get_stream_url(), path)) != ERROR_SUCCESS) { return ret; } dvr_enabled = true; diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index f08c0ac8f..460d03b0a 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -951,16 +951,12 @@ int SrsHlsMuxer::create_dir() app_dir += app; // TODO: cleanup the dir when startup. - - mode_t mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH; - if (::mkdir(app_dir.c_str(), mode) < 0) { - if (errno != EEXIST) { - ret = ERROR_HLS_CREATE_DIR; - srs_error("create app dir %s failed. ret=%d", app_dir.c_str(), ret); - return ret; - } + + if ((ret = srs_create_dir_recursively(app_dir)) != ERROR_SUCCESS) { + srs_error("create app dir %s failed. ret=%d", app_dir.c_str(), ret); + return ret; } - srs_info("create app dir %s success.", app_dir.c_str()); + srs_info("create app dir %s ok", app_dir.c_str()); return ret; } diff --git a/trunk/src/app/srs_app_security.cpp b/trunk/src/app/srs_app_security.cpp index 8d42105d4..b843e58b0 100644 --- a/trunk/src/app/srs_app_security.cpp +++ b/trunk/src/app/srs_app_security.cpp @@ -98,6 +98,9 @@ int SrsSecurity::allow_check(SrsConfDirective* rules, SrsRtmpConnType type, std: break; } break; + case SrsRtmpConnUnknown: + default: + break; } // when matched, donot search more. @@ -140,6 +143,9 @@ int SrsSecurity::deny_check(SrsConfDirective* rules, SrsRtmpConnType type, std:: break; } break; + case SrsRtmpConnUnknown: + default: + break; } // when matched, donot search more. diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index bb776e8ea..7f2ae0f4c 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 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 86 +#define VERSION_REVISION 87 // server info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_ROLE "origin/edge server" diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 278a3351a..d8907ef96 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -93,6 +93,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_SYSTEM_SECURITY 1052 #define ERROR_SYSTEM_SECURITY_DENY 1053 #define ERROR_SYSTEM_SECURITY_ALLOW 1054 +#define ERROR_SYSTEM_TIME 1055 +#define ERROR_SYSTEM_DIR_EXISTS 1056 +#define ERROR_SYSTEM_CREATE_DIR 1057 /////////////////////////////////////////////////////// // RTMP protocol error. @@ -152,7 +155,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /////////////////////////////////////////////////////// #define ERROR_HLS_METADATA 3000 #define ERROR_HLS_DECODE_ERROR 3001 -#define ERROR_HLS_CREATE_DIR 3002 +//#define ERROR_HLS_CREATE_DIR 3002 #define ERROR_HLS_OPEN_FAILED 3003 #define ERROR_HLS_WRITE_FAILED 3004 #define ERROR_HLS_AAC_FRAME_LENGTH 3005 diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index c19fb7d05..1bb6bee35 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -32,10 +32,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #endif #include +#include +#include using namespace std; #include +#include // this value must: // equals to (SRS_SYS_CYCLE_INTERVAL*SRS_SYS_TIME_RESOLUTION_MS_TIMES)*1000 @@ -222,3 +225,56 @@ bool srs_string_ends_with(string str, string flag) return str.rfind(flag) == str.length() - flag.length(); } +int __srs_create_dir_recursively(string dir) +{ + int ret = ERROR_SUCCESS; + + struct stat st; + + // stat current dir, if exists, return error. + if (stat(dir.c_str(), &st) == 0) { + return ERROR_SYSTEM_DIR_EXISTS; + } + + // create parent first. + size_t pos; + if ((pos = dir.rfind("/")) != std::string::npos) { + std::string parent = dir.substr(0, pos); + ret = __srs_create_dir_recursively(parent); + // return for error. + if (ret != ERROR_SUCCESS && ret != ERROR_SYSTEM_DIR_EXISTS) { + return ret; + } + // parent exists, set to ok. + ret = ERROR_SUCCESS; + } + + // create curren dir. + mode_t mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH; + if (::mkdir(dir.c_str(), mode) < 0) { + if (errno == EEXIST) { + return ERROR_SYSTEM_DIR_EXISTS; + } + + ret = ERROR_SYSTEM_CREATE_DIR; + srs_error("create dir %s failed. ret=%d", dir.c_str(), ret); + return ret; + } + srs_info("create dir %s success.", dir.c_str()); + + return ret; +} + +int srs_create_dir_recursively(string dir) +{ + int ret = ERROR_SUCCESS; + + ret = __srs_create_dir_recursively(dir); + + if (ret == ERROR_SYSTEM_DIR_EXISTS) { + return ERROR_SUCCESS; + } + + return ret; +} + diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp index 36d2155cd..a1a03e0ed 100644 --- a/trunk/src/kernel/srs_kernel_utility.hpp +++ b/trunk/src/kernel/srs_kernel_utility.hpp @@ -59,5 +59,8 @@ extern std::string srs_string_remove(std::string str, std::string remove_chars); // whether string end with extern bool srs_string_ends_with(std::string str, std::string flag); +// create dir recursively +extern int srs_create_dir_recursively(std::string dir); + #endif