正在显示
1 个修改的文件
包含
137 行增加
和
15 行删除
| @@ -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. |
| @@ -342,31 +415,62 @@ class RESTServers(object): | @@ -342,31 +415,62 @@ class RESTServers(object): | ||
| 342 | ''' | 415 | ''' |
| 343 | def POST(self): | 416 | def POST(self): |
| 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}) | ||
| 427 | + | ||
| 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() | ||
| 349 | 438 | ||
| 350 | - return self.__server_ip | 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", |
-
请 注册 或 登录 后发表评论