上传文件至 main

This commit is contained in:
leixiwen 2025-04-16 16:19:53 +08:00
commit e864f3aab2
5 changed files with 912 additions and 0 deletions

112
main/CameraConfigure.py Normal file
View File

@ -0,0 +1,112 @@
import requests
import json
def load_config(config_path="stattioncfg.json"):
"""
从JSON配置文件加载配置信息
:param config_path: 配置文件路径默认当前目录config.json
:return: 配置字典成功时None失败时
"""
try:
with open(config_path, "r", encoding="utf-8") as f:
config = json.load(f)
# 验证必要字段
if "station_name" not in config:
print("配置错误:缺少 station_name 字段")
return None
return config
except FileNotFoundError:
print(f"配置文件 {config_path} 未找到")
return None
except json.JSONDecodeError:
print("配置文件解析失败请检查JSON格式")
return None
def get_camera_rtsp_config(station_name):
"""
获取指定变电站的摄像头RTSP配置信息
:param station_name: 变电站名称字符串
:return: 包含摄像头配置的列表成功时None失败时
"""
# 接口地址
url = "http://192.168.110.229:38091/api/services/isas/VideoElectronicFence/GetVideoCameraRTSPConfigure"
# 请求参数
params = {
"stationName": station_name
}
try:
# 发送GET请求
response = requests.get(url, params=params, timeout=10)
if response.status_code != 200:
print(f"请求失败HTTP状态码{response.status_code}")
return None
json_data = response.json()
if not json_data.get("success", False):
print(f"接口返回错误:{json_data.get('result', {}).get('message', '未知错误')}")
return None
result = json_data.get("result", {})
if not (result.get("flag", False) and "resultData" in result):
print("未找到有效的摄像头配置数据")
return None
# 提取并处理摄像头配置
cameras = result["resultData"]
for cam in cameras:
# 转换ROI格式
roi = cam.get('roi', '0,0,0,0')
if isinstance(roi, str):
cam['roi'] = tuple(map(int, roi.split(',')))
elif isinstance(roi, list):
cam['roi'] = tuple(map(int, roi))
else:
cam['roi'] = (0, 0, 0, 0)
# 补全RTSP参数
# rtsp_url = cam['url']
# if 'transportmode' not in rtsp_url:
# connector = '&' if '?' in rtsp_url else '?'
# cam['url'] = f"{rtsp_url}{connector}"
print(f"成功获取 {result['totalCount']} 个摄像头配置")
return cameras
except requests.exceptions.RequestException as e:
print(f"请求发生异常:{str(e)}")
return None
except ValueError:
print("响应解析失败无效的JSON格式")
return None
# 更新后的使用示例
if __name__ == "__main__":
# 从配置文件加载设置
config = load_config()
if not config:
exit(1)
station = config["station_name"]
print(f"正在查询变电站:{station}")
cameras = get_camera_rtsp_config(station)
if cameras:
print("\n摄像头列表:")
for idx, camera in enumerate(cameras, 1):
print(f"\n摄像头 {idx}:")
print(f"名称:{camera['name']}")
print(f"ID{camera['id']}")
print(f"RTSP地址{camera['url']}")
print(f"ROI区域{camera['roi']}")

159
main/UploadAlarmMsg.py Normal file
View File

@ -0,0 +1,159 @@
import requests
from typing import Optional, Dict, Any
import logging
import json
from datetime import datetime
import time
# 配置日志系统
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('alarm_upload.log', encoding='utf-8'),
logging.StreamHandler()
]
)
# 审计日志独立配置(记录原始请求响应数据)
audit_logger = logging.getLogger('audit')
audit_logger.setLevel(logging.INFO)
audit_handler = logging.FileHandler('alarm_audit.log', encoding='utf-8')
audit_handler.setFormatter(logging.Formatter('%(message)s'))
audit_logger.addHandler(audit_handler)
def upload_alarm(
camera_id: str,
detection_status: Dict[str, Any],
content: str,
timeout: int = 10
) -> Optional[Dict]:
"""
增强版安全报警信息上传函数
Parameters:
camera_id: 摄像头唯一标识ID
detection_status: 检测状态字典需包含
{
"has_head": bool,
"has_helmet": bool,
"has_safevest": bool,
"timestamp": str # 格式YYYY-MM-DD HH:MM:SS
}
content: 报警内容描述
timeout: 请求超时时间
Returns:
服务器响应字典成功时None失败时
"""
# 参数验证
required_keys = ['has_head', 'has_helmet', 'has_safevest', 'timestamp']
if not all(key in detection_status for key in required_keys):
logging.error(f"参数错误:缺失必要检测状态字段,需要包含:{required_keys}")
return None
if not camera_id or not content:
logging.error("参数错误camera_id 和 content 不能为空")
return None
# 构造请求参数
base_url = "http://192.168.110.229:38090/api/services/isas/VideoElectronicFence/UploadAlarmMsg"
params = {
"id": camera_id,
"content": f"[{detection_status['timestamp']}] {content}",
"details": json.dumps({
"camera_id": camera_id,
"detection_status": detection_status,
# "system_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}, ensure_ascii=False)
}
# 审计日志数据
audit_data = {
"camera_id": camera_id,
# "request_time": time.strftime("%Y-%m-%d %H:%M:%S"),
"request_params": params,
"response_status": None,
"response_content": None,
"exception": None
}
try:
# 发送GET请求
response = requests.get(
base_url,
params=params,
timeout=timeout
)
audit_data["response_status"] = response.status_code
# HTTP状态码检查
if response.status_code != 200:
logging.error(f"HTTP错误 | 摄像头: {camera_id} | 状态码: {response.status_code}")
audit_data["exception"] = f"HTTP错误{response.status_code}"
return None
# 解析JSON响应
try:
json_data = response.json()
audit_data["response_content"] = json_data
except ValueError:
logging.error(f"响应解析失败 | 摄像头: {camera_id}")
audit_data["exception"] = "无效的JSON响应"
return None
# 检查业务逻辑成功状态
if json_data.get("success", False):
logging.info(f"报警上传成功 | 摄像头: {camera_id} | 内容: {content}")
return json_data
else:
error_msg = json_data.get("result", {}).get("message", "未知错误")
logging.error(f"业务逻辑错误 | 摄像头: {camera_id} | 错误: {error_msg}")
audit_data["exception"] = error_msg
return None
except requests.exceptions.Timeout:
error_msg = f"请求超时 | 摄像头: {camera_id} | 超时时间: {timeout}s"
logging.error(error_msg)
audit_data["exception"] = error_msg
except requests.exceptions.RequestException as e:
error_msg = f"网络请求异常 | 摄像头: {camera_id} | 错误: {str(e)}"
logging.error(error_msg)
audit_data["exception"] = str(e)
except Exception as e:
error_msg = f"未知错误 | 摄像头: {camera_id} | 错误: {str(e)}"
logging.error(error_msg)
audit_data["exception"] = str(e)
finally:
# 记录审计日志
audit_logger.info(json.dumps(audit_data, ensure_ascii=False))
return None
# 测试用例
if __name__ == "__main__":
# 模拟检测状态
test_status = {
"has_head": True,
"has_helmet": False,
"has_safevest": True,
# "timestamp": "2023-08-20 15:30:00"
}
# 测试正常上传
result = upload_alarm(
camera_id="1236",
detection_status=test_status,
content="安全警报:未佩戴安全帽"
)
print("上传结果:", result)
# 测试错误参数
result = upload_alarm(
camera_id="",
detection_status=test_status,
content=""
)
print("错误测试结果:", result)

45
main/roi_config.json Normal file
View File

@ -0,0 +1,45 @@
{
"cameraCfgs": [
{
"box": [
87,
23,
1826,
1038
],
"channel": 3,
"types": [
"person",
"animal",
"smoke",
"helmet",
"safevest",
"cellphone",
"fire"
]
},
{
"box": [
34,
27,
1866,
1040
],
"channel": 9,
"types": [
"animal",
"smoke",
"helmet",
"safevest",
"cellphone",
"fire"
]
}
],
"nvr": {
"ip": "192.168.81.33",
"password": "yunda123",
"port": 8000,
"username": "admin"
}
}

4
main/stattioncfg.json Normal file
View File

@ -0,0 +1,4 @@
{
"station_name": "变电所",
"comment": "其他配置项可以继续添加..."
}

592
main/test_main.py Normal file
View File

@ -0,0 +1,592 @@
# coding=utf-8
import os
import platform
import tkinter
from tkinter import *
from HCNetSDK import *
from PlayCtrl import *
from datetime import datetime
import json
from yolo_detector import YOLODetector
import cv2
import numpy as np
from PIL import Image, ImageTk
import redis
from PIL import ImageFont, ImageDraw, Image
import base64
from threading import Thread
from queue import Queue
import queue
import time
import threading
from yolo_processor import process_frame_with_yolo
class DISPLAY_INFO(Structure):
_fields_ = [
("pBuf", POINTER(c_ubyte)),
("nSize", c_ulong),
("nWidth", c_long),
("nHeight", c_long),
("nStamp", c_ulong),
("nType", c_ulong),
("nReserved", c_ulong),
]
# 读取配置文件
def load_nvr_config(config_path='roi_config.json'):
with open(config_path, 'r') as f:
config = json.load(f)
return config['nvr'], config['cameraCfgs']
# 获取NVR配置和摄像头配置
nvr_config, camera_configs = load_nvr_config()
# 登录的设备信息
DEV_IP = create_string_buffer(nvr_config['ip'].encode())
DEV_PORT = nvr_config['port']
DEV_USER_NAME = create_string_buffer(nvr_config['username'].encode())
DEV_PASSWORD = create_string_buffer(nvr_config['password'].encode())
YOLO_FPS = 2 # 每秒处理的帧数
WINDOWS_FLAG = True
win = None # 预览窗口
funcRealDataCallBack_V30 = None # 实时预览回调函数,需要定义为全局的
PlayCtrl_Port = c_long(-1) # 播放句柄
Playctrldll = None # 播放库
FuncDecCB = None # 播放库解码回调函数,需要定义为全局的
max_channels = 9 # 最大支持9个通道
windows = [None] * max_channels # 预览窗口数组
canvases = [None] * max_channels # 画布数组
PlayCtrl_Ports = [c_long(-1)] * max_channels # 播放句柄数组
funcRealDataCallBack_V30_array = [None] * max_channels # 预览回调函数数组
funcDecCB_array = [None] * max_channels # 预览回调函数数组
frame_queues = [Queue(maxsize=2) for _ in range(max_channels)]
# 线程控制参数
WORKER_THREADS = 4 # 根据CPU核心数调整建议物理核心数×2
EXIT_SIGNAL = "EXIT"
consumers = [] # 消费者线程池
# 初始化队列
frame_queue = queue.Queue(maxsize=200) # 原始数据队列
result_queue = queue.Queue() # GUI更新队列
processing_threads = [None] * max_channels
enable_yolo = True
last_process_times = [datetime.now()] * max_channels # 每个通道上次处理的时间
redis_client = redis.Redis(host='192.168.110.229', port=36379, db=0, password="yunda123")
# 获取当前系统环境
def GetPlatform():
sysstr = platform.system()
print('' + sysstr)
if sysstr != "Windows":
global WINDOWS_FLAG
WINDOWS_FLAG = False
# 设置SDK初始化依赖库路径
def SetSDKInitCfg():
# 设置HCNetSDKCom组件库和SSL库加载路径
# print(os.getcwd())
if WINDOWS_FLAG:
strPath = os.getcwd().encode('gbk')
sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()
sdk_ComPath.sPath = strPath
Objdll.NET_DVR_SetSDKInitCfg(2, byref(sdk_ComPath))
Objdll.NET_DVR_SetSDKInitCfg(3, create_string_buffer(strPath + b'\\libcrypto-1_1-x64.dll'))
Objdll.NET_DVR_SetSDKInitCfg(4, create_string_buffer(strPath + b'\\libssl-1_1-x64.dll'))
else:
strPath = os.getcwd().encode('utf-8')
sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()
sdk_ComPath.sPath = strPath
Objdll.NET_DVR_SetSDKInitCfg(2, byref(sdk_ComPath))
Objdll.NET_DVR_SetSDKInitCfg(3, create_string_buffer(strPath + b'/libcrypto.so.1.1'))
Objdll.NET_DVR_SetSDKInitCfg(4, create_string_buffer(strPath + b'/libssl.so.1.1'))
def LoginDev(Objdll):
# 登录注册设备
device_info = NET_DVR_DEVICEINFO_V30()
lUserId = Objdll.NET_DVR_Login_V30(DEV_IP, DEV_PORT, DEV_USER_NAME, DEV_PASSWORD, byref(device_info))
return (lUserId, device_info)
def start_processing_thread(channel_index):
def run():
while True:
frame_data = frame_queues[channel_index].get()
print("out")
if frame_data is None: # 终止信号
break
# 在此处执行YOLO处理和Redis发布
# process_frame_with_yolo(frame_data, channel_index)
process_frame_with_yolo(frame_data, channel_index, camera_configs, yolo_detector, redis_client)
thread = Thread(target=run, daemon=True)
thread.start()
return thread
def on_closing():
for i in range(num_channels):
frame_queues[i].put(None) # 发送终止信号
processing_threads[i].join()
root.destroy()
def DecCBFun(nPort, pBuf, nSize, pFrameInfo, nUser, nReserved2):
# 解码回调函数
channel_index = nUser - 1
# print(f"回调函数被调用 - 通道: {channel_index + 1}, 数据类型: {dwDataType}, 数据大小: {dwBufSize}")
if channel_index < 0 or channel_index >= max_channels:
print(f"无效的通道索引: {channel_index}")
return
port = PlayCtrl_Ports[channel_index]
canvas = canvases[channel_index]
if pFrameInfo.contents.nType == 3:
# 获取当前时间
now = datetime.now()
# 格式化为 HH:mm:ss:fff 格式
formatted_time = now.strftime("%H:%M:%S:%f")[:-3]
sFileName = ('../../pic/test_stamp[%s].jpg' % pFrameInfo.contents.nStamp)
nWidth = pFrameInfo.contents.nWidth
nHeight = pFrameInfo.contents.nHeight
nType = pFrameInfo.contents.nType
dwFrameNum = pFrameInfo.contents.dwFrameNum
nStamp = pFrameInfo.contents.nStamp
#print(nWidth, nHeight, nType, dwFrameNum, nStamp, sFileName)
if yolo_detector is not None:
try:
if nWidth > 0 and nHeight > 0:
try:
# 提取YUV数据并转换为RGB
yuv_data = np.frombuffer(pBuf[:nSize], dtype=np.uint8)
height = pFrameInfo.contents.nHeight
width = pFrameInfo.contents.nWidth
# YUV420转RGB
yuv_frame = yuv_data.reshape((height * 3 // 2, width))
# rgb_frame = cv2.cvtColor(yuv_frame, cv2.COLOR_YUV2RGB_I420)
try:
# 仅传递原始数据到处理队列
if not frame_queue.full():
frame_queue.put((channel_index, yuv_frame), block=False)
except Exception as e:
print(f"回调异常: {str(e)}")
# cv2.imshow("frame", frame)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
except Exception as e:
print(f"YUV转RGB失败: {str(e)}")
except Exception as e:
print(f"YOLO处理异常: {str(e)}")
import traceback
traceback.print_exc()
def save_detections(detections, frame, output_dir="."):
os.makedirs(output_dir, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_image = os.path.join(output_dir, f"detection_{timestamp}.jpg")
output_json = os.path.join(output_dir, f"detection_{timestamp}.json")
# 保存可视化图片
cv2.imwrite(output_image, frame)
# 保存结构化数据
with open(output_json, 'w') as f:
json.dump(detections, f, indent=2)
# 在全局变量部分修改
# 删除原有的win1, win2, cv1, cv2等变量
# 替换为通道数组
# 修改RealDataCallBack_V30函数支持多通道
def RealDataCallBack_V30(lPlayHandle, dwDataType, pBuffer, dwBufSize, pUser):
try:
channel_index = pUser - 1
# print(f"回调函数被调用 - 通道: {channel_index + 1}, 数据类型: {dwDataType}, 数据大小: {dwBufSize}")
if channel_index < 0 or channel_index >= max_channels:
print(f"无效的通道索引: {channel_index}")
return
port = PlayCtrl_Ports[channel_index]
canvas = canvases[channel_index]
if dwDataType == NET_DVR_SYSHEAD:
print(f"通道 {channel_index + 1} 收到系统头数据")
Playctrldll.PlayM4_SetStreamOpenMode(port, 0)
if Playctrldll.PlayM4_OpenStream(port, pBuffer, dwBufSize, 1024 * 1024):
canvas_id = canvas.winfo_id()
print(f"通道 {pUser} 尝试使用画布ID: {canvas_id}")
# 确保画布仍然有效
if not canvas.winfo_exists():
print(f"通道 {channel_index + 1} 的画布已经不存在")
return
# 设置解码回调可以返回解码后YUV视频数据
funcDecCB_array[channel_index] = DECCBFUNWIN(DecCBFun)
Playctrldll.PlayM4_SetDecCallBackExMend(port, funcDecCB_array[channel_index], None, 0, pUser)
Playctrldll.PlayM4_Stop(port)
if Playctrldll.PlayM4_Play(port, canvas_id):
print(f'通道 {pUser} 播放库播放成功使用画布ID: {canvas_id}')
Playctrldll.PlayM4_SetDisplayBuf(port, 15)
Playctrldll.PlayM4_SetOverlayMode(port, 0, 0)
# 禁用自动显示
Playctrldll.PlayM4_SetDisplayCallBack(port, None)
else:
err = Playctrldll.PlayM4_GetLastError(port)
print(f'通道 {pUser} 播放库播放失败,错误码: {err}')
else:
err = Playctrldll.PlayM4_GetLastError(port)
print(f'通道 {pUser} 播放库打开流失败,错误码: {err}')
elif dwDataType == NET_DVR_STREAMDATA:
try:
# 检查端口是否有效
if port.value < 0:
print(f"通道 {channel_index + 1} 的播放端口无效")
return
# 直接输入数据,但不显示
if not Playctrldll.PlayM4_InputData(port, pBuffer, dwBufSize):
err = Playctrldll.PlayM4_GetLastError(port)
# print(f"通道 {channel_index + 1} 输入数据失败,错误码: {err}")
# return
except Exception as e:
print(f"通道 {channel_index + 1} 数据处理异常: {str(e)}")
import traceback
traceback.print_exc()
except Exception as e:
print(f"回调函数发生未知异常: {str(e)}")
import traceback
traceback.print_exc()
# 添加一个计算布局的函数
def calculate_layout(num_channels):
"""根据通道数计算最佳布局"""
if num_channels <= 1:
return 1, 1
elif num_channels <= 4:
return 2, 2
elif num_channels <= 6:
return 2, 3
else: # 7-9通道
return 3, 3
def OpenPreview(Objdll, lUserId, callbackFun, channel_index):
'''
打开预览
'''
preview_info = NET_DVR_PREVIEWINFO()
preview_info.hPlayWnd = 0
preview_info.lChannel = camera_configs[channel_index]['channel'] # 根据索引获取对应通道号
preview_info.dwStreamType = 0
preview_info.dwLinkMode = 0
preview_info.bBlocked = 1
lRealPlayHandle = Objdll.NET_DVR_RealPlay_V40(lUserId, byref(preview_info), callbackFun, channel_index + 1)
return lRealPlayHandle
def InputData(fileMp4, Playctrldll):
while True:
pFileData = fileMp4.read(4096)
if pFileData is None:
break
if not Playctrldll.PlayM4_InputData(PlayCtrl_Port, pFileData, len(pFileData)):
break
# ------------------ 线程管理函数 ------------------
def start_consumers():
"""启动消费者线程池"""
global consumers
# 清理残留线程
consumers = [t for t in consumers if t.is_alive()]
# 创建新线程
for _ in range(WORKER_THREADS):
t = threading.Thread(
target=consumer_worker,
daemon=True # 主退出时自动结束
)
t.start()
consumers.append(t)
print(f"已启动 {len(consumers)} 个消费者线程")
def stop_consumers():
"""安全停止所有消费者线程"""
for _ in range(WORKER_THREADS):
frame_queue.put((EXIT_SIGNAL, None)) # 发送退出信号
# 等待线程结束
for t in consumers:
t.join(timeout=5)
if t.is_alive():
print(f"警告: 线程 {t.ident} 未正常退出")
print("所有消费者线程已停止")
# 修改消费者工作线程
def consumer_worker():
while True:
try:
channel, yuv_array = frame_queue.get(timeout=5)
if channel is EXIT_SIGNAL:
break
# YUV转RGB实际分辨率从配置读取
config = camera_configs[channel]
rgb_frame = cv2.cvtColor(yuv_array, cv2.COLOR_YUV2RGB_I420)
# # 执行推理
# process_frame_with_yolo(rgb_frame, channel)
process_frame_with_yolo(rgb_frame, channel, camera_configs, yolo_detector, redis_client)
# 准备GUI更新数据
resized_frame = cv2.resize(rgb_frame, (320, 240))
img = cv2.cvtColor(resized_frame, cv2.COLOR_RGB2BGR)
except queue.Empty:
continue
# 增强型GUI更新函数
def update_gui():
while not result_queue.empty():
try:
channel, img_tk, status = result_queue.get_nowait()
# 安全更新GUI组件
canvases[channel].configure(image=img_tk)
canvases[channel].image = img_tk # 保持引用
canvases[channel].itemconfigure("status", text=status)
except Exception as e:
print(f"GUI更新异常: {str(e)}")
root.after(50, update_gui)
# 在界面初始化时预加载画布ID
def create_canvas_grid():
global canvas_ids
canvas_ids = {}
for i in range(num_channels):
canvases[i].update()
canvas_ids[i] = canvases[i].winfo_id() # 预获取窗口ID
print(f"通道 {i} 画布ID: {canvas_ids[i]}")
# 在设备初始化时设置播放参数
def set_play_params(port, channel):
# 使用预存储的画布ID
Playctrldll.PlayM4_SetDisplayWindow(
port,
canvas_ids[channel], # 使用预获取的ID
0, 0,
single_width, single_height
)
# 修改SDK初始化部分
def Initcustom():
# 预加载所有画布ID
create_canvas_grid()
# 启动消费者线程
start_consumers()
# 启动GUI更新循环
root.after(0, update_gui)
# 启动主循环
root.mainloop()
# 程序退出时清理
stop_consumers()
if __name__ == '__main__':
try:
print("程序启动...")
# YOLO初始化
print("正在初始化YOLO检测器...")
try:
yolo_detector = YOLODetector() if enable_yolo else None
print("YOLO检测器初始化成功")
except Exception as e:
print(f"YOLO检测器初始化失败: {str(e)}")
raise
# 创建窗口
print("正在创建主窗口...")
root = tkinter.Tk()
root.title("多通道预览")
# 加载配置
print("正在加载配置文件...")
num_channels = min(len(camera_configs), max_channels)
print(f"检测到 {len(camera_configs)} 个摄像头配置,将显示 {num_channels} 个通道")
# 计算布局
rows, cols = calculate_layout(num_channels)
# 设置单个视频窗口的大小
single_width, single_height = 320, 240
# 设置主窗口大小
window_width = cols * single_width
window_height = rows * single_height
root.geometry(f"{window_width}x{window_height}+100+100")
# 创建帧来容纳所有画布
frame = tkinter.Frame(root, width=window_width, height=window_height)
frame.pack()
# 创建画布网格
for i in range(num_channels):
row = i // cols
col = i % cols
# 创建每个通道的画布,并确保它们有唯一的标识
# 使用Frame作为容器可能有助于解决显示问题
frame_container = tkinter.Frame(frame, width=single_width, height=single_height)
frame_container.grid(row=row, column=col, padx=2, pady=2)
frame_container.grid_propagate(False) # 防止frame自动调整大小
canvases[i] = tkinter.Canvas(frame_container, bg='black',
width=single_width,
height=single_height,
name=f"canvas_{i}")
canvases[i].pack(fill=tkinter.BOTH, expand=True)
# 添加通道标签
canvases[i].create_text(10, 10, text=f"通道 {i + 1}", fill="white", anchor="nw")
# 确保画布已经更新并有有效的窗口ID
canvases[i].update()
# 获取系统平台
print("\n系统环境检测:")
GetPlatform()
print(f"当前系统平台标志: {'Windows' if WINDOWS_FLAG else 'Linux'}")
# 加载库
print("\n正在加载SDK库...")
try:
if WINDOWS_FLAG:
print("当前工作目录:", os.getcwd())
print("尝试切换到SDK目录: ./lib/win")
os.chdir(r'./lib/win')
print("加载 HCNetSDK.dll...")
Objdll = ctypes.CDLL(r'./HCNetSDK.dll')
print("加载 PlayCtrl.dll...")
Playctrldll = ctypes.CDLL(r'./PlayCtrl.dll')
else:
# Linux加载代码保持不变...
pass
print("SDK库加载成功")
except Exception as e:
print(f"SDK库加载失败: {str(e)}")
raise
print("\n正在初始化SDK...")
SetSDKInitCfg()
# 初始化DLL
if Objdll.NET_DVR_Init() == 0:
print(f"SDK初始化失败错误码: {Objdll.NET_DVR_GetLastError()}")
raise Exception("SDK初始化失败")
print("SDK初始化成功")
# 登录设备
print(f"\n正在登录设备 {DEV_IP.value.decode()}:{DEV_PORT}...")
(lUserId, device_info) = LoginDev(Objdll)
if lUserId < 0:
err = Objdll.NET_DVR_GetLastError()
print(f'设备登录失败,错误码: {err}')
Objdll.NET_DVR_Cleanup()
raise Exception("设备登录失败")
print(f"设备登录成功用户ID: {lUserId}")
# 预览通道初始化
print("\n开始初始化预览通道...")
lRealPlayHandles = []
for i in range(num_channels):
PlayCtrl_Ports[i] = c_long(-1)
if not Playctrldll.PlayM4_GetPort(byref(PlayCtrl_Ports[i])):
error_code = Playctrldll.PlayM4_GetLastError(PlayCtrl_Ports[i])
print(f'通道 {i + 1} 获取播放库句柄失败,错误码: {error_code}')
PlayCtrl_Ports[i] = c_long(-1) # 明确标记为无效
continue
print(f'通道 {i + 1} 获取播放库端口成功: {PlayCtrl_Ports[i].value}')
# 创建回调函数
print(f'正在为通道 {i + 1} 创建回调函数...')
funcRealDataCallBack_V30_array[i] = REALDATACALLBACK(RealDataCallBack_V30)
# 开启预览
print(f'正在开启通道 {i + 1} 的预览...')
lRealPlayHandle = OpenPreview(Objdll, lUserId, funcRealDataCallBack_V30_array[i], i)
lRealPlayHandles.append(lRealPlayHandle)
if lRealPlayHandle < 0:
print(f'通道 {i + 1} 预览失败,错误码: {Objdll.NET_DVR_GetLastError()}')
else:
print(f'通道 {i + 1} 预览成功,句柄: {lRealPlayHandle}')
Initcustom()
root.mainloop()
except Exception as e:
print(f"\n程序发生异常: {str(e)}")
print("错误详细信息:")
import traceback
traceback.print_exc()
finally:
print("\n开始清理资源...")
# 关闭预览
if 'lRealPlayHandles' in locals():
for handle in lRealPlayHandles:
if handle >= 0:
print(f"正在关闭预览句柄: {handle}")
Objdll.NET_DVR_StopRealPlay(handle)
# 释放播放库资源
if 'PlayCtrl_Ports' in locals():
for i in range(num_channels):
if PlayCtrl_Ports[i].value > -1:
print(f"正在释放通道 {i + 1} 的播放库资源")
Playctrldll.PlayM4_Stop(PlayCtrl_Ports[i])
Playctrldll.PlayM4_CloseStream(PlayCtrl_Ports[i])
Playctrldll.PlayM4_FreePort(PlayCtrl_Ports[i])
# 登出设备
if 'lUserId' in locals() and lUserId >= 0:
print("正在登出设备...")
Objdll.NET_DVR_Logout(lUserId)
# 清理SDK资源
if 'Objdll' in locals():
print("正在清理SDK资源...")
Objdll.NET_DVR_Cleanup()
print("程序退出")