优化云台跟踪效果
This commit is contained in:
1 parent
aa0c27d499
commit
1b12e76fdb
1860 files changed
+537
-480293
No files matched your search
@@ -0,0 +1,145 @@
|
||||
import threading
|
||||
import time
|
||||
import re
|
||||
import traceback
|
||||
import paho.mqtt.client as mqtt
|
||||
from flask import Blueprint, Response, session
|
||||
|
||||
from app.glo import *
|
||||
|
||||
# ==================== 全局配置 ====================
|
||||
mqtt_client = None
|
||||
MQTT_SUB_TOPIC = ""
|
||||
latest_frame_cache = {}
|
||||
cache_lock = threading.Lock()
|
||||
mqtt_sub_lock = threading.Lock()
|
||||
|
||||
wrobot_cam = Blueprint('wrobot_cam', __name__)
|
||||
|
||||
|
||||
# ==================== MQTT 核心逻辑 ====================
|
||||
def on_mqtt_connect(client, userdata, flags, rc):
|
||||
"""MQTT连接成功回调:此时暂不订阅(等第一次页面访问时再订阅)"""
|
||||
if rc == 0:
|
||||
print("MQTT连接成功(暂未订阅任何主题,等待页面访问)")
|
||||
else:
|
||||
print(f"MQTT连接失败 错误码: {rc}")
|
||||
|
||||
def on_mqtt_message(client, userdata, message):
|
||||
"""MQTT消息回调:仅处理已订阅的主题"""
|
||||
try:
|
||||
if not MQTT_SUB_TOPIC or message.topic != MQTT_SUB_TOPIC:
|
||||
return
|
||||
|
||||
device_id = message.topic.split('/')[0]
|
||||
if len(message.payload) < 100:
|
||||
return
|
||||
|
||||
with cache_lock:
|
||||
latest_frame_cache[device_id] = message.payload
|
||||
|
||||
except Exception as e:
|
||||
print(f"MQTT消息处理异常: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
def start_mqtt_background():
|
||||
"""启动MQTT客户端(仅连接,不订阅)"""
|
||||
global mqtt_client
|
||||
if mqtt_client is not None:
|
||||
return
|
||||
|
||||
mqtt_client = mqtt.Client()
|
||||
mqtt_client.on_connect = on_mqtt_connect
|
||||
mqtt_client.on_message = on_mqtt_message
|
||||
|
||||
try:
|
||||
mqtt_client.connect(MQTT_BROKER, MQTT_PORT, keepalive=60)
|
||||
mqtt_client.loop_start()
|
||||
print(f"MQTT客户端已启动 🚀(仅连接,未订阅主题)")
|
||||
except Exception as e:
|
||||
print(f"MQTT启动失败: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
def subscribe_first_topic(new_topic):
|
||||
"""第一次订阅主题(初始化订阅)"""
|
||||
global MQTT_SUB_TOPIC
|
||||
if not mqtt_client or not mqtt_client.is_connected():
|
||||
raise RuntimeError("MQTT客户端未连接")
|
||||
|
||||
with mqtt_sub_lock:
|
||||
MQTT_SUB_TOPIC = new_topic
|
||||
mqtt_client.subscribe(MQTT_SUB_TOPIC)
|
||||
print(f"MQTT首次订阅: {MQTT_SUB_TOPIC}")
|
||||
|
||||
def update_mqtt_subscription(new_topic):
|
||||
"""更新订阅主题(非首次)"""
|
||||
global MQTT_SUB_TOPIC
|
||||
if not mqtt_client or not mqtt_client.is_connected():
|
||||
raise RuntimeError("MQTT客户端未连接")
|
||||
|
||||
with mqtt_sub_lock:
|
||||
# 取消旧主题订阅
|
||||
mqtt_client.unsubscribe(MQTT_SUB_TOPIC)
|
||||
print(f"MQTT取消订阅: {MQTT_SUB_TOPIC}")
|
||||
|
||||
# 订阅新主题
|
||||
MQTT_SUB_TOPIC = new_topic
|
||||
mqtt_client.subscribe(MQTT_SUB_TOPIC)
|
||||
print(f"MQTT重新订阅: {MQTT_SUB_TOPIC}")
|
||||
|
||||
# ==================== Flask 路由核心逻辑 ====================
|
||||
def generate_video_frame(device_id):
|
||||
"""生成视频流"""
|
||||
blank_jpeg = b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xd9'
|
||||
|
||||
while True:
|
||||
try:
|
||||
with cache_lock:
|
||||
frame_data = latest_frame_cache.get(device_id, blank_jpeg)
|
||||
|
||||
yield (b'--frame\r\n'
|
||||
b'Content-Type: image/jpeg\r\n\r\n' + frame_data + b'\r\n')
|
||||
|
||||
time.sleep(0.01)
|
||||
|
||||
except GeneratorExit:
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"视频流生成异常: {e}")
|
||||
yield (b'--frame\r\n'
|
||||
b'Content-Type: image/jpeg\r\n\r\n' + blank_jpeg + b'\r\n')
|
||||
time.sleep(0.1)
|
||||
|
||||
@wrobot_cam.route('/wrobot_cam/<string:device_id>')
|
||||
@wrobot_cam.route('/wrobot_cam')
|
||||
def main(device_id=None):
|
||||
"""核心逻辑:第一次访问时初始化订阅,后续访问时更新订阅"""
|
||||
# 1. 校验设备ID(无参数时直接报错,因为启动时未知默认设备)
|
||||
if not device_id:
|
||||
return Response("必须指定设备ID(访问方式:/wrobot_cam/设备ID)", status=400, mimetype='text/plain')
|
||||
|
||||
device_list = GetUserDeviceIdList(session['user']['user_id'])
|
||||
if device_id not in device_list:
|
||||
return Response(f"设备ID不合法", status=400, mimetype='text/plain')
|
||||
|
||||
|
||||
# 2. 构建真实主题
|
||||
new_mqtt_topic = f"{device_id}/head_video"
|
||||
|
||||
# 3. 区分「首次订阅」和「更新订阅」
|
||||
try:
|
||||
if not MQTT_SUB_TOPIC: # 初始值为空 → 第一次访问,初始化订阅
|
||||
threading.Thread(target=subscribe_first_topic, args=(new_mqtt_topic,), daemon=True).start()
|
||||
elif new_mqtt_topic != MQTT_SUB_TOPIC: # 非首次,更新订阅
|
||||
threading.Thread(target=update_mqtt_subscription, args=(new_mqtt_topic,), daemon=True).start()
|
||||
except RuntimeError as e:
|
||||
return Response(f"MQTT客户端异常:{str(e)}", status=500, mimetype='text/plain')
|
||||
|
||||
# 4. 返回视频流
|
||||
return Response(
|
||||
generate_video_frame(device_id),
|
||||
mimetype='multipart/x-mixed-replace; boundary=frame'
|
||||
)
|
||||
|
||||
# ==================== 启动初始化 ====================
|
||||
start_mqtt_background()
|
||||
Reference in new issue
Block a user