新增hls下载进程,待完善
This commit is contained in:
227
main_start.py
227
main_start.py
@@ -11,7 +11,8 @@ import os
|
||||
import signal
|
||||
import argparse
|
||||
import time
|
||||
from typing import List, Dict
|
||||
import glob
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
from common.camera_config import CameraConfig
|
||||
from utils.logger import get_logger
|
||||
@@ -21,6 +22,9 @@ logger = get_logger(__name__)
|
||||
# PID 文件目录
|
||||
PID_DIR = "pids"
|
||||
|
||||
# HLS下载器PID文件前缀
|
||||
HLS_DOWNLOADER_PID_PREFIX = "hls_downloader_"
|
||||
|
||||
|
||||
def load_debug_mode(config_path: str = "config.yaml") -> bool:
|
||||
"""从配置文件读取调试模式"""
|
||||
@@ -43,6 +47,27 @@ def load_service_groups_from_yaml(config_path: str = "config.yaml") -> List[dict
|
||||
return cfg.get("service_groups", [])
|
||||
|
||||
|
||||
def load_hls_config(config_path: str = "config.yaml") -> dict:
|
||||
"""从配置文件加载HLS下载器配置"""
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
return {
|
||||
"hls_root_path": cfg.get("hls_root_path", ""),
|
||||
"daily_rotate_hour": cfg.get("hls_downloader_daily_rotate_hour", 3),
|
||||
"retention_days": cfg.get("hls_downloader_retention_days", 3),
|
||||
"retry_interval": cfg.get("hls_downloader_retry_interval_seconds", 10)
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] Failed to load HLS config: {e}")
|
||||
return {
|
||||
"hls_root_path": "",
|
||||
"daily_rotate_hour": 3,
|
||||
"retention_days": 3,
|
||||
"retry_interval": 10
|
||||
}
|
||||
|
||||
|
||||
def cameras_to_base64_json(cameras: List[dict]) -> str:
|
||||
"""将摄像头配置转换为 base64 编码的 JSON 字符串"""
|
||||
json_str = json.dumps(cameras, ensure_ascii=False)
|
||||
@@ -54,6 +79,11 @@ def get_pid_file_path(group_name: str) -> str:
|
||||
return os.path.join(PID_DIR, f"{group_name}.pid")
|
||||
|
||||
|
||||
def get_hls_downloader_pid_file_path(index_code: str) -> str:
|
||||
"""获取HLS下载器的 PID 文件路径"""
|
||||
return os.path.join(PID_DIR, f"{HLS_DOWNLOADER_PID_PREFIX}{index_code}.pid")
|
||||
|
||||
|
||||
def ensure_pid_dir():
|
||||
"""确保 PID 目录存在"""
|
||||
if not os.path.exists(PID_DIR):
|
||||
@@ -72,6 +102,18 @@ def save_pid(pid: int, group_name: str):
|
||||
logger.error(f"[ERROR] Failed to save PID file: {e}")
|
||||
|
||||
|
||||
def save_hls_downloader_pid(pid: int, index_code: str):
|
||||
"""保存HLS下载器进程ID到文件"""
|
||||
ensure_pid_dir()
|
||||
pid_file = get_hls_downloader_pid_file_path(index_code)
|
||||
try:
|
||||
with open(pid_file, "w") as f:
|
||||
f.write(str(pid))
|
||||
logger.info(f"[INFO] Saved HLS downloader PID {pid} to {pid_file}")
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] Failed to save HLS downloader PID file: {e}")
|
||||
|
||||
|
||||
def read_pid(group_name: str):
|
||||
"""从PID文件读取进程ID"""
|
||||
pid_file = get_pid_file_path(group_name)
|
||||
@@ -85,6 +127,19 @@ def read_pid(group_name: str):
|
||||
return None
|
||||
|
||||
|
||||
def read_hls_downloader_pid(index_code: str):
|
||||
"""从PID文件读取HLS下载器进程ID"""
|
||||
pid_file = get_hls_downloader_pid_file_path(index_code)
|
||||
try:
|
||||
with open(pid_file, "r") as f:
|
||||
return int(f.read().strip())
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] Failed to read HLS downloader PID file: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def is_process_running(pid: int):
|
||||
"""检查进程是否在运行"""
|
||||
try:
|
||||
@@ -102,7 +157,7 @@ def get_script_path(video_source_type: str) -> str:
|
||||
return "rtsp_service_ws_kadian.py"
|
||||
|
||||
|
||||
def start_service_group(group: dict):
|
||||
def start_service_group(group: dict, hls_config: dict = None):
|
||||
"""启动服务子进程(后台运行)"""
|
||||
cameras = group.get("cameras", [])
|
||||
ws_host = group.get("ws_host", "0.0.0.0")
|
||||
@@ -135,13 +190,53 @@ def start_service_group(group: dict):
|
||||
return process, group_name
|
||||
|
||||
|
||||
def start_hls_downloader(camera: dict, hls_config: dict) -> Optional[subprocess.Popen]:
|
||||
"""启动HLS下载器进程"""
|
||||
index_code = camera.get("index", "")
|
||||
camera_name = camera.get("name", "")
|
||||
camera_id = camera.get("id", 0)
|
||||
|
||||
if not index_code:
|
||||
logger.warning(f"[WARN] Camera has no index_code, skipping HLS downloader")
|
||||
return None
|
||||
|
||||
# 检查是否已经在运行
|
||||
pid = read_hls_downloader_pid(index_code)
|
||||
if pid and is_process_running(pid):
|
||||
logger.warning(f"[WARN] HLS downloader for '{index_code}' is already running with PID {pid}")
|
||||
return None
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"hls_downloader.py",
|
||||
"--index-code", index_code,
|
||||
"--camera-name", camera_name,
|
||||
"--camera-id", str(camera_id),
|
||||
"--hls-root-path", hls_config.get("hls_root_path", ""),
|
||||
"--rotate-hour", str(hls_config.get("daily_rotate_hour", 3)),
|
||||
"--retention-days", str(hls_config.get("retention_days", 3)),
|
||||
"--retry-interval", str(hls_config.get("retry_interval", 10))
|
||||
]
|
||||
|
||||
logger.info(f"[INFO] Starting HLS downloader for camera '{camera_name}' (index: {index_code})")
|
||||
|
||||
if DEBUG_MODE:
|
||||
process = subprocess.Popen(cmd)
|
||||
else:
|
||||
process = subprocess.Popen(cmd, start_new_session=True)
|
||||
|
||||
return process
|
||||
|
||||
|
||||
def start_service():
|
||||
"""启动所有服务组"""
|
||||
config_path = "config.yaml"
|
||||
|
||||
# 1. 读取配置
|
||||
service_groups = load_service_groups_from_yaml(config_path)
|
||||
hls_config = load_hls_config(config_path)
|
||||
logger.info(f"[INFO] Loaded {len(service_groups)} service groups from {config_path}")
|
||||
logger.info(f"[INFO] HLS config: root_path={hls_config.get('hls_root_path')}, rotate_hour={hls_config.get('daily_rotate_hour')}")
|
||||
|
||||
if not service_groups:
|
||||
logger.error("[ERROR] No service groups found in config, exiting...")
|
||||
@@ -149,27 +244,49 @@ def start_service():
|
||||
|
||||
# 2. 启动每个服务组
|
||||
started_count = 0
|
||||
downloader_count = 0
|
||||
processes = [] # 记录所有子进程,用于调试模式下等待
|
||||
downloader_processes = [] # 记录下载器进程
|
||||
|
||||
for group in service_groups:
|
||||
group_name = group.get("name", "default")
|
||||
video_source_type = group.get("video_source_type", "rtsp")
|
||||
cameras = group.get("cameras", [])
|
||||
|
||||
# 检查是否已经在运行
|
||||
pid = read_pid(group_name)
|
||||
if pid and is_process_running(pid):
|
||||
logger.warning(f"[WARN] Service group '{group_name}' is already running with PID {pid}")
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
process, name = start_service_group(group, hls_config)
|
||||
time.sleep(0.5)
|
||||
save_pid(process.pid, name)
|
||||
logger.info(f"[INFO] Service group '{name}' started with PID {process.pid}")
|
||||
processes.append((process, name))
|
||||
started_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] Failed to start service group '{group_name}': {e}")
|
||||
|
||||
try:
|
||||
process, name = start_service_group(group)
|
||||
time.sleep(0.5)
|
||||
save_pid(process.pid, name)
|
||||
logger.info(f"[INFO] Service group '{name}' started with PID {process.pid}")
|
||||
processes.append((process, name))
|
||||
started_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] Failed to start service group '{group_name}': {e}")
|
||||
# 如果是HLS类型,为每个摄像头启动下载进程
|
||||
if video_source_type == "hls" and hls_config.get("hls_root_path"):
|
||||
for camera in cameras:
|
||||
index_code = camera.get("index", "")
|
||||
if not index_code:
|
||||
continue
|
||||
try:
|
||||
downloader_proc = start_hls_downloader(camera, hls_config)
|
||||
if downloader_proc:
|
||||
time.sleep(0.3)
|
||||
save_hls_downloader_pid(downloader_proc.pid, index_code)
|
||||
logger.info(f"[INFO] HLS downloader for '{index_code}' started with PID {downloader_proc.pid}")
|
||||
downloader_processes.append((downloader_proc, index_code))
|
||||
downloader_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] Failed to start HLS downloader for '{index_code}': {e}")
|
||||
|
||||
logger.info(f"[INFO] Started {started_count}/{len(service_groups)} service groups")
|
||||
logger.info(f"[INFO] Started {downloader_count} HLS downloaders")
|
||||
|
||||
# DEBUG_MODE=True 时,主进程等待所有子进程
|
||||
if DEBUG_MODE and processes:
|
||||
@@ -180,6 +297,18 @@ def start_service():
|
||||
return started_count > 0
|
||||
|
||||
|
||||
def get_all_hls_downloader_pids() -> List[tuple]:
|
||||
"""获取所有HLS下载器的PID文件和index_code"""
|
||||
result = []
|
||||
pid_pattern = os.path.join(PID_DIR, f"{HLS_DOWNLOADER_PID_PREFIX}*.pid")
|
||||
for pid_file in glob.glob(pid_pattern):
|
||||
# 从文件名提取index_code: hls_downloader_{index_code}.pid
|
||||
filename = os.path.basename(pid_file)
|
||||
index_code = filename[len(HLS_DOWNLOADER_PID_PREFIX):-4] # 去掉前缀和.pid后缀
|
||||
result.append((index_code, pid_file))
|
||||
return result
|
||||
|
||||
|
||||
def status_service():
|
||||
"""检查所有服务组状态"""
|
||||
config_path = "config.yaml"
|
||||
@@ -207,11 +336,31 @@ def status_service():
|
||||
pass
|
||||
|
||||
logger.info(f"[INFO] {running_count}/{len(service_groups)} service groups running")
|
||||
|
||||
# 检查HLS下载器状态
|
||||
downloader_pids = get_all_hls_downloader_pids()
|
||||
downloader_running = 0
|
||||
for index_code, pid_file in downloader_pids:
|
||||
pid = read_hls_downloader_pid(index_code)
|
||||
if pid and is_process_running(pid):
|
||||
logger.info(f"[INFO] HLS downloader '{index_code}' is running with PID {pid}")
|
||||
downloader_running += 1
|
||||
else:
|
||||
logger.info(f"[INFO] HLS downloader '{index_code}' is not running")
|
||||
if pid:
|
||||
try:
|
||||
os.remove(pid_file)
|
||||
except:
|
||||
pass
|
||||
|
||||
if downloader_pids:
|
||||
logger.info(f"[INFO] {downloader_running}/{len(downloader_pids)} HLS downloaders running")
|
||||
|
||||
return running_count > 0
|
||||
|
||||
|
||||
def stop_service(force=False):
|
||||
"""停止所有服务组"""
|
||||
"""停止所有服务组和HLS下载器"""
|
||||
config_path = "config.yaml"
|
||||
service_groups = load_service_groups_from_yaml(config_path)
|
||||
|
||||
@@ -268,6 +417,58 @@ def stop_service(force=False):
|
||||
logger.error(f"[ERROR] Failed to stop service group '{group_name}': {e}")
|
||||
|
||||
logger.info(f"[INFO] Stopped {stopped_count}/{len(service_groups)} service groups")
|
||||
|
||||
# 停止所有HLS下载器
|
||||
downloader_pids = get_all_hls_downloader_pids()
|
||||
downloader_stopped = 0
|
||||
for index_code, pid_file in downloader_pids:
|
||||
pid = read_hls_downloader_pid(index_code)
|
||||
|
||||
if not pid:
|
||||
continue
|
||||
|
||||
if not is_process_running(pid):
|
||||
logger.warning(f"[WARN] HLS downloader '{index_code}' PID {pid} not running, cleaning up")
|
||||
try:
|
||||
os.remove(pid_file)
|
||||
except:
|
||||
pass
|
||||
downloader_stopped += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
if force:
|
||||
logger.info(f"[INFO] Force killing HLS downloader '{index_code}' (PID {pid})")
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
else:
|
||||
logger.info(f"[INFO] Stopping HLS downloader '{index_code}' (PID {pid})")
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
|
||||
# 等待进程结束
|
||||
for i in range(10):
|
||||
if not is_process_running(pid):
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
if is_process_running(pid):
|
||||
logger.warning(f"[WARN] Force killing HLS downloader '{index_code}'")
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
time.sleep(1)
|
||||
|
||||
# 清理PID文件
|
||||
try:
|
||||
os.remove(pid_file)
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.info(f"[INFO] HLS downloader '{index_code}' stopped")
|
||||
downloader_stopped += 1
|
||||
except Exception as e:
|
||||
logger.error(f"[ERROR] Failed to stop HLS downloader '{index_code}': {e}")
|
||||
|
||||
if downloader_pids:
|
||||
logger.info(f"[INFO] Stopped {downloader_stopped}/{len(downloader_pids)} HLS downloaders")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user