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