import requests from framework.settings import PROJECT_UA from app.utils import Utils from app.utils.LanguageUtils import LANG_VIEWS_USE_LANG_T class ZLMediaKitApi(): def __init__(self, logger, config): self.__logger = logger self.__config = config self.default_stream_app = "live" self.default_push_stream_app = "analyzer" self.timeout = 30 self.__logger.info("ZLMediaKitApi.__init__()") def __byteFormat(self, bytes, suffix="bps"): factor = 1024 for unit in ["", "K", "M", "G"]: if bytes < factor: return f"{bytes:.2f}{unit}{suffix}" bytes /= factor def get_wsHost(self, request_ip=None): __ip = self.__config.externalHost if __ip == "0.0.0.0" and request_ip: # (v4.725新增) 使用实时请求ip代替逻辑 __ip = request_ip __address = "ws://" + __ip + ":" + str(self.__config.mediaHttpPort) return __address def get_hlsUrl(self, app, name, request_ip=None): __ip = self.__config.externalHost if __ip == "0.0.0.0" and request_ip: # (v4.725新增) 使用实时请求ip代替逻辑 __ip = request_ip __address = "http://" + __ip + ":" + str(self.__config.mediaHttpPort) return "%s/%s/%s.hls.m3u8" % (__address , app, name) def get_httpFlvUrl(self, app, name, request_ip=None): __ip = self.__config.externalHost if __ip == "0.0.0.0" and request_ip: # (v4.725新增) 使用实时请求ip代替逻辑 __ip = request_ip __address = "http://" + __ip + ":" + str(self.__config.mediaHttpPort) return "%s/%s/%s.live.flv" % (__address, app, name) def get_rtspUrl(self, app, name, request_ip=None): __ip = self.__config.externalHost if __ip == "0.0.0.0" and request_ip: # (v4.725新增) 使用实时请求ip代替逻辑 __ip = request_ip __address = "rtsp://" + __ip + ":" + str(self.__config.mediaRtspPort) return "%s/%s/%s" % (__address, app, name) def get_rtmpUrl(self, app, name, request_ip=None): __ip = self.__config.externalHost if __ip == "0.0.0.0" and request_ip: # (v5.017新增) 使用实时请求ip代替逻辑 __ip = request_ip __address = "rtmp://" + __ip + ":" + str(self.__config.mediaRtmpPort) return "%s/%s/%s" % (__address, app, name) def get_wsMp4Url(self, app, name, request_ip=None): __ip = self.__config.externalHost if __ip == "0.0.0.0" and request_ip: # (v4.725新增) 使用实时请求ip代替逻辑 __ip = request_ip __address = "ws://" + __ip + ":" + str(self.__config.mediaHttpPort) return "%s/%s/%s.live.mp4" % (__address, app, name) def get_wsFlvUrl(self, app, name, request_ip=None): __ip = self.__config.externalHost if __ip == "0.0.0.0" and request_ip: # (v4.725新增) 使用实时请求ip代替逻辑 __ip = request_ip __address = "ws://" + __ip + ":" + str(self.__config.mediaHttpPort) return "%s/%s/%s.live.flv" % (__address, app, name) def get_httpMp4Url(self, app, name, request_ip=None): __ip = self.__config.externalHost if __ip == "0.0.0.0" and request_ip: # (v4.725新增) 使用实时请求ip代替逻辑 __ip = request_ip __address = "http://" + __ip + ":" + str(self.__config.mediaHttpPort) return "%s/%s/%s.live.mp4" % (__address, app, name) def getThreadsLoad(self): __ret = False __msg = "zlm.getThreadsLoad()" __data = [] try: url = "{host}/index/api/getThreadsLoad".format(host=self.__config.mediaHttpHost) params = { "secret": self.__config.mediaSecret } res = requests.post(url, headers={ "User-Agent": PROJECT_UA }, json=params, timeout=self.timeout) if res.status_code == 200: res_json = res.json() # print(res_json) if 0 == res_json["code"]: __ret = True __msg = "success" __data = res_json["data"] else: raise Exception(str(res_json)) else: raise Exception("status=%d" % res.status_code) except Exception as e: __msg += " e:%s"%str(e) self.__logger.warning(__msg) return __ret, __msg, __data def addStreamProxy(self, app, name, origin_url, is_audio=0, vhost="__defaultVhost__",enable_rtmp=0): __key = None # 添加成功返回的 "key" : "__defaultVhost__/proxy/0" 流的唯一标识 __msg = "zlm.addStreamProxy(app=%s,name=%s,origin_url=%s,is_audio=%s)" % (app, name, str(origin_url),str(is_audio)) try: url = "{host}/index/api/addStreamProxy".format(host=self.__config.mediaHttpHost) params = { "secret": self.__config.mediaSecret, 'vhost': vhost, 'app': app, 'stream': name, 'url': origin_url } params["rtp_type"] = 0 # rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播 # params["timeout_sec"] = 1; # 拉流超时时间,单位秒,float类型 params["enable_hls"] = 0 # 是否转换成hls协议 params["enable_mp4"] = 0 # 是否允许mp4录制 # params["enable_rtsp"] = 1 # 是否转rtsp协议 if enable_rtmp == 0: # 等于0,表示不开启rtmp转发 params["enable_rtmp"] = 0 # 是否转rtmp / flv协议 params["enable_ts"] = 0 # 是否转http - ts / ws - ts协议 # params["enable_fmp4"] = 1 # 是否转http - fmp4 / ws - fmp4协议 params["enable_audio"] = is_audio # 转协议时是否开启音频 params["add_mute_audio"] = 0 # 转协议时,无音频是否添加静音aac音频 # params["mp4_save_path"] = "" # mp4录制文件保存根目录,置空使用默认 # params["mp4_max_second"] = 1 # mp4录制切片大小,单位秒 # params["hls_save_path"] = "" # hls文件保存保存根目录,置空使用默认 res = requests.post(url, headers={ "User-Agent": PROJECT_UA }, json=params, timeout=self.timeout) if res.status_code == 200: res_json = res.json() if 0 == res_json["code"]: __key = res_json["data"]["key"] __msg = "success" else: raise Exception(str(res_json)) except Exception as e: __msg += " e:%s" % str(e) self.__logger.warning(__msg) return __key, __msg def delStreamProxy(self, app, name, vhost="__defaultVhost__"): __flag = False # "flag" : true 成功与否 __msg = "zlm.delStreamProxy(app=%s,name=%s)" % (app, name) key = "{vhost}/{app}/{name}".format(vhost=vhost, app=app, name=name) try: url = "{host}/index/api/delStreamProxy?secret={secret}&key={key}".format( host=self.__config.mediaHttpHost, secret=self.__config.mediaSecret, key=key ) res = requests.get(url, headers={ "User-Agent": PROJECT_UA }, timeout=self.timeout) if res.status_code == 200: res_json = res.json() if 0 == res_json["code"]: if res_json["data"]["flag"]: __flag = True __msg = "success" else: raise Exception(str(res_json)) else: raise Exception(str(res_json)) else: raise Exception("status=%d" % res.status_code) except Exception as e: __msg += " e:%s" % str(e) self.__logger.warning(__msg) return __flag, __msg def close_streams(self, schema, app, name, vhost="__defaultVhost__"): __ret = False __msg = "zlm.close_streams(app=%s,name=%s)" % (app, name) try: url = "{host}/index/api/close_streams?secret={secret}&schema={schema}&vhost={vhost}&app={app}&stream={stream}&force=1".format( host=self.__config.mediaHttpHost, secret=self.__config.mediaSecret, vhost=vhost, schema=schema, app=app, stream=name, ) res = requests.get(url, headers={ "User-Agent": PROJECT_UA }, timeout=self.timeout) if res.status_code == 200: res_json = res.json() if 0 == res_json["code"]: count_hit = res_json.get("count_hit",0) count_closed = res_json.get("count_closed",0) if count_hit > 0: __ret = True __msg = "success" else: raise Exception(str(res_json)) else: raise Exception(str(res_json)) else: raise Exception("status=%d" % res.status_code) except Exception as e: __msg += " e:%s" % str(e) self.__logger.warning(__msg) return __ret, __msg def openRtpServer(self, port, stream_id, tcp_mode=0): """ 开启RTP服务器(用于GB28181) Args: port: RTP端口 stream_id: 流ID(通常是channel_id) tcp_mode: TCP模式(0=UDP, 1=TCP主动, 2=TCP被动) Returns: tuple: (success, msg, actual_port) success: 是否成功 msg: 消息 actual_port: 实际分配的端口(可能与请求的不同) """ __msg = f"zlm.openRtpServer(port={port}, stream_id={stream_id}, tcp_mode={tcp_mode})" __actual_port = 0 try: url = f"{self.__config.mediaHttpHost}/index/api/openRtpServer" params = { "port": port, "tcp_mode": tcp_mode, "stream_id": stream_id, "secret": self.__config.mediaSecret } res = requests.post(url, json=params, timeout=self.timeout) if res.status_code == 200: res_json = res.json() if 0 == res_json["code"]: __actual_port = res_json.get("port", port) __msg = f"success (actual_port={__actual_port})" return True, __msg, __actual_port else: __msg = f"failed: {res_json.get('msg', 'unknown error')}" return False, __msg, 0 else: __msg = f"HTTP {res.status_code}" return False, __msg, 0 except Exception as e: __msg += f" e:{str(e)}" self.__logger.warning(__msg) return False, __msg, 0 def closeRtpServer(self, name): __hit = 0 __msg = "zlm.closeRtpServer(name=%s)" % name try: url = "{host}/index/api/closeRtpServer?secret={secret}&stream_id={stream_id}".format( host=self.__config.mediaHttpHost, secret=self.__config.mediaSecret, stream_id=name ) res = requests.get(url, headers={ "User-Agent": PROJECT_UA }, timeout=self.timeout) if res.status_code == 200: res_json = res.json() if 0 == res_json["code"]: __hit = res_json["hit"] __msg = "success" else: raise Exception(str(res_json)) else: raise Exception("status=%d" % res.status_code) except Exception as e: __msg += " e:%s" % str(e) self.__logger.warning(__msg) return __hit, __msg def getMediaList(self,request_ip=None): mediaList = [] try: url = "{host}/index/api/getMediaList?secret={secret}".format( host=self.__config.mediaHttpHost, secret=self.__config.mediaSecret ) res = requests.get(url, headers={ "User-Agent": PROJECT_UA }, timeout=self.timeout) if 200 == res.status_code: res_json = res.json() if 0 == res_json.get("code"): data = res_json.get("data") if data: __data_group = {} # 视频流按照流名称进行分组 for d in data: app = d.get("app") # 应用名 name = d.get("stream") # 流id schema = d.get("schema") # 协议 app_name = "%s_%s" % (app, name) v = __data_group.get(app_name) if not v: v = {} v[schema] = d __data_group[app_name] = v for app_name, v in __data_group.items(): schema_clients = [] index = 0 d = None for __schema, __d in v.items(): schema_clients.append({ "schema": __schema, "readerCount": __d.get("readerCount") }) if 0 == index: d = __d index += 1 if d: video_str = "无" video_codec_name = None video_width = 0 video_height = 0 audio_str = "无" tracks = d.get("tracks", None) if tracks: for track in tracks: # codec_id = track.get("codec_id","") codec_id_name = track.get("codec_id_name", "").lower() codec_type = track.get("codec_type", -1) # Video = 0, Audio = 1 # ready = track.get("ready","") if 0 == codec_type: # 视频类型 fps = track.get("fps") video_height = int(track.get("height", 0)) video_width = int(track.get("width", 0)) video_codec_name = codec_id_name video_str = "%s/%d/%dx%d" % (codec_id_name, fps, video_width, video_height) elif 1 == codec_type: # 音频类型 channels = track.get("channels") sample_bit = track.get("sample_bit") sample_rate = track.get("sample_rate") audio_str = "%s/%d/%d/%d" % ( codec_id_name, channels, sample_rate, sample_bit) produce_speed = self.__byteFormat(d.get("bytesSpeed")) # 数据产生速度,单位byte/s app = d.get("app") # 应用名 name = d.get("stream") # 流id mediaList.append({ "is_online": 1, "code": app_name, "an": app_name, "app_name": app_name, "app": app, "name": name, "produce_speed": produce_speed, "video": video_str, "video_codec_name": video_codec_name, "video_width": video_width, "video_height": video_height, "audio": audio_str, "originUrl": d.get("originUrl"), # 推流地址 "originType": d.get("originType"), # 推流地址采用的推流协议类型 "originTypeStr": d.get("originTypeStr"), # 推流地址采用的推流协议类型(字符串) "clients": d.get("totalReaderCount"), # 客户端总数量 "schema_clients": schema_clients, "videoUrl": self.get_wsMp4Url(app=app,name=name,request_ip=request_ip), # 默认播放地址(ws-fmp4) "wsHost": self.get_wsHost(request_ip=request_ip), "wsMp4Url": self.get_wsMp4Url(app=app,name=name,request_ip=request_ip) }) else: raise Exception(str(res_json)) else: raise Exception("status=%d" % res.status_code) except Exception as e: self.__logger.warning("zlm.getMediaList(request_ip=%s) e:%s" % (request_ip,str(e))) return mediaList def getMediaInfo(self, app, name, schema="rtsp", vhost="__defaultVhost__",media_http_host=None,media_secret=None): mediaInfo = {} try: if media_http_host is None: media_http_host = self.__config.mediaHttpHost if media_secret is None: media_secret = self.__config.mediaSecret url = "{host}/index/api/getMediaInfo?secret={secret}&schema={schema}&vhost={vhost}&app={app}&stream={name}".format( host=media_http_host, secret=media_secret, schema=schema, vhost=vhost, app=app, name=name ) res = requests.get(url, headers={ "User-Agent": PROJECT_UA }, timeout=self.timeout) if 200 == res.status_code: res_json = res.json() if 0 == res_json["code"]: """res_json示例 { 'aliveSecond': 851, 'app': 'live', 'bytesSpeed': 116449, 'code': 0, 'createStamp': 1757481125, 'isRecordingHLS': False, 'isRecordingMP4': False, 'originSock': { 'identifier': 'class mediakit::RtspPlayerImp-23', 'local_ip': '192.168.1.106', 'local_port': 32527, 'peer_ip': '192.168.1.15', 'peer_port': 9554 }, 'originType': 4, 'originTypeStr': 'pull', 'originUrl': 'rtsp://192.168.1.15:9554/live/cam77506144ae', 'params': '', 'readerCount': 2, 'schema': 'rtsp', 'stream': 'cam17306fb84c', 'totalBytes': 101651752, 'totalReaderCount': 3, 'tracks': [{ 'codec_id': 0, 'codec_id_name': 'H264', 'codec_type': 0, 'duration': 852751, 'fps': 25.0, 'frames': 21302, 'gop_interval_ms': 970, 'gop_size': 25, 'height': 1080, 'key_frames': 853, 'loss': 0.0, 'ready': True, 'width': 1920 }], 'vhost': '__defaultVhost__' } """ mediaInfo["aliveSecond"] = res_json.get("aliveSecond",0) # 存活时长 mediaInfo["totalReaderCount"] = res_json.get("totalReaderCount",0) tracks = res_json.get("tracks", None) if tracks: if len(tracks) > 0: for track in tracks: codec_type = int(track.get("codec_type", -1)) # Video = 0, Audio = 1 if 0 == codec_type: # 视频类型 mediaInfo["codec_id"] = track.get("codec_id") mediaInfo["video_codec_name"] = track.get("codec_id_name", "").lower() mediaInfo["video_width"] = int(track.get("width", 0)) mediaInfo["video_height"] = int(track.get("height", 0)) mediaInfo["gop_size"] = int(track.get("gop_size", 0)) mediaInfo["success"] = True break if not mediaInfo.get("success"): raise Exception(str(res_json)) else: raise Exception(str(res_json)) else: raise Exception("status=%d" % res.status_code) except Exception as e: self.__logger.warning("zlm.getMediaInfo(app=%s,name=%s,schema=%s) e:%s" % (app,name,schema,str(e))) if mediaInfo.get("success"): return mediaInfo else: return {}