winlin

update raspberry-pi demo api

@@ -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",