录像截图功能提交

This commit is contained in:
zhaozilong12 2025-08-06 08:48:38 +08:00
parent 9d5bdb5005
commit 7bfe1f5acb
4 changed files with 182 additions and 122 deletions

View File

@ -618,47 +618,28 @@ def stop_video_streaming():
def start_detection():
"""开始检测"""
try:
if not db_manager:
return jsonify({'success': False, 'error': '数据库管理器未初始化'}), 500
if not db_manager or not device_manager:
return jsonify({'success': False, 'error': '数据库管理器或设备管理器未初始化'}), 500
data = flask_request.get_json()
patient_id = data.get('patientId') or data.get('patient_id')
settings = data.get('settings', '{}')
patient_id = data.get('patient_id')
creator_id = data.get('creator_id')
if not patient_id or not creator_id:
return jsonify({'success': False, 'error': '缺少患者ID或创建人ID'}), 400
if not patient_id:
return jsonify({
'success': False,
'error': '缺少必要参数: patient_id'
}), 400
# 调用create_detection_session方法settings传空字典
session_id = db_manager.create_detection_session(patient_id, settings={}, creator_id=creator_id)
# 解析设置参数
if isinstance(settings, str):
try:
settings = json.loads(settings)
except json.JSONDecodeError:
settings = {}
# 开始同步录制
recording_response = None
try:
recording_response = device_manager.start_recording(session_id, patient_id)
except Exception as rec_e:
logger.error(f'开始同步录制失败: {rec_e}')
# 创建检测会话
session_id = db_manager.create_detection_session(
patient_id=str(patient_id),
settings=settings,
creator_id=creator_id
)
start_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if session_id:
logger.info(f'检测会话已创建 - 会话ID: {session_id}, 患者ID: {patient_id}')
return jsonify({
'success': True,
'session_id': session_id,
'message': '检测会话创建成功'
})
else:
return jsonify({
'success': False,
'error': '创建检测会话失败'
}), 500
return jsonify({'success': True, 'session_id': session_id, 'detectionStartTime': start_time, 'recording': recording_response})
except Exception as e:
logger.error(f'开始检测失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@ -667,15 +648,50 @@ def start_detection():
def stop_detection(session_id):
"""停止检测"""
try:
if not db_manager:
return jsonify({'success': False, 'error': '数据库管理器未初始化'}), 500
if not db_manager or not device_manager:
logger.error('数据库管理器或设备管理器未初始化')
return jsonify({'success': False, 'error': '数据库管理器或设备管理器未初始化'}), 500
if not session_id:
logger.error('缺少会话ID')
return jsonify({
'success': False,
'error': '缺少会话ID'
}), 400
data = flask_request.get_json()
logger.debug(f'接收到停止检测请求session_id: {session_id}, 请求数据: {data}')
# video_data = data.get('videoData') if data else None
video_data = data['videoData']
mime_type = data.get('mimeType', 'video/webm;codecs=vp9') # 默认webm格式
# 验证base64视频数据格式
if not video_data.startswith('data:video/'):
return jsonify({
'success': False,
'message': '无效的视频数据格式'
}), 400
# try:
# header, encoded = video_data.split(',', 1)
# video_bytes = base64.b64decode(encoded)
# except Exception as e:
# return jsonify({
# 'success': False,
# 'message': f'视频数据解码失败: {str(e)}'
# }), 400
# 停止同步录制,传递视频数据
try:
logger.debug(f'调用device_manager.stop_recordingsession_id: {session_id}, video_data长度: {len(video_data) if video_data else 0}')
if video_data is None:
logger.warning(f'视频数据为空session_id: {session_id}')
else:
logger.debug(f'视频数据长度: {len(video_data)} 字符,约 {len(video_data)*3/4/1024:.2f} KB, session_id: {session_id}')
restrt=device_manager.stop_recording(session_id, video_data_base64=video_data)
logger.error(restrt)
except Exception as rec_e:
logger.error(f'停止同步录制失败: {rec_e}', exc_info=True)
raise
# 更新会话状态为已完成
success = db_manager.update_session_status(session_id, 'completed')
@ -686,13 +702,14 @@ def stop_detection(session_id):
'message': '检测已停止'
})
else:
logger.error('停止检测失败,更新会话状态失败')
return jsonify({
'success': False,
'error': '停止检测失败'
}), 500
except Exception as e:
logger.error(f'停止检测失败: {e}')
logger.error(f'停止检测失败: {e}', exc_info=True)
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/detection/<session_id>/status', methods=['GET'])
@ -823,6 +840,7 @@ def stop_sync_recording():
data = flask_request.get_json()
session_id = data.get('session_id')
video_data = data.get('videoData') # 新增接收前端传递的视频数据
if not session_id:
return jsonify({
@ -830,20 +848,19 @@ def stop_sync_recording():
'error': '缺少必要参数: session_id'
}), 400
result = device_manager.stop_recording(session_id)
result = device_manager.stop_recording(session_id, video_data_base64=video_data)
if result['success']:
logger.info(f'同步录制已停止 - 会话ID: {session_id}')
return jsonify(result)
else:
return jsonify(result), 500
except Exception as e:
logger.error(f'停止同步录制失败: {e}')
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/history/sessions', methods=['GET'])
def get_detection_sessions():
"""获取检测会话历史"""

Binary file not shown.

View File

@ -632,21 +632,19 @@ class DatabaseManager:
cursor.execute('''
INSERT INTO detection_sessions (
id, patient_id, creator_id, duration, frequency, settings, status,
diagnosis_info, treatment_info, suggestion_info, notes, start_time, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
id, patient_id, creator_id, duration, settings, status,
diagnosis_info, treatment_info, suggestion_info, start_time, created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
session_id,
patient_id,
creator_id,
settings.get('duration', 60),
settings.get('frequency', 60),
json.dumps(settings),
'created',
'running',
settings.get('diagnosis_info', ''),
settings.get('treatment_info', ''),
settings.get('suggestion_info', ''),
settings.get('notes', ''),
china_time,
china_time
))
@ -660,7 +658,7 @@ class DatabaseManager:
logger.error(f'创建检测会话失败: {e}')
raise
def update_session_status(self, session_id: str, status: str, data_points: int = 0):
def update_session_status(self, session_id: str, status: str):
"""更新会话状态"""
conn = self.get_connection()
cursor = conn.cursor()
@ -671,15 +669,15 @@ class DatabaseManager:
china_time = self.get_china_time()
cursor.execute('''
UPDATE detection_sessions SET
status = ?, data_points = ?, end_time = ?
status = ?, end_time = ?
WHERE id = ?
''', (status, data_points, china_time, session_id))
''', (status, china_time, session_id))
else:
cursor.execute('''
UPDATE detection_sessions SET
status = ?, data_points = ?
status = ?
WHERE id = ?
''', (status, data_points, session_id))
''', (status, session_id))
conn.commit()
logger.info(f'更新会话状态: {session_id} -> {status}')

View File

@ -24,6 +24,7 @@ from concurrent.futures import ThreadPoolExecutor
import logging
# 数据库管理
# from backend.app import get_detection_sessions
from database import DatabaseManager
# FemtoBolt深度相机支持
@ -265,10 +266,14 @@ class DeviceManager:
if platform.system() == "Windows":
# 优先使用Orbbec SDK K4A Wrapper与azure_kinect_image_example.py一致
orbbec_paths = [
r"D:\OrbbecSDK_K4A_Wrapper_v1.10.3_windows_202408091749\bin\k4a.dll",
]
base_dir = os.path.dirname(os.path.abspath(__file__))
dll_path = os.path.join(base_dir, "dll", "bin", "k4a.dll")
orbbec_paths = []
if os.path.exists(dll_path):
orbbec_paths.append(dll_path)
# orbbec_paths = [
# r"D:\BodyBalanceEvaluation\backend\dll\bin\k4a.dll",
# ]
# Azure Kinect SDK标准安装路径备用
standard_paths = [
r"C:\Program Files\Azure Kinect SDK v1.4.1\sdk\windows-desktop\amd64\release\bin\k4a.dll",
@ -498,54 +503,80 @@ class DeviceManager:
}
try:
# 1. 采集头部姿态数据从IMU设备获取
if self.device_status['imu']:
head_pose_data = self._collect_head_pose_data()
if head_pose_data:
data['head_pose'] = json.dumps(head_pose_data)
logger.debug(f'头部姿态数据采集成功: {session_id}')
# # 1. 采集头部姿态数据从IMU设备获取
# if self.device_status['imu']:
# head_pose_data = self._collect_head_pose_data()
# if head_pose_data:
# data['head_pose'] = json.dumps(head_pose_data)
# logger.debug(f'头部姿态数据采集成功: {session_id}')
# 2. 采集身体姿态数据从FemtoBolt深度相机获取
if self.device_status['femtobolt']:
body_pose_data = self._collect_body_pose_data()
if body_pose_data:
data['body_pose'] = json.dumps(body_pose_data)
logger.debug(f'身体姿态数据采集成功: {session_id}')
# # 2. 采集身体姿态数据从FemtoBolt深度相机获取
# if self.device_status['femtobolt']:
# body_pose_data = self._collect_body_pose_data()
# if body_pose_data:
# data['body_pose'] = json.dumps(body_pose_data)
# logger.debug(f'身体姿态数据采集成功: {session_id}')
# 3. 采集身体视频截图从FemtoBolt深度相机获取
if self.device_status['femtobolt']:
body_image_path = self._capture_body_image(data_dir)
if body_image_path:
data['body_image'] = str(body_image_path)
logger.debug(f'身体截图保存成功: {body_image_path}')
# # 3. 采集身体视频截图从FemtoBolt深度相机获取
# if self.device_status['femtobolt']:
# body_image_path = self._capture_body_image(data_dir)
# if body_image_path:
# data['body_image'] = str(body_image_path)
# logger.debug(f'身体截图保存成功: {body_image_path}')
# 4. 采集足部压力数据(从压力传感器获取)
if self.device_status['pressure']:
foot_data = self._collect_foot_pressure_data()
if foot_data:
data['foot_data'] = json.dumps(foot_data)
logger.debug(f'足部压力数据采集成功: {session_id}')
# # 4. 采集足部压力数据(从压力传感器获取)
# if self.device_status['pressure']:
# foot_data = self._collect_foot_pressure_data()
# if foot_data:
# data['foot_data'] = json.dumps(foot_data)
# logger.debug(f'足部压力数据采集成功: {session_id}')
# 5. 采集足部监测视频截图(从摄像头获取)
if self.device_status['camera']:
foot_image_path = self._capture_foot_image(data_dir)
if foot_image_path:
data['foot_image'] = str(foot_image_path)
logger.debug(f'足部截图保存成功: {foot_image_path}')
# # 5. 采集足部监测视频截图(从摄像头获取)
# if self.device_status['camera']:
# foot_image_path = self._capture_foot_image(data_dir)
# if foot_image_path:
# data['foot_image'] = str(foot_image_path)
# logger.debug(f'足部截图保存成功: {foot_image_path}')
# 6. 生成足底压力数据图(从压力传感器数据生成)
if self.device_status['pressure']:
foot_data_image_path = self._generate_foot_pressure_image(data_dir)
if foot_data_image_path:
data['foot_data_image'] = str(foot_data_image_path)
logger.debug(f'足底压力数据图生成成功: {foot_data_image_path}')
# # 6. 生成足底压力数据图(从压力传感器数据生成)
# if self.device_status['pressure']:
# foot_data_image_path = self._generate_foot_pressure_image(data_dir)
# if foot_data_image_path:
# data['foot_data_image'] = str(foot_data_image_path)
# logger.debug(f'足底压力数据图生成成功: {foot_data_image_path}')
# 7. 保存屏幕录制截图从前端传入的base64数据
if screen_image_base64:
screen_image_path = self._save_screen_image(data_dir, screen_image_base64)
if screen_image_path:
data['screen_image'] = str(screen_image_path)
logger.debug(f'屏幕截图保存成功: {screen_image_path}')
try:
logger.debug(f'屏幕截图保存.................{screen_image_base64}')
# 保存屏幕截图的base64数据为图片文件
screen_image_path = None
if screen_image_base64:
try:
if screen_image_base64.startswith('data:image/'):
base64_data = screen_image_base64.split(',')[1]
else:
base64_data = screen_image_base64
image_data = base64.b64decode(base64_data)
image_path = data_dir / 'screen_image.png'
with open(image_path, 'wb') as f:
f.write(image_data)
abs_image_path = image_path.resolve()
abs_cwd = Path.cwd().resolve()
screen_image_path = str(abs_image_path.relative_to(abs_cwd))
logger.debug(f'屏幕截图保存成功: {screen_image_path}')
except Exception as e:
logger.error(f'屏幕截图保存失败: {e}')
import traceback
logger.error(traceback.format_exc())
if screen_image_path:
data['screen_image'] = str(screen_image_path)
logger.debug(f'屏幕截图保存成功: {screen_image_path}')
except Exception as e:
logger.error(f'屏幕截图保存失败: {e}')
import traceback
logger.error(traceback.format_exc())
# 更新最新数据
with self.data_lock:
@ -1073,10 +1104,10 @@ class DeviceManager:
body_video_path, fourcc, fps, (1280, 720)
)
# 屏幕录制写入器(默认分辨率,后续根据实际帧调整)
self.screen_video_writer = cv2.VideoWriter(
screen_video_path, fourcc, fps, (1920, 1080)
)
# # 屏幕录制写入器(默认分辨率,后续根据实际帧调整)
# self.screen_video_writer = cv2.VideoWriter(
# screen_video_path, fourcc, fps, (1920, 1080)
# )
# 重置停止事件
self.recording_stop_event.clear()
@ -1097,14 +1128,14 @@ class DeviceManager:
name='BodyRecordingThread'
)
self.body_recording_thread.start()
#屏幕录制
if self.screen_video_writer:
self.screen_recording_thread = threading.Thread(
target=self._screen_recording_thread,
daemon=True,
name='ScreenRecordingThread'
)
self.screen_recording_thread.start()
# #屏幕录制
# if self.screen_video_writer:
# self.screen_recording_thread = threading.Thread(
# target=self._screen_recording_thread,
# daemon=True,
# name='ScreenRecordingThread'
# )
# self.screen_recording_thread.start()
# 设置录制状态
self.sync_recording = True
@ -1122,21 +1153,15 @@ class DeviceManager:
return result
def stop_recording(self, session_id: str) -> Dict[str, Any]:
def stop_recording(self, session_id: str, video_data_base64: str = None) -> Dict[str, Any]:
"""停止同步录制
Args:
session_id: 检测会话ID
video_data_base64: 屏幕录制视频的base64编码数据可选
Returns:
Dict: 录制停止状态和信息
{
'success': bool,
'session_id': str,
'recording_duration': float,
'video_files': List[str],
'message': str
}
"""
result = {
'success': False,
@ -1158,12 +1183,18 @@ class DeviceManager:
# 设置停止事件
self.recording_stop_event.set()
session_data = self.db_manager.get_session_data(session_id)
base_path = os.path.join('data', 'patients', session_data['patient_id'], session_id)
# 定义视频文件路径
feet_video_path = os.path.join(base_path, 'feet.mp4')
body_video_path = os.path.join(base_path, 'body.mp4')
screen_video_path = os.path.join(base_path, 'screen.mp4')
# 等待录制线程结束
threads_to_join = [
(self.feet_recording_thread, 'feet'),
(self.body_recording_thread, 'body'),
(self.screen_recording_thread, 'screen')
(self.body_recording_thread, 'body')
]
for thread, name in threads_to_join:
@ -1179,16 +1210,30 @@ class DeviceManager:
# 清理视频写入器并收集文件信息
video_files = self._cleanup_video_writers()
# 保存传入的屏幕录制视频数据,替代原有屏幕录制视频保存逻辑
if video_data_base64:
try:
video_bytes = base64.b64decode(video_data_base64)
with open(screen_video_path, 'wb') as f:
f.write(video_bytes)
video_files.append(screen_video_path)
logger.info(f'屏幕录制视频保存成功,路径: {screen_video_path}, 文件大小: {os.path.getsize(screen_video_path)} 字节')
except Exception as e:
logger.error(f'保存屏幕录制视频失败: {e}', exc_info=True)
logger.debug(f'视频数据长度: {len(video_data_base64)}')
raise
result['video_files'] = video_files
# 更新数据库中的会话信息
if self.db_manager and result['recording_duration'] > 0:
try:
# 更新会话持续时间
duration_seconds = int(result['recording_duration'])
self.db_manager.update_session_duration(session_id, duration_seconds)
# 更新会话状态为已完成
self.db_manager.update_session_normal_video_path(session_id, feet_video_path)
self.db_manager.update_session_femtobolt_video_path(session_id, body_video_path)
self.db_manager.update_session_screen_video_path(session_id, screen_video_path)
self.db_manager.update_session_status(session_id, 'completed')
logger.debug(f'数据库会话信息更新成功 - 会话ID: {session_id}, 持续时间: {duration_seconds}')
@ -1207,7 +1252,7 @@ class DeviceManager:
logger.debug(f'同步录制已停止 - 会话ID: {session_id}, 录制时长: {result["recording_duration"]:.2f}')
except Exception as e:
logger.error(f'停止同步录制失败: {e}')
logger.error(f'停止同步录制失败: {e}', exc_info=True)
result['message'] = f'停止录制失败: {str(e)}'
return result