正在显示
1 个修改的文件
包含
136 行增加
和
14 行删除
@@ -326,6 +326,38 @@ class RESTSessions(object): | @@ -326,6 +326,38 @@ class RESTSessions(object): | ||
326 | 326 | ||
327 | return code | 327 | return code |
328 | 328 | ||
329 | +global_arm_server_id = os.getpid(); | ||
330 | +class ArmServer: | ||
331 | + def __init__(self): | ||
332 | + global global_arm_server_id | ||
333 | + global_arm_server_id += 1 | ||
334 | + | ||
335 | + self.id = str(global_arm_server_id) | ||
336 | + self.ip = None | ||
337 | + self.device_id = None | ||
338 | + | ||
339 | + self.public_ip = cherrypy.request.remote.ip | ||
340 | + self.heartbeat = time.time() | ||
341 | + | ||
342 | + self.clients = 0 | ||
343 | + | ||
344 | + def dead(self): | ||
345 | + dead_time_seconds = 20 | ||
346 | + if time.time() - self.heartbeat > dead_time_seconds: | ||
347 | + return True | ||
348 | + return False | ||
349 | + | ||
350 | + def json_dump(self): | ||
351 | + data = {} | ||
352 | + data["id"] = self.id | ||
353 | + data["ip"] = self.ip | ||
354 | + data["device_id"] = self.device_id | ||
355 | + data["public_ip"] = self.public_ip | ||
356 | + data["heartbeat"] = self.heartbeat | ||
357 | + data["heartbeat_h"] = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(self.heartbeat)) | ||
358 | + data["summaries"] = "http://%s:1985/api/v1/summaries"%(self.ip) | ||
359 | + return data | ||
360 | + | ||
329 | ''' | 361 | ''' |
330 | the server list | 362 | the server list |
331 | ''' | 363 | ''' |
@@ -333,8 +365,49 @@ class RESTServers(object): | @@ -333,8 +365,49 @@ class RESTServers(object): | ||
333 | exposed = True | 365 | exposed = True |
334 | 366 | ||
335 | def __init__(self): | 367 | def __init__(self): |
368 | + self.__nodes = [] | ||
369 | + | ||
336 | self.__last_update = datetime.datetime.now(); | 370 | self.__last_update = datetime.datetime.now(); |
337 | - self.__server_ip = "192.168.1.142"; | 371 | + server_ip = "192.168.1.142"; |
372 | + | ||
373 | + def __get_node(self, device_id): | ||
374 | + for node in self.__nodes: | ||
375 | + if node.device_id == device_id: | ||
376 | + return node | ||
377 | + return None | ||
378 | + | ||
379 | + def __refresh_nodes(self): | ||
380 | + has_dead_node = False | ||
381 | + while True: | ||
382 | + for node in self.__nodes: | ||
383 | + if node.dead(): | ||
384 | + self.__nodes.remove(node) | ||
385 | + has_dead_node = True | ||
386 | + if not has_dead_node: | ||
387 | + break | ||
388 | + | ||
389 | + def __json_dump_nodes(self, peers): | ||
390 | + data = [] | ||
391 | + for node in peers: | ||
392 | + data.append(node.json_dump()) | ||
393 | + return data | ||
394 | + | ||
395 | + def __get_peers_for_play(self, device_id): | ||
396 | + peers = [] | ||
397 | + for node in self.__nodes: | ||
398 | + if node.device_id == device_id: | ||
399 | + peers.append(node) | ||
400 | + return peers | ||
401 | + | ||
402 | + def __select_peer(self, peers, device_id): | ||
403 | + target = None | ||
404 | + for peer in peers: | ||
405 | + if target is None or target.clients > peer.clients: | ||
406 | + target = peer | ||
407 | + if target is None: | ||
408 | + return None | ||
409 | + target.clients += 1 | ||
410 | + return target.ip | ||
338 | 411 | ||
339 | ''' | 412 | ''' |
340 | post to update server ip. | 413 | post to update server ip. |
@@ -344,29 +417,60 @@ class RESTServers(object): | @@ -344,29 +417,60 @@ class RESTServers(object): | ||
344 | enable_crossdomain() | 417 | enable_crossdomain() |
345 | 418 | ||
346 | req = cherrypy.request.body.read() | 419 | req = cherrypy.request.body.read() |
347 | - self.__server_ip = req; | ||
348 | - self.__last_update = datetime.datetime.now(); | 420 | + trace("post to nodes, req=%s"%(req)) |
421 | + try: | ||
422 | + json_req = json.loads(req) | ||
423 | + except Exception, ex: | ||
424 | + code = Error.system_parse_json | ||
425 | + trace("parse the request to json failed, req=%s, ex=%s, code=%s"%(req, ex, code)) | ||
426 | + return json.dumps({"code":code, "data": None}) | ||
349 | 427 | ||
350 | - return self.__server_ip | 428 | + device_id = json_req["device_id"] |
429 | + node = self.__get_node(device_id) | ||
430 | + if node is None: | ||
431 | + node = ArmServer() | ||
432 | + self.__nodes.append(node) | ||
433 | + | ||
434 | + node.ip = json_req["ip"] | ||
435 | + node.device_id = device_id | ||
436 | + node.public_ip = cherrypy.request.remote.ip | ||
437 | + node.heartbeat = time.time() | ||
438 | + | ||
439 | + return json.dumps({"code":Error.success, "data": {"id":node.id}}) | ||
351 | 440 | ||
352 | ''' | 441 | ''' |
353 | id canbe: | 442 | id canbe: |
354 | pi: the pi demo, raspberry-pi default demo. | 443 | pi: the pi demo, raspberry-pi default demo. |
444 | + device_id: the id of device to get. | ||
355 | action: canbe play or mgmt, play to play the inest stream, mgmt to get api/v1/versions. | 445 | action: canbe play or mgmt, play to play the inest stream, mgmt to get api/v1/versions. |
356 | stream: the stream to play, for example, live/livestream for http://server:8080/live/livestream.html | 446 | stream: the stream to play, for example, live/livestream for http://server:8080/live/livestream.html |
357 | meeting: the meeting demo. jump to web meeting if index is None. | 447 | meeting: the meeting demo. jump to web meeting if index is None. |
448 | + device_id: the id of device to get. | ||
358 | local: whether view the local raspberry-pi stream. if "true", redirect to the local(internal) api server. | 449 | local: whether view the local raspberry-pi stream. if "true", redirect to the local(internal) api server. |
359 | index: the meeting stream index, dynamic get the streams from root.api.v1.chats.get_url_by_index(index) | 450 | index: the meeting stream index, dynamic get the streams from root.api.v1.chats.get_url_by_index(index) |
451 | + gslb: the gslb to get edge ip | ||
452 | + device_id: the id of device to get. | ||
360 | ingest: deprecated, alias for pi. | 453 | ingest: deprecated, alias for pi. |
361 | ''' | 454 | ''' |
362 | - def GET(self, id=None, action="play", stream="live/livestream", index=None, local="false"): | 455 | + def GET(self, id=None, action="play", stream="live/livestream", index=None, local="false", device_id=None): |
363 | enable_crossdomain() | 456 | enable_crossdomain() |
457 | + | ||
458 | + self.__refresh_nodes() | ||
459 | + data = self.__json_dump_nodes(self.__nodes) | ||
460 | + | ||
461 | + server_ip = "demo.chnvideo.com" | ||
462 | + ip = cherrypy.request.remote.ip | ||
463 | + if type is not None: | ||
464 | + peers = self.__get_peers_for_play(device_id) | ||
465 | + if len(peers) > 0: | ||
466 | + server_ip = self.__select_peer(peers, device_id) | ||
467 | + | ||
364 | # demo, srs meeting urls. | 468 | # demo, srs meeting urls. |
365 | if id == "meeting": | 469 | if id == "meeting": |
366 | if index is None: | 470 | if index is None: |
367 | - url = "http://%s:8085"%(self.__server_ip) | 471 | + url = "http://%s:8085"%(server_ip) |
368 | elif local == "true": | 472 | elif local == "true": |
369 | - url = "http://%s:8085/api/v1/servers?id=%s&index=%s&local=false"%(self.__server_ip, id, index) | 473 | + url = "http://%s:8085/api/v1/servers?id=%s&index=%s&local=false"%(server_ip, id, index) |
370 | else: | 474 | else: |
371 | rtmp_url = root.api.v1.chats.get_url_by_index(index) | 475 | rtmp_url = root.api.v1.chats.get_url_by_index(index) |
372 | if rtmp_url is None: | 476 | if rtmp_url is None: |
@@ -377,19 +481,30 @@ class RESTServers(object): | @@ -377,19 +481,30 @@ class RESTServers(object): | ||
377 | # raspberry-pi urls. | 481 | # raspberry-pi urls. |
378 | elif id == "ingest" or id == "pi": | 482 | elif id == "ingest" or id == "pi": |
379 | if action == "play": | 483 | if action == "play": |
380 | - url = "http://%s:8080/%s.html"%(self.__server_ip, stream) | 484 | + url = "http://%s:8080/%s.html"%(server_ip, stream) |
381 | elif action == "rtmp": | 485 | elif action == "rtmp": |
382 | - url = "../../players/srs_player.html?server=%s&vhost=%s&app=%s&stream=%s&autostart=true"%(self.__server_ip, self.__server_ip, stream.split("/")[0], stream.split("/")[1]) | 486 | + url = "../../players/srs_player.html?server=%s&vhost=%s&app=%s&stream=%s&autostart=true"%(server_ip, server_ip, stream.split("/")[0], stream.split("/")[1]) |
383 | elif action == "hls": | 487 | elif action == "hls": |
384 | - hls_url = "http://%s:8080/%s"%(self.__server_ip, stream); | 488 | + hls_url = "http://%s:8080/%s.m3u8"%(server_ip, stream); |
385 | if stream.startswith("http://"): | 489 | if stream.startswith("http://"): |
386 | hls_url = stream; | 490 | hls_url = stream; |
387 | return self.__generate_hls(hls_url.replace(".m3u8.m3u8", ".m3u8")) | 491 | return self.__generate_hls(hls_url.replace(".m3u8.m3u8", ".m3u8")) |
388 | else: | 492 | else: |
389 | - url = "http://%s:8080/api/v1/versions"%(self.__server_ip) | 493 | + url = "http://%s:8080/api/v1/versions"%(server_ip) |
494 | + elif id == "gslb": | ||
495 | + return json.dumps({"code":Error.success, "data": { | ||
496 | + "edge":server_ip, "client":ip, | ||
497 | + "peers":self.__json_dump_nodes(peers), | ||
498 | + "streams": { | ||
499 | + "hls-livestream-sales": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-arm&stream=live/livestream", | ||
500 | + "hls-cztv-sales": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-arm&stream=live/rtmp_cztv01-sd", | ||
501 | + "hls-livestream-dev": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-dev-arm&stream=live/livestream", | ||
502 | + "hls-cztv-dev": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-dev-arm&stream=live/rtmp_cztv01-sd" | ||
503 | + } | ||
504 | + }}) | ||
390 | # others, default. | 505 | # others, default. |
391 | else: | 506 | else: |
392 | - return "raspberry-pi ip: <a href='http://%s:8080' target='_blank'>%s</a>, last update: %s"%(self.__server_ip, self.__server_ip, self.__last_update) | 507 | + return json.dumps(data) |
393 | #return "id=%s, action=%s, stream=%s, url=%s, index=%s, local=%s"%(id, action, stream, url, index, local) | 508 | #return "id=%s, action=%s, stream=%s, url=%s, index=%s, local=%s"%(id, action, stream, url, index, local) |
394 | raise cherrypy.HTTPRedirect(url) | 509 | raise cherrypy.HTTPRedirect(url) |
395 | 510 | ||
@@ -425,6 +540,7 @@ class CdnNode: | @@ -425,6 +540,7 @@ class CdnNode: | ||
425 | 540 | ||
426 | self.id = str(global_cdn_id) | 541 | self.id = str(global_cdn_id) |
427 | self.ip = None | 542 | self.ip = None |
543 | + self.origin = None | ||
428 | self.os = None | 544 | self.os = None |
429 | self.srs_status = None | 545 | self.srs_status = None |
430 | 546 | ||
@@ -443,6 +559,7 @@ class CdnNode: | @@ -443,6 +559,7 @@ class CdnNode: | ||
443 | data = {} | 559 | data = {} |
444 | data["id"] = self.id | 560 | data["id"] = self.id |
445 | data["ip"] = self.ip | 561 | data["ip"] = self.ip |
562 | + data["origin"] = self.origin | ||
446 | data["os"] = self.os | 563 | data["os"] = self.os |
447 | data["srs_status"] = self.srs_status | 564 | data["srs_status"] = self.srs_status |
448 | data["public_ip"] = self.public_ip | 565 | data["public_ip"] = self.public_ip |
@@ -482,7 +599,7 @@ class RESTNodes(object): | @@ -482,7 +599,7 @@ class RESTNodes(object): | ||
482 | for node in self.__nodes: | 599 | for node in self.__nodes: |
483 | if node.id == target_node.id: | 600 | if node.id == target_node.id: |
484 | continue | 601 | continue |
485 | - if node.public_ip == target_node.public_ip and node.srs_status == "running": | 602 | + if node.public_ip == target_node.public_ip and node.srs_status == "running" and node.origin != target_node.ip: |
486 | peers.append(node) | 603 | peers.append(node) |
487 | return peers | 604 | return peers |
488 | 605 | ||
@@ -576,6 +693,8 @@ class RESTNodes(object): | @@ -576,6 +693,8 @@ class RESTNodes(object): | ||
576 | node.heartbeat = time.time() | 693 | node.heartbeat = time.time() |
577 | node.srs_status = str(json_req["srs_status"]) | 694 | node.srs_status = str(json_req["srs_status"]) |
578 | node.ip = str(json_req["ip"]) | 695 | node.ip = str(json_req["ip"]) |
696 | + if "origin" in json_req: | ||
697 | + node.origin = str(json_req["origin"]); | ||
579 | node.public_ip = cherrypy.request.remote.ip | 698 | node.public_ip = cherrypy.request.remote.ip |
580 | # reset if restart. | 699 | # reset if restart. |
581 | if node.srs_status != "running": | 700 | if node.srs_status != "running": |
@@ -604,6 +723,8 @@ class RESTNodes(object): | @@ -604,6 +723,8 @@ class RESTNodes(object): | ||
604 | node = CdnNode() | 723 | node = CdnNode() |
605 | node.ip = str(json_req["ip"]); | 724 | node.ip = str(json_req["ip"]); |
606 | node.os = str(json_req["os"]); | 725 | node.os = str(json_req["os"]); |
726 | + if "origin" in json_req: | ||
727 | + node.origin = str(json_req["origin"]); | ||
607 | node.srs_status = str(json_req["srs_status"]) | 728 | node.srs_status = str(json_req["srs_status"]) |
608 | self.__nodes.append(node) | 729 | self.__nodes.append(node) |
609 | 730 | ||
@@ -799,7 +920,8 @@ class V1(object): | @@ -799,7 +920,8 @@ class V1(object): | ||
799 | "servers": { | 920 | "servers": { |
800 | "summary": "for srs raspberry-pi and meeting demo", | 921 | "summary": "for srs raspberry-pi and meeting demo", |
801 | "GET": "get the current raspberry-pi servers info", | 922 | "GET": "get the current raspberry-pi servers info", |
802 | - "POST body=ip": "the new raspberry-pi server ip.", | 923 | + "GET id=gslb&device_id=chnvideo-sales-arm": "get the gslb edge ip", |
924 | + "POST ip=node_ip&device_id=device_id": "the new raspberry-pi server info.", | ||
803 | "GET id=ingest&action=play&stream=live/livestream": "play the ingest HLS stream on raspberry-pi", | 925 | "GET id=ingest&action=play&stream=live/livestream": "play the ingest HLS stream on raspberry-pi", |
804 | "GET id=ingest&action=rtmp&stream=live/livestream": "play the ingest RTMP stream on raspberry-pi", | 926 | "GET id=ingest&action=rtmp&stream=live/livestream": "play the ingest RTMP stream on raspberry-pi", |
805 | "GET id=ingest&action=hls&stream=live/livestream": "play the ingest HLS stream on raspberry-pi", | 927 | "GET id=ingest&action=hls&stream=live/livestream": "play the ingest HLS stream on raspberry-pi", |
-
请 注册 或 登录 后发表评论