winlin

refine the api server

... ... @@ -548,6 +548,7 @@ class ArmServer:
data["heartbeat"] = self.heartbeat
data["heartbeat_h"] = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(self.heartbeat))
data["summaries"] = "http://%s:1985/api/v1/summaries"%(self.ip)
data["console"] = "http://ossrs.net/console/ng_index.html#/summaries?host=%s&port=1985"%(self.ip)
return data
'''
... ... @@ -579,29 +580,6 @@ class RESTServers(object):
if not has_dead_node:
break
def __json_dump_nodes(self, peers):
data = []
for node in peers:
data.append(node.json_dump())
return data
def __get_peers_for_play(self, device_id):
peers = []
for node in self.__nodes:
if node.device_id == device_id:
peers.append(node)
return peers
def __select_peer(self, peers, device_id):
target = None
for peer in peers:
if target is None or target.clients > peer.clients:
target = peer
if target is None:
return None
target.clients += 1
return target.ip
'''
post to update server ip.
request body: the new raspberry-pi server ip. TODO: FIXME: more info.
... ... @@ -637,183 +615,21 @@ class RESTServers(object):
self.__lock.release()
'''
id canbe:
pi: the pi demo, raspberry-pi default demo.
device_id: the id of device to get.
action: canbe play or mgmt, play to play the inest stream, mgmt to get api/v1/versions.
stream: the stream to play, for example, live/livestream for http://server:8080/live/livestream.html
meeting: the meeting demo. jump to web meeting if index is None.
device_id: the id of device to get.
local: whether view the local raspberry-pi stream. if "true", redirect to the local(internal) api server.
index: the meeting stream index, dynamic get the streams from root.api.v1.chats.get_url_by_index(index)
gslb: the gslb to get edge ip
device_id: the id of device to get.
ingest: deprecated, alias for pi.
get all servers which report to this api-server.
'''
def GET(self, id=None, action="play", stream="live/livestream", index=None, local="false", device_id=None):
def GET(self):
enable_crossdomain()
try:
self.__lock.acquire()
self.__refresh_nodes()
data = self.__json_dump_nodes(self.__nodes)
server_ip = "demo.chnvideo.com"
ip = cherrypy.request.remote.ip
if type is not None:
peers = self.__get_peers_for_play(device_id)
if len(peers) > 0:
server_ip = self.__select_peer(peers, device_id)
data = []
for node in self.__nodes:
data.append(node.json_dump())
# demo, srs meeting urls.
if id == "meeting":
if index is None:
url = "http://%s:8085"%(server_ip)
elif local == "true":
url = "http://%s:8085/api/v1/servers?id=%s&index=%s&local=false"%(server_ip, id, index)
else:
rtmp_url = root.api.v1.chats.get_url_by_index(index)
if rtmp_url is None:
return "meeting stream not found"
urls = rtmp_url.replace("...vhost...", "?vhost=").replace("rtmp://", "").split("/")
hls_url = "http://%s:8080/%s/%s.m3u8"%(urls[0].strip(":19350").strip(":1935"), urls[1].split("?")[0], urls[2])
return self.__generate_hls(hls_url)
# raspberry-pi urls.
elif id == "ingest" or id == "pi":
if action == "play":
url = "http://%s:8080/%s.html"%(server_ip, stream)
elif action == "rtmp":
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])
elif action == "hls":
hls_url = "http://%s:8080/%s.m3u8"%(server_ip, stream);
if stream.startswith("http://"):
hls_url = stream;
return self.__generate_hls(hls_url.replace(".m3u8.m3u8", ".m3u8"))
else:
url = "http://%s:8080/api/v1/versions"%(server_ip)
elif id == "gslb":
return json.dumps({"code":Error.success, "data": {
"edge":server_ip, "client":ip,
"peers":self.__json_dump_nodes(peers),
"streams": {
"pi": {
"livestream": {
"sales-pi-hls": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-arm&stream=live/livestream",
"dev-pi-hls": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-dev-arm&stream=live/livestream"
},
"cztv": {
"sales-pi-hls": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-arm&stream=live/rtmp_cztv01-sd",
"dev-pi-hls": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-dev-arm&stream=live/rtmp_cztv01-sd"
}
},
"hiwifi": {
"hls": {
"dev-livestream": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-dev-hiwifi&stream=live/livestream",
"sales-livestream": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-hiwifi&stream=live/livestream"
},
"rtmp":{
"dev-livestream": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-dev-hiwifi&stream=live/livestream",
"sales-livestream": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-sales-hiwifi&stream=live/livestream"
},
"meiyi": {
"rtmp": {
"avatar": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-sales-hiwifi&stream=live/avatar",
"MenInBlack3": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-sales-hiwifi&stream=live/MenInBlack3",
"skyfall": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-sales-hiwifi&stream=live/skyfall",
"SpiderMan": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-sales-hiwifi&stream=live/SpiderMan",
"thehobbit": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-sales-hiwifi&stream=live/thehobbit",
"thorthedarkworld": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-sales-hiwifi&stream=live/thorthedarkworld",
"transformers": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-sales-hiwifi&stream=live/transformers"
},
"hls": {
"avatar": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-hiwifi&stream=live/avatar",
"MenInBlack3": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-hiwifi&stream=live/MenInBlack3",
"skyfall": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-hiwifi&stream=live/skyfall",
"SpiderMan": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-hiwifi&stream=live/SpiderMan",
"thehobbit": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-hiwifi&stream=live/thehobbit",
"thorthedarkworld": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-hiwifi&stream=live/thorthedarkworld",
"transformers": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-sales-hiwifi&stream=live/transformers"
}
}
},
"cubieboard": {
"meiyi": {
"rtmp": {
"livesteam": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard&stream=live/livestream",
"stream1": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard&stream=live/stream1",
"stream2": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard&stream=live/stream2",
"stream3": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard&stream=live/stream3",
"stream4": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard&stream=live/stream4",
"stream5": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard&stream=live/stream5",
"stream6": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard&stream=live/stream6",
"stream7": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard&stream=live/stream7"
},
"hls": {
"livesteam": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard&stream=live/livestream",
"stream1": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard&stream=live/stream1",
"stream2": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard&stream=live/stream2",
"stream3": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard&stream=live/stream3",
"stream4": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard&stream=live/stream4",
"stream5": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard&stream=live/stream5",
"stream6": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard&stream=live/stream6",
"stream7": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard&stream=live/stream7"
}
},
"meiyi-house": {
"rtmp": {
"livesteam": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-house&stream=live/livestream"
},
"hls": {
"livesteam": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-house&stream=live/livestream"
}
},
"meiyi-bk": {
"rtmp": {
"livesteam": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/livestream",
"stream1": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream1",
"stream2": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream2",
"stream3": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream3",
"stream4": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream4",
"stream5": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream5",
"stream6": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream6",
"stream7": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream7"
},
"hls": {
"livesteam": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/livestream",
"stream1": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream1",
"stream2": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream2",
"stream3": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream3",
"stream4": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream4",
"stream5": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream5",
"stream6": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream6",
"stream7": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-bk&stream=live/stream7"
}
},
"meiyi-dev1": {
"rtmp": {
"livesteam": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-dev1&stream=live/livestream"
},
"hls": {
"livesteam": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-dev1&stream=live/livestream"
}
},
"meiyi-dev2": {
"rtmp": {
"livesteam": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=rtmp&device_id=chnvideo-meiyi-cubieboard-dev2&stream=live/livestream"
},
"hls": {
"livesteam": "http://demo.chnvideo.com:8085/api/v1/servers?id=ingest&action=hls&device_id=chnvideo-meiyi-cubieboard-dev2&stream=live/livestream"
}
}
}
}
}})
# others, default.
else:
return json.dumps(data)
#return "id=%s, action=%s, stream=%s, url=%s, index=%s, local=%s"%(id, action, stream, url, index, local)
raise cherrypy.HTTPRedirect(url)
return json.dumps(data)
finally:
self.__lock.release()
... ... @@ -827,248 +643,6 @@ class RESTServers(object):
def OPTIONS(self, *args, **kwargs):
enable_crossdomain()
def __generate_hls(self, hls_url):
return SrsUtility().hls_html(hls_url)
class SrsUtility:
def hls_html(self, hls_url):
return """
<h1>%s</h1>
<video width="640" height="360"
autoplay controls autobuffer
src="%s"
type="application/vnd.apple.mpegurl">
</video>"""%(hls_url, hls_url);
global_cdn_id = os.getpid();
class CdnNode:
def __init__(self):
global global_cdn_id
global_cdn_id += 1
self.id = str(global_cdn_id)
self.ip = None
self.origin = None
self.os = None
self.srs_status = None
self.public_ip = cherrypy.request.remote.ip
self.heartbeat = time.time()
self.clients = 0
def dead(self):
dead_time_seconds = 10
if time.time() - self.heartbeat > dead_time_seconds:
return True
return False
def json_dump(self):
data = {}
data["id"] = self.id
data["ip"] = self.ip
data["origin"] = self.origin
data["os"] = self.os
data["srs_status"] = self.srs_status
data["public_ip"] = self.public_ip
data["heartbeat"] = self.heartbeat
data["heartbeat_h"] = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(self.heartbeat))
data["clients"] = self.clients
data["summaries"] = "http://%s:1985/api/v1/summaries"%(self.ip)
return data
'''
the cdn nodes list
'''
class RESTNodes(object):
exposed = True
def __init__(self):
self.__nodes = []
# @remark, if there is shared data, such as the self.__nodes,
# we must use lock for cherrypy, or the cpu of cherrypy will high
# and performance suffer.
self.__lock = threading.Lock()
def __get_node(self, id):
for node in self.__nodes:
if node.id == id:
return node
return None
def __refresh_nodes(self):
while len(self.__nodes) > 0:
has_dead_node = False
for node in self.__nodes:
if node.dead():
self.__nodes.remove(node)
has_dead_node = True
if not has_dead_node:
break
def __get_peers(self, target_node):
peers = []
for node in self.__nodes:
if str(node.id).strip() == str(target_node.id).strip():
continue
if node.public_ip == target_node.public_ip and node.srs_status == "running" and node.origin != target_node.ip:
peers.append(node)
return peers
def __get_peers_for_play(self, ip):
peers = []
for node in self.__nodes:
if node.public_ip == ip and node.srs_status == "running":
peers.append(node)
return peers
def __json_dump_nodes(self, peers):
data = []
for node in peers:
data.append(node.json_dump())
return data
def __select_peer(self, peers, ip):
target = None
for peer in peers:
if target is None or target.clients > peer.clients:
target = peer
if target is None:
return None
target.clients += 1
return target.ip
def GET(self, type=None, format=None, origin=None, vhost=None, port=None, stream=None, node_id=None):
enable_crossdomain()
try:
self.__lock.acquire()
self.__refresh_nodes()
data = self.__json_dump_nodes(self.__nodes)
ip = cherrypy.request.remote.ip
if type is not None:
server = origin
peers = self.__get_peers_for_play(ip)
if len(peers) > 0:
server = self.__select_peer(peers, ip)
if type == "hls":
hls_url = "http://%s:%s/%s.m3u8"%(server, port, stream)
hls_url = hls_url.replace(".m3u8.m3u8", ".m3u8")
if format == "html":
return SrsUtility().hls_html(hls_url)
else:
#return hls_url
raise cherrypy.HTTPRedirect(hls_url)
elif type == "rtmp":
rtmp_url = "rtmp://%s:%s/%s?vhost=%s/%s"%(server, port, stream.split("/")[0], vhost, stream.split("/")[1])
if format == "html":
html = "%s?server=%s&port=%s&vhost=%s&app=%s&stream=%s&autostart=true"%(
"http://demo.chnvideo.com:8085/srs/trunk/research/players/srs_player.html",
server, port, vhost, stream.split("/")[0], stream.split("/")[1])
#return html
raise cherrypy.HTTPRedirect(html)
return rtmp_url
elif type == "gslb":
return json.dumps({"code":Error.success, "data": {
"edge":server, "client":ip,
"peers":self.__json_dump_nodes(peers),
"streams": {
"cztv": {
"hls": "http://demo.chnvideo.com:8085/api/v1/nodes?type=hls&format=html&origin=demo.chnvideo.com&port=8080&stream=live/rtmp_cztv01-sd",
"rtmp": "http://demo.chnvideo.com:8085/api/v1/nodes?type=rtmp&format=html&origin=demo.chnvideo.com&vhost=android&port=1935&stream=live/rtmp_cztv01-sd"
},
"livestream": {
"hls": "http://demo.chnvideo.com:8085/api/v1/nodes?type=hls&format=html&origin=demo.chnvideo.com&port=8080&stream=live/livestream",
"rtmp": "http://demo.chnvideo.com:8085/api/v1/nodes?type=rtmp&format=html&origin=demo.chnvideo.com&vhost=demo.srs.com&port=1935&stream=live/livestream"
},
"apk": "http://demo.chnvideo.com/android.srs.apk"
}
}})
return json.dumps({"code":Error.success, "data": data})
finally:
self.__lock.release()
def PUT(self):
enable_crossdomain()
try:
self.__lock.acquire()
req = cherrypy.request.body.read()
trace("put to nodes, 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 json.dumps({"code":code, "data": None})
id = str(json_req["id"])
node = self.__get_node(id)
if node is None:
code = Error.cdn_node_not_exists
trace("cdn node not exists, req=%s, id=%s, code=%s"%(req, id, code))
return json.dumps({"code":code, "data": None})
node.heartbeat = time.time()
node.srs_status = str(json_req["srs_status"])
node.ip = str(json_req["ip"])
if "origin" in json_req:
node.origin = str(json_req["origin"]);
node.public_ip = cherrypy.request.remote.ip
# reset if restart.
if node.srs_status != "running":
node.clients = 0
self.__refresh_nodes()
peers = self.__get_peers(node)
peers_data = self.__json_dump_nodes(peers)
res = json.dumps({"code":Error.success, "data": {"id":node.id, "peers":peers_data}})
trace(res)
return res
finally:
self.__lock.release()
def POST(self):
enable_crossdomain()
try:
self.__lock.acquire()
req = cherrypy.request.body.read()
trace("post to nodes, 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 json.dumps({"code":code, "data": None})
node = CdnNode()
node.ip = str(json_req["ip"]);
node.os = str(json_req["os"]);
if "origin" in json_req:
node.origin = str(json_req["origin"]);
node.srs_status = str(json_req["srs_status"])
self.__nodes.append(node)
self.__refresh_nodes()
peers = self.__get_peers(node)
peers_data = self.__json_dump_nodes(peers)
res = json.dumps({"code":Error.success, "data": {"id":node.id, "peers":peers_data}})
trace(res)
return res
finally:
self.__lock.release()
def OPTIONS(self, *args, **kwargs):
enable_crossdomain()
global_chat_id = os.getpid();
'''
... ... @@ -1235,7 +809,6 @@ class V1(object):
self.proxy = RESTProxy()
self.chats = RESTChats()
self.servers = RESTServers()
self.nodes = RESTNodes()
def GET(self):
enable_crossdomain();
return json.dumps({"code":Error.success, "urls":{
... ... @@ -1244,26 +817,10 @@ class V1(object):
"sessions": "for srs http callback, to handle the sessions requests: client play/stop stream",
"dvrs": "for srs http callback, to handle the dvr requests: dvr stream.",
"chats": "for srs demo meeting, the chat streams, public chat room.",
"nodes": {
"summary": "for srs cdn node",
"POST ip=node_ip&os=node_os": "register a new node",
"GET": "get the active edge nodes",
"GET type=gslb&origin=demo.chnvideo.com": "get the gslb edge ip",
"GET type=hls&format=html&origin=demo.chnvideo.com&port=8080&stream=live/livestream": "get the play url, html for hls",
"GET type=rtmp&format=html&origin=demo.chnvideo.com&vhost=demo.srs.com&port=1935&stream=live/livestream": "get the play url, for rtmp"
},
"servers": {
"summary": "for srs raspberry-pi and meeting demo",
"GET": "get the current raspberry-pi servers info",
"GET id=gslb&device_id=chnvideo-sales-arm": "get the gslb edge ip",
"POST ip=node_ip&device_id=device_id": "the new raspberry-pi server info.",
"GET id=ingest&action=play&stream=live/livestream": "play the ingest HLS stream on raspberry-pi",
"GET id=ingest&action=rtmp&stream=live/livestream": "play the ingest RTMP stream on raspberry-pi",
"GET id=ingest&action=hls&stream=live/livestream": "play the ingest HLS stream on raspberry-pi",
"GET id=ingest&action=mgmt": "open the HTTP api url of raspberry-pi",
"GET id=meeting": "redirect to local raspberry-pi meeting url(local ignored)",
"GET id=meeting&local=false&index=0": "play the first(index=0) meeting HLS stream on demo.chnvideo.com(not local)",
"GET id=meeting&local=true&index=0": "play the first(index=0) meeting HLS stream on local server(local x86/x64 server), warn: raspberry-pi donot support HLS meeting."
"POST ip=node_ip&device_id=device_id": "the new raspberry-pi server info."
}
}});
def OPTIONS(self, *args, **kwargs):
... ...