video_monitor/app/utils/GlobalUtils.py
2026-08-30 22:23:12 +08:00

299 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import time
import json
from datetime import datetime
import sys
from framework.settings import BASE_DIR, PROJECT_UA, PROJECT_BUILT, PROJECT_VERSION, PROJECT_FLAG, PROJECT_ADMIN_START_TIMESTAMP
from app.utils.ZLMediaKitApi import ZLMediaKitApi
from app.utils.Config import Config
from app.utils.Logger import CreateLogger
from app.utils.Database import Database
from app.utils.GB28181SipServer import GB28181SipServer
# ========== 应用名常量 ==========
APP_NAME_LIVE = "live"
APP_NAME_RTP = "rtp"
from app.utils.LanguageUtils import LANG_UI_DICT, LANG_VIEWS_USE_LANG_T
from app.models import *
# BASE_DIR 是项目根目录(扁平化后 settings.py 的父父目录即根目录)
# BASE_PARENT_DIR = str(BASE_DIR) # 扁平化后 BASE_PARENT_DIR 与 BASE_DIR 相同,均在根目录
g_filepath_settings_json = os.path.join(BASE_DIR, "settings.json")
g_filepath_config_json = os.path.join(BASE_DIR, "config.json")
g_config = Config(filepath=g_filepath_config_json)
__log_dir = os.path.join(BASE_DIR, "log")
if not os.path.exists(__log_dir):
os.makedirs(__log_dir)
__log_name = "%s%s.log" % ("monitor", datetime.now().strftime("%Y%m%d-%H%M%S"))
g_logger = CreateLogger(filepath=os.path.join(__log_dir, __log_name),
is_show_console=False,
log_debug=g_config.logDebug)
g_logger.info("%s v%s,%s" % (PROJECT_UA, PROJECT_VERSION, PROJECT_FLAG))
g_logger.info(PROJECT_BUILT)
g_logger.info("g_filepath_config_json=%s" % g_filepath_config_json)
g_logger.info("config.json:%s" % g_config.getStr())
g_logger.info("logDebug=%d" % g_config.logDebug)
__argv_extend = sys.argv[1] if len(sys.argv) >= 2 else None
g_logger.info("argv_extend=%s" % str(__argv_extend))
g_zlm = ZLMediaKitApi(logger=g_logger, config=g_config)
g_database = Database(logger=g_logger)
__config_sip_server = g_config.sipServer
# SIP 对象仅在此构造;绑定端口由显式 ServiceManager 负责。
g_gb28181SipServer = GB28181SipServer(
server_ip=__config_sip_server.get("sipServerIp"),
server_port=__config_sip_server.get("sipServerPort"),
server_id=__config_sip_server.get("sipServerId"),
realm=__config_sip_server.get("sipServerRealm"),
password=__config_sip_server.get("sipServerPass"),
sip_server_timeout=__config_sip_server.get("sipServerTimeout"),
sip_server_expiry=__config_sip_server.get("sipServerExpiry"),
sip_transfer_mode=__config_sip_server.get("sipTransferMode"),
rtp_transfer_mode=__config_sip_server.get("rtpTransferMode"),
rtp_transfer_audio_type=__config_sip_server.get("rtpTransferAudioType"),
auto_invite_after_rec_cate_log=__config_sip_server.get("autoInviteAfterRecCateLog"),
admin_host=g_config.adminHost,
zlm=g_zlm,
logger=g_logger
)
g_pull_stream_types = [
{"id": 1, "name": "RTSP"},
{"id": 2, "name": "RTMP"},
{"id": 3, "name": "FLV"},
{"id": 4, "name": "HLS"},
{"id": 21, "name": "GB28181"},
{"id": 31, "name": "cRTSP"},
{"id": 32, "name": "cRTMP"}
]
def get_audio_types(lang='zh'):
"""返回音频类型"""
T = LANG_UI_DICT.get(lang, {})
result = []
__audio_types = [
{"type": 0, "name": "静音", "name_key": "audio_pull_mute"},
{"type": 1, "name": "原始音频", "name_key": "audio_pull_original"}
]
for audio_type in __audio_types:
result.append({
"type": audio_type["type"],
"name": T.get(audio_type["name_key"], audio_type["name"])
})
return result
g_session_key_user = "user"
g_session_key_captcha = "captcha"
def _bool_cfg(v):
if v is None:
return False
if isinstance(v, bool):
return v
if isinstance(v, (int, float)):
return v != 0
return str(v).strip().lower() in ("1", "true", "yes", "on")
class GlobalUtils(object):
@staticmethod
def addStreamProxy(stream, lang=None):
"""开启流代理拉流到ZLM"""
__ret = False
__msg = LANG_VIEWS_USE_LANG_T(lang, "msg_unknown_error")
if stream.pull_stream_type in [1, 2, 3, 4]:
enable_rtmp = 1 if g_config.isEnableMediaProxyRtmp else 0
add_key, add_msg = g_zlm.addStreamProxy(app=stream.app,
name=stream.name,
origin_url=stream.pull_stream_url,
is_audio=stream.is_audio,
enable_rtmp=enable_rtmp)
if add_key:
__ret = True
__msg = LANG_VIEWS_USE_LANG_T(lang, "stream_forward_enabled_success")
else:
__msg = add_msg
elif stream.pull_stream_type == 21:
__ret, __msg = g_gb28181SipServer.request_invite(client_id=stream.camera_device_id, channel_id=stream.name)
if __ret:
__msg = LANG_VIEWS_USE_LANG_T(lang, "stream_forward_enabled_success")
elif stream.pull_stream_type in [31, 32]:
__msg = LANG_VIEWS_USE_LANG_T(lang, "media_push_stream_hint")
else:
__msg = LANG_VIEWS_USE_LANG_T(lang, "media_protocol_not_supported")
return __ret, __msg
@staticmethod
def addAllStreamProxy(lang=None):
"""开启所有流代理"""
ret = False
msg = LANG_VIEWS_USE_LANG_T(lang, "msg_unknown_error")
online_streams = g_zlm.getMediaList()
online_stream_dict = {}
if len(online_streams) == 0:
g_database.execute("update av_stream set forward_state=0")
else:
for d in online_streams:
an = "{app}_{name}".format(app=d["app"], name=d["name"])
online_stream_dict[an] = d
success_count = 0
error_count = 0
streams = StreamModel.objects.all()
for stream in streams:
stream_an = "{app}_{name}".format(app=stream.app, name=stream.name)
if online_stream_dict.get(stream_an):
success_count += 1
else:
__add_ret, __add_msg = GlobalUtils.addStreamProxy(stream, lang=lang)
if __add_ret:
stream.forward_state = 1
stream.save()
success_count += 1
else:
error_count += 1
ret = True
msg = LANG_VIEWS_USE_LANG_T(lang, "msg_batch_result") % (success_count, error_count)
return ret, msg
@staticmethod
def delStreamProxy(stream, lang=None):
"""关闭流代理"""
__ret = False
__msg = LANG_VIEWS_USE_LANG_T(lang, "msg_unknown_error")
if stream.pull_stream_type in [1, 2, 3, 4]:
del_flag, del_msg = g_zlm.delStreamProxy(app=stream.app, name=stream.name)
if del_flag:
__ret = True
__msg = LANG_VIEWS_USE_LANG_T(lang, "media_forward_stop_success")
else:
__msg = del_msg
elif stream.pull_stream_type == 21:
__ret, __msg = g_gb28181SipServer.request_bye(client_id=stream.camera_device_id, channel_id=stream.name)
if __ret:
__msg = LANG_VIEWS_USE_LANG_T(lang, "media_forward_stop_success")
elif stream.pull_stream_type == 31:
__ret, __msg = g_zlm.close_streams(schema="rtsp", app=stream.app, name=stream.name)
if __ret:
__msg = LANG_VIEWS_USE_LANG_T(lang, "media_forward_stop_success")
elif stream.pull_stream_type == 32:
__ret, __msg = g_zlm.close_streams(schema="rtmp", app=stream.app, name=stream.name)
if __ret:
__msg = LANG_VIEWS_USE_LANG_T(lang, "media_forward_stop_success")
else:
__msg = LANG_VIEWS_USE_LANG_T(lang, "media_protocol_not_supported")
return __ret, __msg
@staticmethod
def delAllStreamProxy(lang=None):
"""关闭所有流代理"""
online_streams = g_zlm.getMediaList()
for d in online_streams:
stream = StreamModel.objects.filter(app=d["app"], name=d["name"]).first()
if stream:
__ret, __msg = GlobalUtils.delStreamProxy(stream, lang=lang)
g_database.execute("update av_stream set forward_state=0")
@staticmethod
def apply_runtime_config(before_cfg):
"""保存 config.json 后热更新运行时组件(不重启 Django 进程)。"""
before_cfg = before_cfg or {}
after_cfg = g_config.to_dict(include_secrets=True)
notes = []
def _sip_snapshot(cfg):
sip = dict((cfg or {}).get("sipServer") or {})
return (
sip.get("sipServerIp"), sip.get("sipServerPort"), sip.get("sipServerId"),
sip.get("sipServerRealm"), sip.get("sipServerPass"), sip.get("sipServerTimeout"),
sip.get("sipServerExpiry"), sip.get("sipTransferMode"), sip.get("rtpTransferMode"),
sip.get("rtpTransferAudioType"), sip.get("autoInviteAfterRecCateLog"),
)
if _sip_snapshot(before_cfg) != _sip_snapshot(after_cfg):
try:
from app.services.lifecycle import is_service_leader
if is_service_leader():
GlobalUtils._reload_gb28181_sip()
g_logger.info("apply_runtime_config: GB28181 SIP reloaded")
else:
notes.append("gb28181")
except Exception as e:
g_logger.warning("apply_runtime_config: GB28181 reload failed: %s", e)
notes.append("gb28181")
before_rec = _bool_cfg(before_cfg.get("recordingEnabled"))
after_rec = bool(g_config.recordingEnabled)
if after_rec != before_rec:
try:
from app.services.lifecycle import get_service_manager
get_service_manager().reconcile()
except Exception as e:
g_logger.warning("apply_runtime_config: recording manager: %s", e)
if before_cfg.get("adminPort") != after_cfg.get("adminPort"):
notes.append("adminPort")
if _bool_cfg(before_cfg.get("logDebug")) != bool(g_config.logDebug):
notes.append("logDebug")
media_keys = (
"mediaHttpPort", "mediaRtspPort", "mediaRtmpPort",
"mediaStartPath", "mediaStartConfigPath", "mediaSecret",
)
if any(before_cfg.get(k) != after_cfg.get(k) for k in media_keys):
notes.append("zlm")
return notes
@staticmethod
def _reload_gb28181_sip():
global g_gb28181SipServer
try:
g_gb28181SipServer.stop()
except Exception:
pass
sip = dict(g_config.sipServer or {})
g_gb28181SipServer = GB28181SipServer(
server_ip=sip.get("sipServerIp"),
server_port=sip.get("sipServerPort"),
server_id=sip.get("sipServerId"),
realm=sip.get("sipServerRealm"),
password=sip.get("sipServerPass"),
sip_server_timeout=sip.get("sipServerTimeout"),
sip_server_expiry=sip.get("sipServerExpiry"),
sip_transfer_mode=sip.get("sipTransferMode"),
rtp_transfer_mode=sip.get("rtpTransferMode"),
rtp_transfer_audio_type=sip.get("rtpTransferAudioType"),
auto_invite_after_rec_cate_log=sip.get("autoInviteAfterRecCateLog"),
admin_host=g_config.adminHost,
zlm=g_zlm,
logger=g_logger,
)
g_gb28181SipServer.start()
class CheckServerUtils():
@staticmethod
def checkVersion(request_ip, peer_ip, peer_port, lang=None):
# Network and host information parameters are intentionally ignored.
from app.services.telemetry import check_update
return check_update(lang=lang)
@staticmethod
def reportHeart(report_count, lang=None):
from app.services.telemetry import send_heartbeat
return send_heartbeat(report_count)