winlin

enhance hls, increase piece one by one.

@@ -563,6 +563,7 @@ vhost with-hls.srs.com { @@ -563,6 +563,7 @@ vhost with-hls.srs.com {
563 # whether use floor for the hls_ts_file path generation. 563 # whether use floor for the hls_ts_file path generation.
564 # if on, use floor(timestamp/hls_fragment) as the variable [timestamp], 564 # if on, use floor(timestamp/hls_fragment) as the variable [timestamp],
565 # and use enahanced algorithm to calc deviation for segment. 565 # and use enahanced algorithm to calc deviation for segment.
  566 + # @remark when floor on, recommend the hls_segment>=2*gop.
566 # default: off 567 # default: off
567 hls_ts_floor off; 568 hls_ts_floor off;
568 # the hls entry prefix, which is base url of ts url. 569 # the hls entry prefix, which is base url of ts url.
@@ -740,7 +741,8 @@ vhost hooks.callback.srs.com { @@ -740,7 +741,8 @@ vhost hooks.callback.srs.com {
740 # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", 741 # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
741 # "stream": "livestream", 742 # "stream": "livestream",
742 # "cwd": "/usr/local/srs", 743 # "cwd": "/usr/local/srs",
743 - # "file": "./objs/nginx/html/live/livestream.1420254068776-100.ts" 744 + # "file": "./objs/nginx/html/live/livestream.1420254068776-100.ts",
  745 + # "seq_no": 100
744 # } 746 # }
745 # if valid, the hook must return HTTP code 200(Stauts OK) and response 747 # if valid, the hook must return HTTP code 200(Stauts OK) and response
746 # an int value specifies the error code(0 corresponding to success): 748 # an int value specifies the error code(0 corresponding to success):
@@ -334,6 +334,99 @@ class RESTDvrs(object): @@ -334,6 +334,99 @@ class RESTDvrs(object):
334 return code 334 return code
335 335
336 ''' 336 '''
  337 +handle the hls requests: hls stream.
  338 +'''
  339 +class RESTHls(object):
  340 + exposed = True
  341 +
  342 + def GET(self):
  343 + enable_crossdomain()
  344 +
  345 + hls = {}
  346 + return json.dumps(hls)
  347 +
  348 + '''
  349 + for SRS hook: on_dvr, on_dvr_reap_segment
  350 + on_dvr:
  351 + when srs reap a dvr file, call the hook,
  352 + the request in the POST data string is a object encode by json:
  353 + {
  354 + "action": "on_dvr",
  355 + "client_id": 1985,
  356 + "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live",
  357 + "stream": "livestream",
  358 + "cwd": "/usr/local/srs",
  359 + "file": "./objs/nginx/html/live/livestream.1420254068776.flv"
  360 + }
  361 + on_dvr_reap_segment:
  362 + when api dvr specifes the callback when reap flv segment, call the hook,
  363 + the request in the POST data string is a object encode by json:
  364 + {
  365 + "action": "on_dvr_reap_segment",
  366 + "client_id": 1985,
  367 + "vhost": "video.test.com", "app": "live",
  368 + "stream": "livestream",
  369 + "cwd": "/usr/local/srs",
  370 + "file": "./objs/nginx/html/live/livestream.1420254068776.flv"
  371 + }
  372 + if valid, the hook must return HTTP code 200(Stauts OK) and response
  373 + an int value specifies the error code(0 corresponding to success):
  374 + 0
  375 + '''
  376 + def POST(self):
  377 + enable_crossdomain()
  378 +
  379 + # return the error code in str
  380 + code = Error.success
  381 +
  382 + req = cherrypy.request.body.read()
  383 + trace("post to dvrs, req=%s"%(req))
  384 + try:
  385 + json_req = json.loads(req)
  386 + except Exception, ex:
  387 + code = Error.system_parse_json
  388 + trace("parse the request to json failed, req=%s, ex=%s, code=%s"%(req, ex, code))
  389 + return str(code)
  390 +
  391 + action = json_req["action"]
  392 + if action == "on_dvr":
  393 + code = self.__on_dvr(json_req)
  394 + if action == "on_dvr_reap_segment":
  395 + code = self.__on_dvr_reap_segment(json_req)
  396 + else:
  397 + trace("invalid request action: %s"%(json_req["action"]))
  398 + code = Error.request_invalid_action
  399 +
  400 + return str(code)
  401 +
  402 + def OPTIONS(self, *args, **kwargs):
  403 + enable_crossdomain()
  404 +
  405 + def __on_dvr(self, req):
  406 + code = Error.success
  407 +
  408 + trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, cwd=%s, file=%s"%(
  409 + req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"],
  410 + req["cwd"], req["file"]
  411 + ))
  412 +
  413 + # TODO: process the on_dvr event
  414 +
  415 + return code
  416 +
  417 + def __on_dvr_reap_segment(self, req):
  418 + code = Error.success
  419 +
  420 + trace("srs %s: client id=%s, vhost=%s, app=%s, stream=%s, cwd=%s, file=%s"%(
  421 + req["action"], req["client_id"], req["vhost"], req["app"], req["stream"],
  422 + req["cwd"], req["file"]
  423 + ))
  424 +
  425 + # TODO: process the on_dvr event
  426 +
  427 + return code
  428 +
  429 +'''
337 handle the sessions requests: client play/stop stream 430 handle the sessions requests: client play/stop stream
338 ''' 431 '''
339 class RESTSessions(object): 432 class RESTSessions(object):
@@ -1133,6 +1226,7 @@ class V1(object): @@ -1133,6 +1226,7 @@ class V1(object):
1133 self.streams = RESTStreams() 1226 self.streams = RESTStreams()
1134 self.sessions = RESTSessions() 1227 self.sessions = RESTSessions()
1135 self.dvrs = RESTDvrs() 1228 self.dvrs = RESTDvrs()
  1229 + self.hls = RESTHls()
1136 self.chats = RESTChats() 1230 self.chats = RESTChats()
1137 self.servers = RESTServers() 1231 self.servers = RESTServers()
1138 self.nodes = RESTNodes() 1232 self.nodes = RESTNodes()
@@ -331,17 +331,18 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts) @@ -331,17 +331,18 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts)
331 std::string ts_file = hls_ts_file; 331 std::string ts_file = hls_ts_file;
332 ts_file = srs_path_build_stream(ts_file, req->vhost, req->app, req->stream); 332 ts_file = srs_path_build_stream(ts_file, req->vhost, req->app, req->stream);
333 if (hls_ts_floor) { 333 if (hls_ts_floor) {
334 - int64_t floor_ts = (int64_t)(srs_get_system_time_ms() / (1000 * hls_fragment)); 334 + // we always ensure the piece is increase one by one.
335 std::stringstream ts_floor; 335 std::stringstream ts_floor;
336 - ts_floor << floor_ts; 336 + ts_floor << (int64_t)(previous_floor_ts + 1);
337 ts_file = srs_string_replace(ts_file, "[timestamp]", ts_floor.str()); 337 ts_file = srs_string_replace(ts_file, "[timestamp]", ts_floor.str());
338 338
339 // dup/jmp detect for ts in floor mode. 339 // dup/jmp detect for ts in floor mode.
  340 + int64_t floor_ts = (int64_t)(srs_get_system_time_ms() / (1000 * hls_fragment));
340 if (previous_floor_ts && previous_floor_ts != floor_ts - 1) { 341 if (previous_floor_ts && previous_floor_ts != floor_ts - 1) {
341 srs_warn("hls: dup or jmp for floor ts, previous=%"PRId64", current=%"PRId64", ts=%s, deviation=%.2f", 342 srs_warn("hls: dup or jmp for floor ts, previous=%"PRId64", current=%"PRId64", ts=%s, deviation=%.2f",
342 previous_floor_ts, floor_ts, ts_file.c_str(), hls_fragment_deviation); 343 previous_floor_ts, floor_ts, ts_file.c_str(), hls_fragment_deviation);
343 } 344 }
344 - previous_floor_ts = floor_ts; 345 + previous_floor_ts++;
345 } 346 }
346 ts_file = srs_path_build_timestamp(ts_file); 347 ts_file = srs_path_build_timestamp(ts_file);
347 if (true) { 348 if (true) {