winlin

for #179, refine dvr code to more simple.

@@ -281,6 +281,12 @@ vhost dvr.srs.com { @@ -281,6 +281,12 @@ vhost dvr.srs.com {
281 # whether enabled dvr features 281 # whether enabled dvr features
282 # default: off 282 # default: off
283 enabled on; 283 enabled on;
  284 + # the dvr plan. canbe:
  285 + # session reap flv when session end(unpublish).
  286 + # segment reap flv when flv duration exceed the specified dvr_duration.
  287 + # api reap flv when api required.
  288 + # default: session
  289 + dvr_plan session;
284 # the dvr output path. 290 # the dvr output path.
285 # we supports some variables to generate the filename. 291 # we supports some variables to generate the filename.
286 # [vhost], the vhost of stream. 292 # [vhost], the vhost of stream.
@@ -314,22 +320,28 @@ vhost dvr.srs.com { @@ -314,22 +320,28 @@ vhost dvr.srs.com {
314 # dvr_path /data/ossrs.net/live/2015/01/livestream-03-10.57.30.776.flv; 320 # dvr_path /data/ossrs.net/live/2015/01/livestream-03-10.57.30.776.flv;
315 # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#custom-path 321 # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#custom-path
316 # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#custom-path 322 # @see https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#custom-path
  323 + # segment,session apply it.
  324 + # api apply before api specified the path.
317 # default: ./objs/nginx/html 325 # default: ./objs/nginx/html
318 dvr_path ./objs/nginx/html; 326 dvr_path ./objs/nginx/html;
319 - # the dvr plan. canbe:  
320 - # session reap flv when session end(unpublish).  
321 - # segment reap flv when flv duration exceed the specified dvr_duration.  
322 - # default: session  
323 - dvr_plan session;  
324 - # the param for plan(segment), in seconds. 327 + # the duration for dvr file, reap if exeed, in seconds.
  328 + # segment apply it.
  329 + # session,api ignore.
325 # default: 30 330 # default: 30
326 dvr_duration 30; 331 dvr_duration 30;
327 - # the param for plan(segment),  
328 # whether wait keyframe to reap segment, 332 # whether wait keyframe to reap segment,
329 # if off, reap segment when duration exceed the dvr_duration, 333 # if off, reap segment when duration exceed the dvr_duration,
330 # if on, reap segment when duration exceed and got keyframe. 334 # if on, reap segment when duration exceed and got keyframe.
  335 + # segment apply it.
  336 + # session,api ignore.
331 # default: on 337 # default: on
332 dvr_wait_keyframe on; 338 dvr_wait_keyframe on;
  339 + # whether dvr auto start when publish.
  340 + # if off, dvr wait for api to start it.
  341 + # api apply it.
  342 + # segment,session ignore.
  343 + # default: on
  344 + dvr_autostart on;
333 # about the stream monotonically increasing: 345 # about the stream monotonically increasing:
334 # 1. video timestamp is monotonically increasing, 346 # 1. video timestamp is monotonically increasing,
335 # 2. audio timestamp is monotonically increasing, 347 # 2. audio timestamp is monotonically increasing,
@@ -340,10 +352,11 @@ vhost dvr.srs.com { @@ -340,10 +352,11 @@ vhost dvr.srs.com {
340 # 1. full, to ensure stream start at zero, and ensure stream monotonically increasing. 352 # 1. full, to ensure stream start at zero, and ensure stream monotonically increasing.
341 # 2. zero, only ensure sttream start at zero, ignore timestamp jitter. 353 # 2. zero, only ensure sttream start at zero, ignore timestamp jitter.
342 # 3. off, disable the time jitter algorithm, like atc. 354 # 3. off, disable the time jitter algorithm, like atc.
  355 + # apply for all dvr plan.
343 # default: full 356 # default: full
344 time_jitter full; 357 time_jitter full;
345 358
346 - # on_dvr 359 + # on_dvr, never config in here, should config in http_hooks.
347 # for the dvr http callback, @see http_hooks.on_dvr of vhost hooks.callback.srs.com 360 # for the dvr http callback, @see http_hooks.on_dvr of vhost hooks.callback.srs.com
348 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback 361 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_CN_DVR#http-callback
349 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback 362 # @read https://github.com/winlinvip/simple-rtmp-server/wiki/v2_EN_DVR#http-callback
@@ -1418,6 +1418,7 @@ int SrsConfig::check_config() @@ -1418,6 +1418,7 @@ int SrsConfig::check_config()
1418 string m = conf->at(j)->name.c_str(); 1418 string m = conf->at(j)->name.c_str();
1419 if (m != "enabled" && m != "dvr_path" && m != "dvr_plan" 1419 if (m != "enabled" && m != "dvr_path" && m != "dvr_plan"
1420 && m != "dvr_duration" && m != "dvr_wait_keyframe" && m != "time_jitter" 1420 && m != "dvr_duration" && m != "dvr_wait_keyframe" && m != "time_jitter"
  1421 + && m != "dvr_autostart"
1421 ) { 1422 ) {
1422 ret = ERROR_SYSTEM_CONFIG_INVALID; 1423 ret = ERROR_SYSTEM_CONFIG_INVALID;
1423 srs_error("unsupported vhost dvr directive %s, ret=%d", m.c_str(), ret); 1424 srs_error("unsupported vhost dvr directive %s, ret=%d", m.c_str(), ret);
@@ -3377,6 +3378,23 @@ bool SrsConfig::get_dvr_wait_keyframe(string vhost) @@ -3377,6 +3378,23 @@ bool SrsConfig::get_dvr_wait_keyframe(string vhost)
3377 return false; 3378 return false;
3378 } 3379 }
3379 3380
  3381 +bool SrsConfig::get_dvr_autostart(string vhost)
  3382 +{
  3383 + SrsConfDirective* dvr = get_dvr(vhost);
  3384 +
  3385 + if (!dvr) {
  3386 + return true;
  3387 + }
  3388 +
  3389 + SrsConfDirective* conf = dvr->get("dvr_autostart");
  3390 +
  3391 + if (!conf || conf->arg0() != "off") {
  3392 + return true;
  3393 + }
  3394 +
  3395 + return false;
  3396 +}
  3397 +
3380 int SrsConfig::get_dvr_time_jitter(string vhost) 3398 int SrsConfig::get_dvr_time_jitter(string vhost)
3381 { 3399 {
3382 SrsConfDirective* dvr = get_dvr(vhost); 3400 SrsConfDirective* dvr = get_dvr(vhost);
@@ -60,6 +60,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -60,6 +60,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
60 #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html" 60 #define SRS_CONF_DEFAULT_DVR_PATH "./objs/nginx/html"
61 #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session" 61 #define SRS_CONF_DEFAULT_DVR_PLAN_SESSION "session"
62 #define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment" 62 #define SRS_CONF_DEFAULT_DVR_PLAN_SEGMENT "segment"
  63 +#define SRS_CONF_DEFAULT_DVR_PLAN_API "api"
63 #define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION 64 #define SRS_CONF_DEFAULT_DVR_PLAN SRS_CONF_DEFAULT_DVR_PLAN_SESSION
64 #define SRS_CONF_DEFAULT_DVR_DURATION 30 65 #define SRS_CONF_DEFAULT_DVR_DURATION 30
65 #define SRS_CONF_DEFAULT_TIME_JITTER "full" 66 #define SRS_CONF_DEFAULT_TIME_JITTER "full"
@@ -921,14 +922,18 @@ public: @@ -921,14 +922,18 @@ public:
921 */ 922 */
922 virtual std::string get_dvr_plan(std::string vhost); 923 virtual std::string get_dvr_plan(std::string vhost);
923 /** 924 /**
924 - * get the duration of dvr flv, for segment plan. 925 + * get the duration of dvr flv.
925 */ 926 */
926 virtual int get_dvr_duration(std::string vhost); 927 virtual int get_dvr_duration(std::string vhost);
927 /** 928 /**
928 - * whether wait keyframe to reap segment, for segment plan. 929 + * whether wait keyframe to reap segment.
929 */ 930 */
930 virtual bool get_dvr_wait_keyframe(std::string vhost); 931 virtual bool get_dvr_wait_keyframe(std::string vhost);
931 /** 932 /**
  933 + * whether autostart for dvr. wait api to start dvr if false.
  934 + */
  935 + virtual bool get_dvr_autostart(std::string vhost);
  936 + /**
932 * get the time_jitter algorithm for dvr. 937 * get the time_jitter algorithm for dvr.
933 */ 938 */
934 virtual int get_dvr_time_jitter(std::string vhost); 939 virtual int get_dvr_time_jitter(std::string vhost);
@@ -39,8 +39,17 @@ using namespace std; @@ -39,8 +39,17 @@ using namespace std;
39 #include <srs_kernel_flv.hpp> 39 #include <srs_kernel_flv.hpp>
40 #include <srs_kernel_file.hpp> 40 #include <srs_kernel_file.hpp>
41 41
42 -SrsFlvSegment::SrsFlvSegment() 42 +SrsFlvSegment::SrsFlvSegment(SrsDvrPlan* p)
43 { 43 {
  44 + req = NULL;
  45 + source = NULL;
  46 + jitter = NULL;
  47 + plan = p;
  48 +
  49 + fs = new SrsFileWriter();
  50 + enc = new SrsFlvEncoder();
  51 + jitter_algorithm = SrsRtmpJitterAlgorithmOFF;
  52 +
44 path = ""; 53 path = "";
45 has_keyframe = false; 54 has_keyframe = false;
46 duration = 0; 55 duration = 0;
@@ -48,95 +57,238 @@ SrsFlvSegment::SrsFlvSegment() @@ -48,95 +57,238 @@ SrsFlvSegment::SrsFlvSegment()
48 stream_starttime = 0; 57 stream_starttime = 0;
49 stream_previous_pkt_time = -1; 58 stream_previous_pkt_time = -1;
50 stream_duration = 0; 59 stream_duration = 0;
  60 +
  61 + _srs_config->subscribe(this);
51 } 62 }
52 63
53 SrsFlvSegment::~SrsFlvSegment() 64 SrsFlvSegment::~SrsFlvSegment()
54 { 65 {
  66 + _srs_config->unsubscribe(this);
  67 +
  68 + srs_freep(jitter);
  69 + srs_freep(fs);
  70 + srs_freep(enc);
55 } 71 }
56 72
57 -void SrsFlvSegment::reset() 73 +int SrsFlvSegment::initialize(SrsSource* s, SrsRequest* r)
58 { 74 {
59 - has_keyframe = false;  
60 - starttime = -1;  
61 - duration = 0; 75 + int ret = ERROR_SUCCESS;
  76 +
  77 + source = s;
  78 + req = r;
  79 + jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(req->vhost);
  80 +
  81 + return ret;
62 } 82 }
63 83
64 -SrsDvrPlan::SrsDvrPlan() 84 +bool SrsFlvSegment::is_overflow(int64_t max_duration)
65 { 85 {
66 - _source = NULL;  
67 - _req = NULL;  
68 - jitter = NULL;  
69 - dvr_enabled = false;  
70 - fs = new SrsFileWriter();  
71 - enc = new SrsFlvEncoder();  
72 - segment = new SrsFlvSegment();  
73 - jitter_algorithm = SrsRtmpJitterAlgorithmOFF;  
74 -  
75 - _srs_config->subscribe(this); 86 + return duration >= max_duration;
76 } 87 }
77 88
78 -SrsDvrPlan::~SrsDvrPlan() 89 +int SrsFlvSegment::open()
79 { 90 {
80 - _srs_config->unsubscribe(this); 91 + int ret = ERROR_SUCCESS;
81 92
82 - srs_freep(jitter);  
83 - srs_freep(fs);  
84 - srs_freep(enc);  
85 - srs_freep(segment); 93 + // ignore when already open.
  94 + if (fs->is_open()) {
  95 + return ret;
  96 + }
  97 +
  98 + path = generate_path();
  99 + bool fresh_flv_file = !srs_path_exists(path);
  100 +
  101 + // create dir first.
  102 + std::string dir = path.substr(0, path.rfind("/"));
  103 + if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) {
  104 + srs_error("create dir=%s failed. ret=%d", dir.c_str(), ret);
  105 + return ret;
  106 + }
  107 + srs_info("create dir=%s ok", dir.c_str());
  108 +
  109 + // create jitter.
  110 + if ((ret = create_jitter(!fresh_flv_file)) != ERROR_SUCCESS) {
  111 + srs_error("create jitter failed, path=%s, fresh=%d. ret=%d", path.c_str(), fresh_flv_file, ret);
  112 + return ret;
  113 + }
  114 +
  115 + // generate the tmp flv path.
  116 + if (!fresh_flv_file) {
  117 + // when path exists, always append to it.
  118 + // so we must use the target flv path as output flv.
  119 + tmp_flv_file = path;
  120 + } else {
  121 + // when path not exists, dvr to tmp file.
  122 + tmp_flv_file = path + ".tmp";
  123 + }
  124 +
  125 + // open file writer, in append or create mode.
  126 + if (!fresh_flv_file) {
  127 + if ((ret = fs->open_append(tmp_flv_file)) != ERROR_SUCCESS) {
  128 + srs_error("append file stream for file %s failed. ret=%d", path.c_str(), ret);
  129 + return ret;
  130 + }
  131 + srs_trace("dvr: always append to when exists, file=%s.", path.c_str());
  132 + } else {
  133 + if ((ret = fs->open(tmp_flv_file)) != ERROR_SUCCESS) {
  134 + srs_error("open file stream for file %s failed. ret=%d", path.c_str(), ret);
  135 + return ret;
  136 + }
  137 + }
  138 +
  139 + // when exists, donot write flv header.
  140 + if (fresh_flv_file) {
  141 + // initialize the encoder.
  142 + if ((ret = enc->initialize(fs)) != ERROR_SUCCESS) {
  143 + srs_error("initialize enc by fs for file %s failed. ret=%d", path.c_str(), ret);
  144 + return ret;
  145 + }
  146 +
  147 + // write the flv header to writer.
  148 + if ((ret = enc->write_header()) != ERROR_SUCCESS) {
  149 + srs_error("write flv header failed. ret=%d", ret);
  150 + return ret;
  151 + }
  152 + }
  153 +
  154 + srs_trace("dvr stream %s to file %s", req->stream.c_str(), path.c_str());
  155 +
  156 + return ret;
86 } 157 }
87 158
88 -int SrsDvrPlan::initialize(SrsSource* source, SrsRequest* req) 159 +int SrsFlvSegment::close()
89 { 160 {
90 int ret = ERROR_SUCCESS; 161 int ret = ERROR_SUCCESS;
91 162
92 - _source = source;  
93 - _req = req; 163 + // ignore when already closed.
  164 + if (!fs->is_open()) {
  165 + return ret;
  166 + }
94 167
95 - jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(_req->vhost); 168 + fs->close();
  169 +
  170 + // when tmp flv file exists, reap it.
  171 + if (tmp_flv_file != path) {
  172 + if (rename(tmp_flv_file.c_str(), path.c_str()) < 0) {
  173 + ret = ERROR_SYSTEM_FILE_RENAME;
  174 + srs_error("rename flv file failed, %s => %s. ret=%d",
  175 + tmp_flv_file.c_str(), path.c_str(), ret);
  176 + return ret;
  177 + }
  178 + }
  179 +
  180 +#ifdef SRS_AUTO_HTTP_CALLBACK
  181 + if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {
  182 + // HTTP: on_dvr
  183 + SrsConfDirective* on_dvr = _srs_config->get_vhost_on_dvr(req->vhost);
  184 + if (!on_dvr) {
  185 + srs_info("ignore the empty http callback: on_dvr");
  186 + return ret;
  187 + }
  188 +
  189 + int connection_id = _srs_context->get_id();
  190 + std::string ip = req->ip;
  191 + std::string cwd = _srs_config->cwd();
  192 + std::string file = path;
  193 + for (int i = 0; i < (int)on_dvr->args.size(); i++) {
  194 + std::string url = on_dvr->args.at(i);
  195 + if ((ret = SrsHttpHooks::on_dvr(url, connection_id, ip, req, cwd, file)) != ERROR_SUCCESS) {
  196 + srs_error("hook client on_dvr failed. url=%s, ret=%d", url.c_str(), ret);
  197 + return ret;
  198 + }
  199 + }
  200 + }
  201 +#endif
96 202
97 return ret; 203 return ret;
98 } 204 }
99 205
100 -int SrsDvrPlan::on_publish() 206 +int SrsFlvSegment::write_metadata(SrsOnMetaDataPacket* metadata)
101 { 207 {
102 int ret = ERROR_SUCCESS; 208 int ret = ERROR_SUCCESS;
103 209
104 - // support multiple publish.  
105 - if (dvr_enabled) { 210 + int size = 0;
  211 + char* payload = NULL;
  212 + if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) {
106 return ret; 213 return ret;
107 } 214 }
  215 + SrsAutoFree(char, payload);
108 216
109 - SrsRequest* req = _req;  
110 -  
111 - if (!_srs_config->get_dvr_enabled(req->vhost)) { 217 + if ((ret = enc->write_metadata(18, payload, size)) != ERROR_SUCCESS) {
112 return ret; 218 return ret;
113 } 219 }
114 220
115 - // jitter when publish, ensure whole stream start from 0.  
116 - srs_freep(jitter);  
117 - jitter = new SrsRtmpJitter();  
118 -  
119 - // always update time cache.  
120 - srs_update_system_time_ms(); 221 + return ret;
  222 +}
  223 +
  224 +int SrsFlvSegment::write_audio(SrsSharedPtrMessage* __audio)
  225 +{
  226 + int ret = ERROR_SUCCESS;
  227 +
  228 + SrsSharedPtrMessage* audio = __audio->copy();
  229 + SrsAutoFree(SrsSharedPtrMessage, audio);
121 230
122 - // when republish, stream starting.  
123 - segment->stream_previous_pkt_time = -1;  
124 - segment->stream_starttime = srs_get_system_time_ms();  
125 - segment->stream_duration = 0; 231 + if ((jitter->correct(audio, 0, 0, jitter_algorithm)) != ERROR_SUCCESS) {
  232 + return ret;
  233 + }
126 234
127 - if ((ret = open_new_segment()) != ERROR_SUCCESS) { 235 + char* payload = audio->payload;
  236 + int size = audio->size;
  237 + int64_t timestamp = plan->filter_timestamp(audio->timestamp);
  238 + if ((ret = enc->write_audio(timestamp, payload, size)) != ERROR_SUCCESS) {
  239 + return ret;
  240 + }
  241 +
  242 + if ((ret = on_update_duration(audio)) != ERROR_SUCCESS) {
128 return ret; 243 return ret;
129 } 244 }
130 245
131 return ret; 246 return ret;
132 } 247 }
133 248
134 -int SrsDvrPlan::open_new_segment() 249 +int SrsFlvSegment::write_video(SrsSharedPtrMessage* __video)
135 { 250 {
136 int ret = ERROR_SUCCESS; 251 int ret = ERROR_SUCCESS;
  252 +
  253 + SrsSharedPtrMessage* video = __video->copy();
  254 + SrsAutoFree(SrsSharedPtrMessage, video);
  255 +
  256 + char* payload = video->payload;
  257 + int size = video->size;
137 258
138 - SrsRequest* req = _req; 259 +#ifdef SRS_AUTO_HTTP_CALLBACK
  260 + bool is_key_frame = SrsFlvCodec::video_is_h264(payload, size)
  261 + && SrsFlvCodec::video_is_keyframe(payload, size)
  262 + && !SrsFlvCodec::video_is_sequence_header(payload, size);
  263 + if (is_key_frame) {
  264 + has_keyframe = true;
  265 + if ((ret = plan->on_video_keyframe()) != ERROR_SUCCESS) {
  266 + return ret;
  267 + }
  268 + }
  269 + srs_verbose("dvr video is key: %d", is_key_frame);
  270 +#endif
139 271
  272 + if ((jitter->correct(video, 0, 0, jitter_algorithm)) != ERROR_SUCCESS) {
  273 + return ret;
  274 + }
  275 +
  276 + // update segment duration, session plan just update the duration,
  277 + // the segment plan will reap segment if exceed, this video will write to next segment.
  278 + if ((ret = on_update_duration(video)) != ERROR_SUCCESS) {
  279 + return ret;
  280 + }
  281 +
  282 + int32_t timestamp = plan->filter_timestamp(video->timestamp);
  283 + if ((ret = enc->write_video(timestamp, payload, size)) != ERROR_SUCCESS) {
  284 + return ret;
  285 + }
  286 +
  287 + return ret;
  288 +}
  289 +
  290 +string SrsFlvSegment::generate_path()
  291 +{
140 // the path in config, for example, 292 // the path in config, for example,
141 // /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv 293 // /data/[vhost]/[app]/[stream]/[2006]/[01]/[02]/[15].[04].[05].[999].flv
142 std::string path_config = _srs_config->get_dvr_path(req->vhost); 294 std::string path_config = _srs_config->get_dvr_path(req->vhost);
@@ -147,26 +299,26 @@ int SrsDvrPlan::open_new_segment() @@ -147,26 +299,26 @@ int SrsDvrPlan::open_new_segment()
147 } 299 }
148 300
149 // the flv file path 301 // the flv file path
150 - std::string path = path_config; 302 + std::string flv_path = path_config;
151 303
152 // variable [vhost] 304 // variable [vhost]
153 - path = srs_string_replace(path, "[vhost]", req->vhost); 305 + flv_path = srs_string_replace(flv_path, "[vhost]", req->vhost);
154 // variable [app] 306 // variable [app]
155 - path = srs_string_replace(path, "[app]", req->app); 307 + flv_path = srs_string_replace(flv_path, "[app]", req->app);
156 // variable [stream] 308 // variable [stream]
157 - path = srs_string_replace(path, "[stream]", req->stream); 309 + flv_path = srs_string_replace(flv_path, "[stream]", req->stream);
158 310
159 // date and time substitude 311 // date and time substitude
160 // clock time 312 // clock time
161 timeval tv; 313 timeval tv;
162 if (gettimeofday(&tv, NULL) == -1) { 314 if (gettimeofday(&tv, NULL) == -1) {
163 - return ERROR_SYSTEM_TIME; 315 + return flv_path;
164 } 316 }
165 317
166 // to calendar time 318 // to calendar time
167 struct tm* tm; 319 struct tm* tm;
168 if ((tm = localtime(&tv.tv_sec)) == NULL) { 320 if ((tm = localtime(&tv.tv_sec)) == NULL) {
169 - return ERROR_SYSTEM_TIME; 321 + return flv_path;
170 } 322 }
171 323
172 // the buffer to format the date and time. 324 // the buffer to format the date and time.
@@ -175,316 +327,212 @@ int SrsDvrPlan::open_new_segment() @@ -175,316 +327,212 @@ int SrsDvrPlan::open_new_segment()
175 // [2006], replace with current year. 327 // [2006], replace with current year.
176 if (true) { 328 if (true) {
177 snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); 329 snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year);
178 - path = srs_string_replace(path, "[2006]", buf); 330 + flv_path = srs_string_replace(flv_path, "[2006]", buf);
179 } 331 }
180 // [2006], replace with current year. 332 // [2006], replace with current year.
181 if (true) { 333 if (true) {
182 snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year); 334 snprintf(buf, sizeof(buf), "%d", 1900 + tm->tm_year);
183 - path = srs_string_replace(path, "[2006]", buf); 335 + flv_path = srs_string_replace(flv_path, "[2006]", buf);
184 } 336 }
185 // [01], replace this const to current month. 337 // [01], replace this const to current month.
186 if (true) { 338 if (true) {
187 snprintf(buf, sizeof(buf), "%d", 1 + tm->tm_mon); 339 snprintf(buf, sizeof(buf), "%d", 1 + tm->tm_mon);
188 - path = srs_string_replace(path, "[01]", buf); 340 + flv_path = srs_string_replace(flv_path, "[01]", buf);
189 } 341 }
190 // [02], replace this const to current date. 342 // [02], replace this const to current date.
191 if (true) { 343 if (true) {
192 snprintf(buf, sizeof(buf), "%d", tm->tm_mday); 344 snprintf(buf, sizeof(buf), "%d", tm->tm_mday);
193 - path = srs_string_replace(path, "[02]", buf); 345 + flv_path = srs_string_replace(flv_path, "[02]", buf);
194 } 346 }
195 // [15], replace this const to current hour. 347 // [15], replace this const to current hour.
196 if (true) { 348 if (true) {
197 snprintf(buf, sizeof(buf), "%d", tm->tm_hour); 349 snprintf(buf, sizeof(buf), "%d", tm->tm_hour);
198 - path = srs_string_replace(path, "[15]", buf); 350 + flv_path = srs_string_replace(flv_path, "[15]", buf);
199 } 351 }
200 // [04], repleace this const to current minute. 352 // [04], repleace this const to current minute.
201 if (true) { 353 if (true) {
202 snprintf(buf, sizeof(buf), "%d", tm->tm_min); 354 snprintf(buf, sizeof(buf), "%d", tm->tm_min);
203 - path = srs_string_replace(path, "[04]", buf); 355 + flv_path = srs_string_replace(flv_path, "[04]", buf);
204 } 356 }
205 // [05], repleace this const to current second. 357 // [05], repleace this const to current second.
206 if (true) { 358 if (true) {
207 snprintf(buf, sizeof(buf), "%d", tm->tm_sec); 359 snprintf(buf, sizeof(buf), "%d", tm->tm_sec);
208 - path = srs_string_replace(path, "[05]", buf); 360 + flv_path = srs_string_replace(flv_path, "[05]", buf);
209 } 361 }
210 // [999], repleace this const to current millisecond. 362 // [999], repleace this const to current millisecond.
211 if (true) { 363 if (true) {
212 snprintf(buf, sizeof(buf), "%03d", (int)(tv.tv_usec / 1000)); 364 snprintf(buf, sizeof(buf), "%03d", (int)(tv.tv_usec / 1000));
213 - path = srs_string_replace(path, "[999]", buf); 365 + flv_path = srs_string_replace(flv_path, "[999]", buf);
214 } 366 }
215 // [timestamp],replace this const to current UNIX timestamp in ms. 367 // [timestamp],replace this const to current UNIX timestamp in ms.
216 if (true) { 368 if (true) {
217 int64_t now_us = ((int64_t)tv.tv_sec) * 1000 * 1000 + (int64_t)tv.tv_usec; 369 int64_t now_us = ((int64_t)tv.tv_sec) * 1000 * 1000 + (int64_t)tv.tv_usec;
218 snprintf(buf, sizeof(buf), "%"PRId64, now_us / 1000); 370 snprintf(buf, sizeof(buf), "%"PRId64, now_us / 1000);
219 - path = srs_string_replace(path, "[timestamp]", buf); 371 + flv_path = srs_string_replace(flv_path, "[timestamp]", buf);
220 } 372 }
  373 +
  374 + return flv_path;
  375 +}
  376 +
  377 +int SrsFlvSegment::create_jitter(bool loads_from_flv)
  378 +{
  379 + int ret = ERROR_SUCCESS;
221 380
222 - // create dir first.  
223 - std::string dir = path.substr(0, path.rfind("/"));  
224 - if ((ret = srs_create_dir_recursively(dir)) != ERROR_SUCCESS) {  
225 - srs_error("create dir=%s failed. ret=%d", dir.c_str(), ret); 381 + // when path exists, use exists jitter.
  382 + if (!loads_from_flv) {
  383 + // jitter when publish, ensure whole stream start from 0.
  384 + srs_freep(jitter);
  385 + jitter = new SrsRtmpJitter();
  386 +
  387 + // fresh stream starting.
  388 + starttime = -1;
  389 + stream_previous_pkt_time = -1;
  390 + stream_starttime = srs_update_system_time_ms();
  391 + stream_duration = 0;
  392 +
  393 + // fresh segment starting.
  394 + has_keyframe = false;
  395 + duration = 0;
  396 +
226 return ret; 397 return ret;
227 } 398 }
228 - srs_info("create dir=%s ok", dir.c_str());  
229 -  
230 - if ((ret = flv_open(req->get_stream_url(), path)) != ERROR_SUCCESS) { 399 +
  400 + // when jitter ok, do nothing.
  401 + if (jitter) {
231 return ret; 402 return ret;
232 } 403 }
233 - dvr_enabled = true;  
234 - 404 +
  405 + // always ensure the jitter crote.
  406 + // for the first time, initialize jitter from exists file.
  407 + jitter = new SrsRtmpJitter();
  408 +
  409 + // TODO: FIXME: implements it.
  410 +
235 return ret; 411 return ret;
236 } 412 }
237 413
238 -int SrsDvrPlan::on_dvr_request_sh() 414 +int SrsFlvSegment::on_update_duration(SrsSharedPtrMessage* msg)
239 { 415 {
240 int ret = ERROR_SUCCESS; 416 int ret = ERROR_SUCCESS;
241 417
242 - // the dvr is enabled, notice the source to push the data.  
243 - if ((ret = _source->on_dvr_request_sh()) != ERROR_SUCCESS) {  
244 - return ret; 418 + // we must assumpt that the stream timestamp is monotonically increase,
  419 + // that is, always use time jitter to correct the timestamp.
  420 + // except the time jitter is disabled in config.
  421 +
  422 + // set the segment starttime at first time
  423 + if (starttime < 0) {
  424 + starttime = msg->timestamp;
245 } 425 }
246 426
  427 + // no previous packet or timestamp overflow.
  428 + if (stream_previous_pkt_time < 0 || stream_previous_pkt_time > msg->timestamp) {
  429 + stream_previous_pkt_time = msg->timestamp;
  430 + }
  431 +
  432 + // collect segment and stream duration, timestamp overflow is ok.
  433 + duration += msg->timestamp - stream_previous_pkt_time;
  434 + stream_duration += msg->timestamp - stream_previous_pkt_time;
  435 +
  436 + // update previous packet time
  437 + stream_previous_pkt_time = msg->timestamp;
  438 +
247 return ret; 439 return ret;
248 } 440 }
249 441
250 -int SrsDvrPlan::on_video_keyframe() 442 +int SrsFlvSegment::on_reload_vhost_dvr(std::string /*vhost*/)
251 { 443 {
252 int ret = ERROR_SUCCESS; 444 int ret = ERROR_SUCCESS;
  445 +
  446 + jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(req->vhost);
  447 +
253 return ret; 448 return ret;
254 } 449 }
255 450
256 -int64_t SrsDvrPlan::filter_timestamp(int64_t timestamp) 451 +SrsDvrPlan::SrsDvrPlan()
257 { 452 {
258 - return timestamp; 453 + source = NULL;
  454 + req = NULL;
  455 +
  456 + dvr_enabled = false;
  457 + segment = new SrsFlvSegment(this);
259 } 458 }
260 459
261 -int SrsDvrPlan::on_meta_data(SrsOnMetaDataPacket* metadata) 460 +SrsDvrPlan::~SrsDvrPlan()
262 { 461 {
263 - int ret = ERROR_SUCCESS;  
264 -  
265 - if (!dvr_enabled) {  
266 - return ret;  
267 - }  
268 -  
269 - int size = 0;  
270 - char* payload = NULL;  
271 - if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) {  
272 - return ret;  
273 - }  
274 - SrsAutoFree(char, payload);  
275 -  
276 - if ((ret = enc->write_metadata(18, payload, size)) != ERROR_SUCCESS) {  
277 - return ret;  
278 - }  
279 -  
280 - return ret; 462 + srs_freep(segment);
281 } 463 }
282 464
283 -int SrsDvrPlan::on_audio(SrsSharedPtrMessage* __audio) 465 +int SrsDvrPlan::initialize(SrsSource* s, SrsRequest* r)
284 { 466 {
285 int ret = ERROR_SUCCESS; 467 int ret = ERROR_SUCCESS;
286 -  
287 - if (!dvr_enabled) {  
288 - return ret;  
289 - }  
290 468
291 - SrsSharedPtrMessage* audio = __audio->copy();  
292 - SrsAutoFree(SrsSharedPtrMessage, audio);  
293 -  
294 - if ((jitter->correct(audio, 0, 0, jitter_algorithm)) != ERROR_SUCCESS) {  
295 - return ret;  
296 - }  
297 -  
298 - char* payload = audio->payload;  
299 - int size = audio->size;  
300 - int64_t timestamp = filter_timestamp(audio->timestamp);  
301 - if ((ret = enc->write_audio(timestamp, payload, size)) != ERROR_SUCCESS) {  
302 - return ret;  
303 - }  
304 -  
305 - if ((ret = update_duration(audio)) != ERROR_SUCCESS) { 469 + source = s;
  470 + req = r;
  471 +
  472 + if ((ret = segment->initialize(s, r)) != ERROR_SUCCESS) {
306 return ret; 473 return ret;
307 } 474 }
308 - 475 +
309 return ret; 476 return ret;
310 } 477 }
311 478
312 -int SrsDvrPlan::on_video(SrsSharedPtrMessage* __video) 479 +int SrsDvrPlan::on_dvr_request_sh()
313 { 480 {
314 int ret = ERROR_SUCCESS; 481 int ret = ERROR_SUCCESS;
315 482
316 - if (!dvr_enabled) {  
317 - return ret;  
318 - }  
319 -  
320 - SrsSharedPtrMessage* video = __video->copy();  
321 - SrsAutoFree(SrsSharedPtrMessage, video);  
322 -  
323 - char* payload = video->payload;  
324 - int size = video->size;  
325 -  
326 -#ifdef SRS_AUTO_HTTP_CALLBACK  
327 - bool is_key_frame = SrsFlvCodec::video_is_h264(payload, size)  
328 - && SrsFlvCodec::video_is_keyframe(payload, size)  
329 - && !SrsFlvCodec::video_is_sequence_header(payload, size);  
330 - if (is_key_frame) {  
331 - segment->has_keyframe = true;  
332 - if ((ret = on_video_keyframe()) != ERROR_SUCCESS) {  
333 - return ret;  
334 - }  
335 - }  
336 - srs_verbose("dvr video is key: %d", is_key_frame);  
337 -#endif  
338 -  
339 - if ((jitter->correct(video, 0, 0, jitter_algorithm)) != ERROR_SUCCESS) {  
340 - return ret;  
341 - }  
342 -  
343 - // update segment duration, session plan just update the duration,  
344 - // the segment plan will reap segment if exceed, this video will write to next segment.  
345 - if ((ret = update_duration(video)) != ERROR_SUCCESS) {  
346 - return ret;  
347 - }  
348 -  
349 - int32_t timestamp = filter_timestamp(video->timestamp);  
350 - if ((ret = enc->write_video(timestamp, payload, size)) != ERROR_SUCCESS) { 483 + // the dvr is enabled, notice the source to push the data.
  484 + if ((ret = source->on_dvr_request_sh()) != ERROR_SUCCESS) {
351 return ret; 485 return ret;
352 } 486 }
353 487
354 return ret; 488 return ret;
355 } 489 }
356 490
357 -int SrsDvrPlan::on_reload_vhost_dvr(std::string /*vhost*/) 491 +int SrsDvrPlan::on_video_keyframe()
358 { 492 {
359 - int ret = ERROR_SUCCESS;  
360 -  
361 - jitter_algorithm = (SrsRtmpJitterAlgorithm)_srs_config->get_dvr_time_jitter(_req->vhost);  
362 -  
363 - return ret; 493 + return ERROR_SUCCESS;
364 } 494 }
365 495
366 -int SrsDvrPlan::flv_open(string stream, string path) 496 +int64_t SrsDvrPlan::filter_timestamp(int64_t timestamp)
367 { 497 {
368 - int ret = ERROR_SUCCESS;  
369 -  
370 - segment->reset();  
371 -  
372 - if (srs_path_exists(path)) {  
373 - // when path exists, always append to it.  
374 - // so we must use the target flv path as output flv.  
375 - tmp_flv_file = path;  
376 - } else {  
377 - // when path not exists, dvr to tmp file.  
378 - tmp_flv_file = path + ".tmp";  
379 - }  
380 -  
381 - if (srs_path_exists(path)) {  
382 - if ((ret = fs->open_append(tmp_flv_file)) != ERROR_SUCCESS) {  
383 - srs_error("append file stream for file %s failed. ret=%d", path.c_str(), ret);  
384 - return ret;  
385 - }  
386 - srs_trace("dvr: always append to when exists, file=%s.", path.c_str());  
387 - } else {  
388 - if ((ret = fs->open(tmp_flv_file)) != ERROR_SUCCESS) {  
389 - srs_error("open file stream for file %s failed. ret=%d", path.c_str(), ret);  
390 - return ret;  
391 - }  
392 - }  
393 -  
394 - if ((ret = enc->initialize(fs)) != ERROR_SUCCESS) {  
395 - srs_error("initialize enc by fs for file %s failed. ret=%d", path.c_str(), ret);  
396 - return ret;  
397 - }  
398 -  
399 - // when exists, donot write flv header.  
400 - if (tmp_flv_file != path) {  
401 - if ((ret = write_flv_header()) != ERROR_SUCCESS) {  
402 - return ret;  
403 - }  
404 - }  
405 -  
406 - segment->path = path;  
407 -  
408 - srs_trace("dvr stream %s to file %s", stream.c_str(), path.c_str());  
409 - return ret; 498 + return timestamp;
410 } 499 }
411 500
412 -int SrsDvrPlan::flv_close() 501 +int SrsDvrPlan::on_meta_data(SrsOnMetaDataPacket* metadata)
413 { 502 {
414 int ret = ERROR_SUCCESS; 503 int ret = ERROR_SUCCESS;
415 504
416 - fs->close();  
417 -  
418 - // when tmp flv file exists, reap it.  
419 - if (tmp_flv_file != segment->path) {  
420 - if (rename(tmp_flv_file.c_str(), segment->path.c_str()) < 0) {  
421 - ret = ERROR_SYSTEM_FILE_RENAME;  
422 - srs_error("rename flv file failed, %s => %s. ret=%d",  
423 - tmp_flv_file.c_str(), segment->path.c_str(), ret);  
424 - return ret;  
425 - } 505 + if (!dvr_enabled) {
  506 + return ret;
426 } 507 }
427 508
428 -#ifdef SRS_AUTO_HTTP_CALLBACK  
429 - SrsRequest* req = _req;  
430 - if (_srs_config->get_vhost_http_hooks_enabled(req->vhost)) {  
431 - // HTTP: on_dvr  
432 - SrsConfDirective* on_dvr = _srs_config->get_vhost_on_dvr(req->vhost);  
433 - if (!on_dvr) {  
434 - srs_info("ignore the empty http callback: on_dvr");  
435 - return ret;  
436 - }  
437 -  
438 - int connection_id = _srs_context->get_id();  
439 - std::string ip = req->ip;  
440 - std::string cwd = _srs_config->cwd();  
441 - std::string file = segment->path;  
442 - for (int i = 0; i < (int)on_dvr->args.size(); i++) {  
443 - std::string url = on_dvr->args.at(i);  
444 - if ((ret = SrsHttpHooks::on_dvr(url, connection_id, ip, req, cwd, file)) != ERROR_SUCCESS) {  
445 - srs_error("hook client on_dvr failed. url=%s, ret=%d", url.c_str(), ret);  
446 - return ret;  
447 - }  
448 - }  
449 - }  
450 -#endif  
451 -  
452 - return ret; 509 + return segment->write_metadata(metadata);
453 } 510 }
454 511
455 -int SrsDvrPlan::update_duration(SrsSharedPtrMessage* msg) 512 +int SrsDvrPlan::on_audio(SrsSharedPtrMessage* __audio)
456 { 513 {
457 int ret = ERROR_SUCCESS; 514 int ret = ERROR_SUCCESS;
458 -  
459 - // we must assumpt that the stream timestamp is monotonically increase,  
460 - // that is, always use time jitter to correct the timestamp.  
461 515
462 - // set the segment starttime at first time  
463 - if (segment->starttime < 0) {  
464 - segment->starttime = msg->timestamp; 516 + if (!dvr_enabled) {
  517 + return ret;
465 } 518 }
466 -  
467 - // no previous packet or timestamp overflow.  
468 - if (segment->stream_previous_pkt_time < 0 || segment->stream_previous_pkt_time > msg->timestamp) {  
469 - segment->stream_previous_pkt_time = msg->timestamp; 519 +
  520 + if ((ret = segment->write_audio(__audio)) != ERROR_SUCCESS) {
  521 + return ret;
470 } 522 }
471 523
472 - // collect segment and stream duration, timestamp overflow is ok.  
473 - segment->duration += msg->timestamp - segment->stream_previous_pkt_time;  
474 - segment->stream_duration += msg->timestamp - segment->stream_previous_pkt_time;  
475 -  
476 - // update previous packet time  
477 - segment->stream_previous_pkt_time = msg->timestamp;  
478 -  
479 return ret; 524 return ret;
480 } 525 }
481 526
482 -int SrsDvrPlan::write_flv_header() 527 +int SrsDvrPlan::on_video(SrsSharedPtrMessage* __video)
483 { 528 {
484 int ret = ERROR_SUCCESS; 529 int ret = ERROR_SUCCESS;
485 530
486 - if ((ret = enc->write_header()) != ERROR_SUCCESS) {  
487 - srs_error("write flv header failed. ret=%d", ret); 531 + if (!dvr_enabled) {
  532 + return ret;
  533 + }
  534 +
  535 + if ((ret = segment->write_video(__video)) != ERROR_SUCCESS) {
488 return ret; 536 return ret;
489 } 537 }
490 538
@@ -511,6 +559,32 @@ SrsDvrSessionPlan::~SrsDvrSessionPlan() @@ -511,6 +559,32 @@ SrsDvrSessionPlan::~SrsDvrSessionPlan()
511 { 559 {
512 } 560 }
513 561
  562 +int SrsDvrSessionPlan::on_publish()
  563 +{
  564 + int ret = ERROR_SUCCESS;
  565 +
  566 + // support multiple publish.
  567 + if (dvr_enabled) {
  568 + return ret;
  569 + }
  570 +
  571 + if (!_srs_config->get_dvr_enabled(req->vhost)) {
  572 + return ret;
  573 + }
  574 +
  575 + if ((ret = segment->close()) != ERROR_SUCCESS) {
  576 + return ret;
  577 + }
  578 +
  579 + if ((ret = segment->open()) != ERROR_SUCCESS) {
  580 + return ret;
  581 + }
  582 +
  583 + dvr_enabled = true;
  584 +
  585 + return ret;
  586 +}
  587 +
514 void SrsDvrSessionPlan::on_unpublish() 588 void SrsDvrSessionPlan::on_unpublish()
515 { 589 {
516 // support multiple publish. 590 // support multiple publish.
@@ -519,7 +593,7 @@ void SrsDvrSessionPlan::on_unpublish() @@ -519,7 +593,7 @@ void SrsDvrSessionPlan::on_unpublish()
519 } 593 }
520 594
521 // ignore error. 595 // ignore error.
522 - int ret = flv_close(); 596 + int ret = segment->close();
523 if (ret != ERROR_SUCCESS) { 597 if (ret != ERROR_SUCCESS) {
524 srs_warn("ignore flv close error. ret=%d", ret); 598 srs_warn("ignore flv close error. ret=%d", ret);
525 } 599 }
@@ -558,65 +632,86 @@ int SrsDvrSegmentPlan::on_publish() @@ -558,65 +632,86 @@ int SrsDvrSegmentPlan::on_publish()
558 { 632 {
559 int ret = ERROR_SUCCESS; 633 int ret = ERROR_SUCCESS;
560 634
561 - // if already opened, continue to dvr.  
562 - // the segment plan maybe keep running longer than the encoder.  
563 - // for example, segment running, encoder restart,  
564 - // the segment plan will just continue going and donot open new segment.  
565 - if (fs->is_open()) {  
566 - dvr_enabled = true; 635 + // support multiple publish.
  636 + if (dvr_enabled) {
567 return ret; 637 return ret;
568 } 638 }
569 -  
570 - return SrsDvrPlan::on_publish(); 639 +
  640 + if (!_srs_config->get_dvr_enabled(req->vhost)) {
  641 + return ret;
  642 + }
  643 +
  644 + if ((ret = segment->close()) != ERROR_SUCCESS) {
  645 + return ret;
  646 + }
  647 +
  648 + if ((ret = segment->open()) != ERROR_SUCCESS) {
  649 + return ret;
  650 + }
  651 +
  652 + dvr_enabled = true;
  653 +
  654 + return ret;
571 } 655 }
572 656
573 void SrsDvrSegmentPlan::on_unpublish() 657 void SrsDvrSegmentPlan::on_unpublish()
574 { 658 {
575 - // support multiple publish.  
576 - if (!dvr_enabled) {  
577 - return;  
578 - }  
579 - dvr_enabled = false;  
580 } 659 }
581 660
582 int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* audio) 661 int SrsDvrSegmentPlan::on_audio(SrsSharedPtrMessage* audio)
583 { 662 {
  663 + int ret = ERROR_SUCCESS;
  664 +
584 if (SrsFlvCodec::audio_is_sequence_header(audio->payload, audio->size)) { 665 if (SrsFlvCodec::audio_is_sequence_header(audio->payload, audio->size)) {
585 srs_freep(sh_audio); 666 srs_freep(sh_audio);
586 sh_audio = audio->copy(); 667 sh_audio = audio->copy();
587 } 668 }
  669 +
  670 + if ((ret = update_duration(audio)) != ERROR_SUCCESS) {
  671 + return ret;
  672 + }
588 673
589 - return SrsDvrPlan::on_audio(audio); 674 + if ((ret = SrsDvrPlan::on_audio(audio)) != ERROR_SUCCESS) {
  675 + return ret;
  676 + }
  677 +
  678 + return ret;
590 } 679 }
591 680
592 int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* video) 681 int SrsDvrSegmentPlan::on_video(SrsSharedPtrMessage* video)
593 { 682 {
  683 + int ret = ERROR_SUCCESS;
  684 +
594 if (SrsFlvCodec::video_is_sequence_header(video->payload, video->size)) { 685 if (SrsFlvCodec::video_is_sequence_header(video->payload, video->size)) {
595 srs_freep(sh_video); 686 srs_freep(sh_video);
596 sh_video = video->copy(); 687 sh_video = video->copy();
597 } 688 }
  689 +
  690 + if ((ret = update_duration(video)) != ERROR_SUCCESS) {
  691 + return ret;
  692 + }
598 693
599 - return SrsDvrPlan::on_video(video); 694 + if ((ret = SrsDvrPlan::on_video(video)) != ERROR_SUCCESS) {
  695 + return ret;
  696 + }
  697 +
  698 + return ret;
600 } 699 }
601 700
602 int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) 701 int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg)
603 { 702 {
604 int ret = ERROR_SUCCESS; 703 int ret = ERROR_SUCCESS;
605 704
606 - if ((ret = SrsDvrPlan::update_duration(msg)) != ERROR_SUCCESS) {  
607 - return ret;  
608 - }  
609 -  
610 srs_assert(segment); 705 srs_assert(segment);
611 706
612 // ignore if duration ok. 707 // ignore if duration ok.
613 - if (segment_duration <= 0 || segment->duration < segment_duration) { 708 + if (segment_duration <= 0 || !segment->is_overflow(segment_duration)) {
614 return ret; 709 return ret;
615 } 710 }
616 711
617 // when wait keyframe, ignore if no frame arrived. 712 // when wait keyframe, ignore if no frame arrived.
618 // @see https://github.com/winlinvip/simple-rtmp-server/issues/177 713 // @see https://github.com/winlinvip/simple-rtmp-server/issues/177
619 - if (_srs_config->get_dvr_wait_keyframe(_req->vhost)) { 714 + if (_srs_config->get_dvr_wait_keyframe(req->vhost)) {
620 if (!msg->is_video()) { 715 if (!msg->is_video()) {
621 return ret; 716 return ret;
622 } 717 }
@@ -632,14 +727,12 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) @@ -632,14 +727,12 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg)
632 } 727 }
633 728
634 // reap segment 729 // reap segment
635 - if ((ret = flv_close()) != ERROR_SUCCESS) {  
636 - segment->reset(); 730 + if ((ret = segment->close()) != ERROR_SUCCESS) {
637 return ret; 731 return ret;
638 } 732 }
639 - on_unpublish();  
640 733
641 // open new flv file 734 // open new flv file
642 - if ((ret = open_new_segment()) != ERROR_SUCCESS) { 735 + if ((ret = segment->open()) != ERROR_SUCCESS) {
643 return ret; 736 return ret;
644 } 737 }
645 738
@@ -654,9 +747,9 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg) @@ -654,9 +747,9 @@ int SrsDvrSegmentPlan::update_duration(SrsSharedPtrMessage* msg)
654 return ret; 747 return ret;
655 } 748 }
656 749
657 -SrsDvr::SrsDvr(SrsSource* source) 750 +SrsDvr::SrsDvr(SrsSource* s)
658 { 751 {
659 - _source = source; 752 + source = s;
660 plan = NULL; 753 plan = NULL;
661 } 754 }
662 755
@@ -665,21 +758,21 @@ SrsDvr::~SrsDvr() @@ -665,21 +758,21 @@ SrsDvr::~SrsDvr()
665 srs_freep(plan); 758 srs_freep(plan);
666 } 759 }
667 760
668 -int SrsDvr::initialize(SrsRequest* req) 761 +int SrsDvr::initialize(SrsRequest* r)
669 { 762 {
670 int ret = ERROR_SUCCESS; 763 int ret = ERROR_SUCCESS;
671 764
672 srs_freep(plan); 765 srs_freep(plan);
673 - plan = SrsDvrPlan::create_plan(req->vhost); 766 + plan = SrsDvrPlan::create_plan(r->vhost);
674 767
675 - if ((ret = plan->initialize(_source, req)) != ERROR_SUCCESS) { 768 + if ((ret = plan->initialize(source, r)) != ERROR_SUCCESS) {
676 return ret; 769 return ret;
677 } 770 }
678 771
679 return ret; 772 return ret;
680 } 773 }
681 774
682 -int SrsDvr::on_publish(SrsRequest* /*req*/) 775 +int SrsDvr::on_publish(SrsRequest* /*r*/)
683 { 776 {
684 int ret = ERROR_SUCCESS; 777 int ret = ERROR_SUCCESS;
685 778
@@ -695,11 +788,11 @@ void SrsDvr::on_unpublish() @@ -695,11 +788,11 @@ void SrsDvr::on_unpublish()
695 plan->on_unpublish(); 788 plan->on_unpublish();
696 } 789 }
697 790
698 -int SrsDvr::on_meta_data(SrsOnMetaDataPacket* metadata) 791 +int SrsDvr::on_meta_data(SrsOnMetaDataPacket* m)
699 { 792 {
700 int ret = ERROR_SUCCESS; 793 int ret = ERROR_SUCCESS;
701 794
702 - if ((ret = plan->on_meta_data(metadata)) != ERROR_SUCCESS) { 795 + if ((ret = plan->on_meta_data(m)) != ERROR_SUCCESS) {
703 return ret; 796 return ret;
704 } 797 }
705 798
@@ -41,16 +41,34 @@ class SrsOnMetaDataPacket; @@ -41,16 +41,34 @@ class SrsOnMetaDataPacket;
41 class SrsSharedPtrMessage; 41 class SrsSharedPtrMessage;
42 class SrsFileWriter; 42 class SrsFileWriter;
43 class SrsFlvEncoder; 43 class SrsFlvEncoder;
  44 +class SrsDvrPlan;
44 45
45 #include <srs_app_source.hpp> 46 #include <srs_app_source.hpp>
46 #include <srs_app_reload.hpp> 47 #include <srs_app_reload.hpp>
47 48
48 /** 49 /**
49 * a piece of flv segment. 50 * a piece of flv segment.
  51 +* when open segment, support start at 0 or not.
50 */ 52 */
51 -class SrsFlvSegment 53 +class SrsFlvSegment : public ISrsReloadHandler
52 { 54 {
53 -public: 55 +private:
  56 + SrsSource* source;
  57 + SrsRequest* req;
  58 + SrsDvrPlan* plan;
  59 +private:
  60 + /**
  61 + * the underlayer dvr stream.
  62 + * if close, the flv is reap and closed.
  63 + * if open, new flv file is crote.
  64 + */
  65 + SrsFlvEncoder* enc;
  66 + SrsRtmpJitter* jitter;
  67 + SrsRtmpJitterAlgorithm jitter_algorithm;
  68 + SrsFileWriter* fs;
  69 +private:
  70 + std::string tmp_flv_file;
  71 +private:
54 /** 72 /**
55 * current segment flv file path. 73 * current segment flv file path.
56 */ 74 */
@@ -81,10 +99,56 @@ public: @@ -81,10 +99,56 @@ public:
81 */ 99 */
82 int64_t stream_previous_pkt_time; 100 int64_t stream_previous_pkt_time;
83 public: 101 public:
84 - SrsFlvSegment(); 102 + SrsFlvSegment(SrsDvrPlan* p);
85 virtual ~SrsFlvSegment(); 103 virtual ~SrsFlvSegment();
86 public: 104 public:
87 - virtual void reset(); 105 + /**
  106 + * initialize the segment.
  107 + */
  108 + virtual int initialize(SrsSource* s, SrsRequest* r);
  109 + /**
  110 + * whether segment is overflow.
  111 + */
  112 + virtual bool is_overflow(int64_t max_duration);
  113 + /**
  114 + * open new segment file, timestamp start at 0 for fresh flv file.
  115 + * @remark ignore when already open.
  116 + */
  117 + virtual int open();
  118 + /**
  119 + * close current segment.
  120 + * @remark ignore when already closed.
  121 + */
  122 + virtual int close();
  123 + /**
  124 + * write the metadata to segment.
  125 + */
  126 + virtual int write_metadata(SrsOnMetaDataPacket* metadata);
  127 + /**
  128 + * @param __audio, directly ptr, copy it if need to save it.
  129 + */
  130 + virtual int write_audio(SrsSharedPtrMessage* __audio);
  131 + /**
  132 + * @param __video, directly ptr, copy it if need to save it.
  133 + */
  134 + virtual int write_video(SrsSharedPtrMessage* __video);
  135 +private:
  136 + /**
  137 + * generate the flv segment path.
  138 + */
  139 + virtual std::string generate_path();
  140 + /**
  141 + * create flv jitter. load jitter when flv exists.
  142 + * @param loads_from_flv whether loads the jitter from exists flv file.
  143 + */
  144 + virtual int create_jitter(bool loads_from_flv);
  145 + /**
  146 + * when update the duration of segment by rtmp msg.
  147 + */
  148 + virtual int on_update_duration(SrsSharedPtrMessage* msg);
  149 +// interface ISrsReloadHandler
  150 +public:
  151 + virtual int on_reload_vhost_dvr(std::string vhost);
88 }; 152 };
89 153
90 /** 154 /**
@@ -94,32 +158,25 @@ public: @@ -94,32 +158,25 @@ public:
94 * 2. reap flv: when to reap the flv and start new piece. 158 * 2. reap flv: when to reap the flv and start new piece.
95 */ 159 */
96 // TODO: FIXME: the plan is too fat, refine me. 160 // TODO: FIXME: the plan is too fat, refine me.
97 -class SrsDvrPlan : public ISrsReloadHandler 161 +class SrsDvrPlan
98 { 162 {
99 -private:  
100 - /**  
101 - * the underlayer dvr stream.  
102 - * if close, the flv is reap and closed.  
103 - * if open, new flv file is crote.  
104 - */  
105 - SrsFlvEncoder* enc;  
106 - SrsSource* _source;  
107 - SrsRtmpJitter* jitter;  
108 - SrsRtmpJitterAlgorithm jitter_algorithm; 163 +public:
  164 + friend class SrsFlvSegment;
109 protected: 165 protected:
  166 + SrsSource* source;
  167 + SrsRequest* req;
110 SrsFlvSegment* segment; 168 SrsFlvSegment* segment;
111 - SrsRequest* _req;  
112 bool dvr_enabled; 169 bool dvr_enabled;
113 - SrsFileWriter* fs;  
114 -private:  
115 - std::string tmp_flv_file;  
116 public: 170 public:
117 SrsDvrPlan(); 171 SrsDvrPlan();
118 virtual ~SrsDvrPlan(); 172 virtual ~SrsDvrPlan();
119 public: 173 public:
120 - virtual int initialize(SrsSource* source, SrsRequest* req);  
121 - virtual int on_publish(); 174 + virtual int initialize(SrsSource* s, SrsRequest* r);
  175 + virtual int on_publish() = 0;
122 virtual void on_unpublish() = 0; 176 virtual void on_unpublish() = 0;
  177 + /**
  178 + * when got metadata.
  179 + */
123 virtual int on_meta_data(SrsOnMetaDataPacket* metadata); 180 virtual int on_meta_data(SrsOnMetaDataPacket* metadata);
124 /** 181 /**
125 * @param __audio, directly ptr, copy it if need to save it. 182 * @param __audio, directly ptr, copy it if need to save it.
@@ -129,15 +186,7 @@ public: @@ -129,15 +186,7 @@ public:
129 * @param __video, directly ptr, copy it if need to save it. 186 * @param __video, directly ptr, copy it if need to save it.
130 */ 187 */
131 virtual int on_video(SrsSharedPtrMessage* __video); 188 virtual int on_video(SrsSharedPtrMessage* __video);
132 -// interface ISrsReloadHandler  
133 -public:  
134 - virtual int on_reload_vhost_dvr(std::string vhost);  
135 protected: 189 protected:
136 - virtual int flv_open(std::string stream, std::string path);  
137 - virtual int flv_close();  
138 - virtual int open_new_segment();  
139 - virtual int update_duration(SrsSharedPtrMessage* msg);  
140 - virtual int write_flv_header();  
141 virtual int on_dvr_request_sh(); 190 virtual int on_dvr_request_sh();
142 virtual int on_video_keyframe(); 191 virtual int on_video_keyframe();
143 virtual int64_t filter_timestamp(int64_t timestamp); 192 virtual int64_t filter_timestamp(int64_t timestamp);
@@ -154,6 +203,7 @@ public: @@ -154,6 +203,7 @@ public:
154 SrsDvrSessionPlan(); 203 SrsDvrSessionPlan();
155 virtual ~SrsDvrSessionPlan(); 204 virtual ~SrsDvrSessionPlan();
156 public: 205 public:
  206 + virtual int on_publish();
157 virtual void on_unpublish(); 207 virtual void on_unpublish();
158 }; 208 };
159 209
@@ -193,11 +243,11 @@ private: @@ -193,11 +243,11 @@ private:
193 class SrsDvr 243 class SrsDvr
194 { 244 {
195 private: 245 private:
196 - SrsSource* _source; 246 + SrsSource* source;
197 private: 247 private:
198 SrsDvrPlan* plan; 248 SrsDvrPlan* plan;
199 public: 249 public:
200 - SrsDvr(SrsSource* source); 250 + SrsDvr(SrsSource* s);
201 virtual ~SrsDvr(); 251 virtual ~SrsDvr();
202 public: 252 public:
203 /** 253 /**
@@ -205,12 +255,12 @@ public: @@ -205,12 +255,12 @@ public:
205 * when system initialize(encoder publish at first time, or reload), 255 * when system initialize(encoder publish at first time, or reload),
206 * initialize the dvr will reinitialize the plan, the whole dvr framework. 256 * initialize the dvr will reinitialize the plan, the whole dvr framework.
207 */ 257 */
208 - virtual int initialize(SrsRequest* req); 258 + virtual int initialize(SrsRequest* r);
209 /** 259 /**
210 * publish stream event, 260 * publish stream event,
211 * when encoder start to publish RTMP stream. 261 * when encoder start to publish RTMP stream.
212 */ 262 */
213 - virtual int on_publish(SrsRequest* req); 263 + virtual int on_publish(SrsRequest* r);
214 /** 264 /**
215 * the unpublish event., 265 * the unpublish event.,
216 * when encoder stop(unpublish) to publish RTMP stream. 266 * when encoder stop(unpublish) to publish RTMP stream.
@@ -219,7 +269,7 @@ public: @@ -219,7 +269,7 @@ public:
219 /** 269 /**
220 * get some information from metadata, it's optinal. 270 * get some information from metadata, it's optinal.
221 */ 271 */
222 - virtual int on_meta_data(SrsOnMetaDataPacket* metadata); 272 + virtual int on_meta_data(SrsOnMetaDataPacket* m);
223 /** 273 /**
224 * mux the audio packets to dvr. 274 * mux the audio packets to dvr.
225 * @param __audio, directly ptr, copy it if need to save it. 275 * @param __audio, directly ptr, copy it if need to save it.
@@ -106,7 +106,8 @@ int SrsGoApiV1::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r) @@ -106,7 +106,8 @@ int SrsGoApiV1::serve_http(ISrsGoHttpResponseWriter* w, SrsHttpMessage* r)
106 << __SRS_JFIELD_STR("authors", "the primary authors and contributors") << __SRS_JFIELD_CONT 106 << __SRS_JFIELD_STR("authors", "the primary authors and contributors") << __SRS_JFIELD_CONT
107 << __SRS_JFIELD_STR("requests", "the request itself, for http debug") << __SRS_JFIELD_CONT 107 << __SRS_JFIELD_STR("requests", "the request itself, for http debug") << __SRS_JFIELD_CONT
108 << __SRS_JFIELD_STR("vhosts", "dumps vhost to json") << __SRS_JFIELD_CONT 108 << __SRS_JFIELD_STR("vhosts", "dumps vhost to json") << __SRS_JFIELD_CONT
109 - << __SRS_JFIELD_STR("streams", "dumps streams to json") 109 + << __SRS_JFIELD_STR("streams", "dumps streams to json") << __SRS_JFIELD_CONT
  110 + << __SRS_JFIELD_STR("dvrs", "query or control the dvr plan")
110 << __SRS_JOBJECT_END 111 << __SRS_JOBJECT_END
111 << __SRS_JOBJECT_END; 112 << __SRS_JOBJECT_END;
112 113