diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index dddecc20a..b06472438 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -563,6 +563,7 @@ vhost with-hls.srs.com { # whether use floor for the hls_ts_file path generation. # if on, use floor(timestamp/hls_fragment) as the variable [timestamp], # and use enahanced algorithm to calc deviation for segment. + # @remark when floor on, recommend the hls_segment>=2*gop. # default: off hls_ts_floor off; # the hls entry prefix, which is base url of ts url. @@ -740,7 +741,8 @@ vhost hooks.callback.srs.com { # "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", # "stream": "livestream", # "cwd": "/usr/local/srs", - # "file": "./objs/nginx/html/live/livestream.1420254068776-100.ts" + # "file": "./objs/nginx/html/live/livestream.1420254068776-100.ts", + # "seq_no": 100 # } # if valid, the hook must return HTTP code 200(Stauts OK) and response # an int value specifies the error code(0 corresponding to success): diff --git a/trunk/research/api-server/server.py b/trunk/research/api-server/server.py index 5fcd0ea63..e8fbd0418 100755 --- a/trunk/research/api-server/server.py +++ b/trunk/research/api-server/server.py @@ -333,6 +333,99 @@ class RESTDvrs(object): return code +''' +handle the hls requests: hls stream. +''' +class RESTHls(object): + exposed = True + + def GET(self): + enable_crossdomain() + + hls = {} + return json.dumps(hls) + + ''' + for SRS hook: on_dvr, on_dvr_reap_segment + on_dvr: + when srs reap a dvr file, call the hook, + the request in the POST data string is a object encode by json: + { + "action": "on_dvr", + "client_id": 1985, + "ip": "192.168.1.10", "vhost": "video.test.com", "app": "live", + "stream": "livestream", + "cwd": "/usr/local/srs", + "file": "./objs/nginx/html/live/livestream.1420254068776.flv" + } + on_dvr_reap_segment: + when api dvr specifes the callback when reap flv segment, call the hook, + the request in the POST data string is a object encode by json: + { + "action": "on_dvr_reap_segment", + "client_id": 1985, + "vhost": "video.test.com", "app": "live", + "stream": "livestream", + "cwd": "/usr/local/srs", + "file": "./objs/nginx/html/live/livestream.1420254068776.flv" + } + if valid, the hook must return HTTP code 200(Stauts OK) and response + an int value specifies the error code(0 corresponding to success): + 0 + ''' + def POST(self): + enable_crossdomain() + + # return the error code in str + code = Error.success + + req = cherrypy.request.body.read() + trace("post to dvrs, req=%s"%(req)) + try: + json_req = json.loads(req) + except Exception, ex: + code = Error.system_parse_json + trace("parse the request to json failed, req=%s, ex=%s, code=%s"%(req, ex, code)) + return str(code) + + action = json_req["action"] + if action == "on_dvr": + code = self.__on_dvr(json_req) + if action == "on_dvr_reap_segment": + code = self.__on_dvr_reap_segment(json_req) + else: + trace("invalid request action: %s"%(json_req["action"])) + code = Error.request_invalid_action + + return str(code) + + def OPTIONS(self, *args, **kwargs): + enable_crossdomain() + + def __on_dvr(self, req): + code = Error.success + + trace("srs %s: client id=%s, ip=%s, vhost=%s, app=%s, stream=%s, cwd=%s, file=%s"%( + req["action"], req["client_id"], req["ip"], req["vhost"], req["app"], req["stream"], + req["cwd"], req["file"] + )) + + # TODO: process the on_dvr event + + return code + + def __on_dvr_reap_segment(self, req): + code = Error.success + + trace("srs %s: client id=%s, vhost=%s, app=%s, stream=%s, cwd=%s, file=%s"%( + req["action"], req["client_id"], req["vhost"], req["app"], req["stream"], + req["cwd"], req["file"] + )) + + # TODO: process the on_dvr event + + return code + ''' handle the sessions requests: client play/stop stream ''' @@ -1133,6 +1226,7 @@ class V1(object): self.streams = RESTStreams() self.sessions = RESTSessions() self.dvrs = RESTDvrs() + self.hls = RESTHls() self.chats = RESTChats() self.servers = RESTServers() self.nodes = RESTNodes() diff --git a/trunk/src/app/srs_app_hls.cpp b/trunk/src/app/srs_app_hls.cpp index 6f3a76c71..be838dd8f 100644 --- a/trunk/src/app/srs_app_hls.cpp +++ b/trunk/src/app/srs_app_hls.cpp @@ -331,17 +331,18 @@ int SrsHlsMuxer::segment_open(int64_t segment_start_dts) std::string ts_file = hls_ts_file; ts_file = srs_path_build_stream(ts_file, req->vhost, req->app, req->stream); if (hls_ts_floor) { - int64_t floor_ts = (int64_t)(srs_get_system_time_ms() / (1000 * hls_fragment)); + // we always ensure the piece is increase one by one. std::stringstream ts_floor; - ts_floor << floor_ts; + ts_floor << (int64_t)(previous_floor_ts + 1); ts_file = srs_string_replace(ts_file, "[timestamp]", ts_floor.str()); // dup/jmp detect for ts in floor mode. + int64_t floor_ts = (int64_t)(srs_get_system_time_ms() / (1000 * hls_fragment)); if (previous_floor_ts && previous_floor_ts != floor_ts - 1) { srs_warn("hls: dup or jmp for floor ts, previous=%"PRId64", current=%"PRId64", ts=%s, deviation=%.2f", previous_floor_ts, floor_ts, ts_file.c_str(), hls_fragment_deviation); } - previous_floor_ts = floor_ts; + previous_floor_ts++; } ts_file = srs_path_build_timestamp(ts_file); if (true) {