From cc1ff597cc562e942f3facbd87ebdf73de3f4f10 Mon Sep 17 00:00:00 2001 From: "Andrew K. Choi" Date: Wed, 3 Dec 2025 20:49:03 +0900 Subject: [PATCH] tests VIDEO TRANSFER UNAVAILABLE --- server.py | 123 ++++++++++++++++++++++++++++++++------------- test_frame.jpg | Bin 0 -> 332 bytes test_send_frame.py | 97 +++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 34 deletions(-) create mode 100644 test_frame.jpg create mode 100644 test_send_frame.py diff --git a/server.py b/server.py index 9034c0f..fa6d8d3 100644 --- a/server.py +++ b/server.py @@ -220,21 +220,25 @@ class ClientManager: """Добавление нового клиента""" global rooms, clients, room_stats, server_stats, stats_lock + print(f"[ClientManager] add_client called: room={room_id}, ip={ip_address}") + if room_id not in rooms: - print(f"[ClientManager] Room not found: {room_id}") + print(f"[ClientManager] ❌ Room not found: {room_id}") return None room = rooms[room_id] + print(f"[ClientManager] Found room: {room['name']}") if room["password"] != password: - print(f"[ClientManager] Invalid password for room: {room_id}") + print(f"[ClientManager] ❌ Invalid password for room: {room_id} (expected: {room['password']}, got: {password})") return None if len(room["clients"]) >= room["max_connections"]: - print(f"[ClientManager] Room {room_id} is full") + print(f"[ClientManager] ❌ Room {room_id} is full: {len(room['clients'])}/{room['max_connections']}") return None client_id = ClientManager.generate_client_id() + print(f"[ClientManager] Generated client_id: {client_id}") client = { "id": client_id, @@ -264,7 +268,7 @@ class ClientManager: server_stats["total_clients"] = len(clients) - print(f"[ClientManager] Added client: {client_id} to room: {room_id}") + print(f"[ClientManager] ✓ Added client: {client_id} to room: {room_id} (total in room: {len(room['clients'])})") return client_id @staticmethod @@ -329,7 +333,7 @@ class VideoProcessor: ) self.process.daemon = True self.process.start() - print(f"[VideoProcessor] Started for client: {self.client_id}") + print(f"[VideoProcessor] ✓ Started process for client: {self.client_id} (PID: {self.process.pid})") def stop(self): """Остановка процесса""" @@ -344,58 +348,80 @@ class VideoProcessor: @staticmethod def _process_video_stream(client_id: str): """Основной цикл обработки видео""" - print(f"[VideoProcessor] Process started for client {client_id}") + print(f"\n[VideoProcessor Process] ===== PROCESS START =====") + print(f"[VideoProcessor Process] Client ID: {client_id}, PID: {os.getpid()}") video_queue = None command_queue = None # Ждем инициализации очередей - for _ in range(100): + for attempt in range(100): try: if client_id in video_queues and client_id in command_queues: video_queue = video_queues[client_id] command_queue = command_queues[client_id] + print(f"[VideoProcessor Process] ✓ Queues found on attempt {attempt+1}") break except: pass time.sleep(0.1) if not video_queue or not command_queue: - print(f"[VideoProcessor] Queues not found for client {client_id}") + print(f"[VideoProcessor Process] ❌ CRITICAL: Queues not found after 10s for client {client_id}") return frame_count = 0 last_log_time = time.time() + last_frame_time = time.time() + + print(f"[VideoProcessor Process] ===== WAITING FOR FRAMES =====\n") while True: try: if not video_queue.empty(): frame_data = video_queue.get(timeout=1) frame_count += 1 + last_frame_time = time.time() - nparr = np.frombuffer(frame_data, np.uint8) - frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) - - if frame is not None: - # Применяем команды из очереди - while not command_queue.empty(): - try: - command = command_queue.get_nowait() - frame = VideoProcessor._apply_command(frame, command) - except: - break + try: + nparr = np.frombuffer(frame_data, np.uint8) + frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) - # Логируем каждые 5 секунд - current_time = time.time() - if current_time - last_log_time > 5: - print(f"[VideoProcessor] Client {client_id}: Processed {frame_count} frames") - last_log_time = current_time + if frame is not None: + # Применяем команды из очереди + commands_applied = 0 + while not command_queue.empty(): + try: + command = command_queue.get_nowait() + frame = VideoProcessor._apply_command(frame, command) + commands_applied += 1 + except: + break + + if commands_applied > 0: + print(f"[VideoProcessor Process] {client_id}: applied {commands_applied} commands to frame #{frame_count}") + else: + print(f"[VideoProcessor Process] ⚠️ {client_id}: frame decode FAILED at frame #{frame_count}") + except Exception as e: + print(f"[VideoProcessor Process] ❌ {client_id}: Error decoding frame #{frame_count}: {e}") + + # Логируем прогресс каждые 5 секунд + current_time = time.time() + if current_time - last_log_time > 5: + time_since_frame = current_time - last_frame_time + if frame_count == 0: + print(f"[VideoProcessor Process] ⚠️ {client_id}: NO FRAMES YET (waiting for {time_since_frame:.1f}s)") + else: + print(f"[VideoProcessor Process] {client_id}: Processed {frame_count} frames (last {time_since_frame:.1f}s ago)") + last_log_time = current_time time.sleep(0.001) except Exception as e: - # print(f"[VideoProcessor] Error: {e}") + print(f"[VideoProcessor Process] ❌ {client_id}: Error in main loop: {type(e).__name__}: {e}") time.sleep(0.1) + + print(f"[VideoProcessor Process] ===== PROCESS END =====\n") @staticmethod def _apply_command(frame: np.ndarray, command: Dict) -> np.ndarray: @@ -990,28 +1016,43 @@ async def client_websocket_endpoint(websocket: WebSocket, room_id: str, password try: client_ip = websocket.client.host if websocket.client else "unknown" + print(f"\n[WebSocket Client] ===== NEW CONNECTION =====") + print(f"[WebSocket Client] IP: {client_ip}, Room: {room_id}, Password: {password}") + client_id = ClientManager.add_client(room_id, password, client_ip) if not client_id: + print(f"[WebSocket Client] ❌ ClientManager.add_client returned None") await websocket.send_text(json.dumps({"error": "Invalid room or password"})) await websocket.close() return + print(f"[WebSocket Client] ✓ Client added: {client_id}") client_websockets[client_id] = websocket + print(f"[WebSocket Client] ✓ WebSocket registered for {client_id}") + video_processor = VideoProcessor(client_id) + print(f"[WebSocket Client] ✓ VideoProcessor created for {client_id}") + video_processor.start() + print(f"[WebSocket Client] ✓ VideoProcessor started for {client_id}") with stats_lock: if client_id in clients: clients[client_id]["is_streaming"] = True + print(f"[WebSocket Client] ✓ is_streaming set to True for {client_id}") - await websocket.send_text(json.dumps({ + success_msg = { "success": True, "client_id": client_id, "room_id": room_id - })) + } + await websocket.send_text(json.dumps(success_msg)) + print(f"[WebSocket Client] ✓ Sent success message to {client_id}") + print(f"[WebSocket Client] ===== WAITING FOR DATA =====\n") - print(f"[WebSocket] Client connected: {client_id} to room {room_id}") + frame_count = 0 + command_count = 0 while True: try: @@ -1024,6 +1065,13 @@ async def client_websocket_endpoint(websocket: WebSocket, room_id: str, password if client_id in video_queues: if not video_queues[client_id].full(): video_queues[client_id].put(message) + frame_count += 1 + if frame_count % 30 == 0: # log every 30 frames + print(f"[WebSocket Client] {client_id}: received {frame_count} frames") + else: + print(f"[WebSocket Client] ⚠️ {client_id}: video queue is FULL, dropping frame") + else: + print(f"[WebSocket Client] ❌ {client_id}: video_queue not found!") elif isinstance(message, str): try: @@ -1035,31 +1083,38 @@ async def client_websocket_endpoint(websocket: WebSocket, room_id: str, password if client_id in command_queues: command_queues[client_id].put(command) - except: - pass + command_count += 1 + print(f"[WebSocket Client] {client_id}: command received: {command.get('type')}") + except json.JSONDecodeError as e: + print(f"[WebSocket Client] ⚠️ {client_id}: invalid JSON: {message[:50]}") elif data["type"] == "websocket.disconnect": - print(f"[WebSocket] Client disconnected: {client_id}") + print(f"[WebSocket Client] {client_id}: CLIENT DISCONNECT (frames: {frame_count}, commands: {command_count})") break except WebSocketDisconnect: - print(f"[WebSocket] Client WebSocket disconnected: {client_id}") + print(f"[WebSocket Client] {client_id}: WEBSOCKET DISCONNECT (frames: {frame_count})") break except Exception as e: - print(f"[WebSocket] Error in client WebSocket {client_id}: {e}") + print(f"[WebSocket Client] ❌ {client_id}: Error in receive loop: {type(e).__name__}: {e}") break except Exception as e: - print(f"[WebSocket] Client connection error: {e}") + print(f"[WebSocket Client] ❌ Connection error: {type(e).__name__}: {e}") finally: + print(f"[WebSocket Client] ===== CLEANUP =====") if client_id: with stats_lock: if client_id in clients: clients[client_id]["is_streaming"] = False + print(f"[WebSocket Client] ✓ is_streaming set to False for {client_id}") ClientManager.disconnect_client(client_id) + print(f"[WebSocket Client] ✓ Disconnected client: {client_id}") if video_processor: video_processor.stop() + print(f"[WebSocket Client] ✓ VideoProcessor stopped for {client_id}") + print(f"[WebSocket Client] ===== END OF CONNECTION =====\n") @app.websocket("/ws/admin/{session_id}") async def admin_websocket_endpoint(websocket: WebSocket, session_id: str): diff --git a/test_frame.jpg b/test_frame.jpg new file mode 100644 index 0000000000000000000000000000000000000000..be9675df74bf1d548109ca273268759c48dd7e61 GIT binary patch literal 332 zcmex=^(PF6}rMnOeST|r4lSw=>~TvNxu(8R<)Zf(-wU zFvtT9X9aSAfB^~^nV4Bv+1NQaxwwG}whAyXF)}kVu`si;vakSE*8=4kSOi&x6b&8O zgaZ@Vl?p|S8YeE~Pwh=DOELf4NWZ*Q!{f5ODks=S2uSL zPp{yR(6I1`$f)F$)U@=B%&g*)(z5c3%Btp;*0%PJ&aO$5r%atTea6gLixw|gx@`H1 zm8&*w-m-Pu_8mKS9XfpE=&|D`PM*4S`O4L6*Kgds_3+W-Cr_U}fAR9w$4{TXeEs(Q U$IoB?Z!vIy{A17X`||&r0Jhm`vH$=8 literal 0 HcmV?d00001 diff --git a/test_send_frame.py b/test_send_frame.py new file mode 100644 index 0000000..a39ad62 --- /dev/null +++ b/test_send_frame.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +""" +Скрипт для отправки тестового JPEG-кадра на сервер. +Помогает проверить, работает ли VideoProcessor без мобильного приложения. +""" + +import asyncio +import websockets +import json +import sys +import cv2 +import numpy as np +from pathlib import Path + +# Параметры подключения +HOST = "192.168.0.112" # Измените на ваш PUBLIC_HOST из .env +PORT = 8000 +ROOM_ID = "DazqDVlwdGX" # Измените на ID комнаты +PASSWORD = "1" # Измените на пароль комнаты +FRAME_FILE = "test_frame.jpg" # Путь к тестовому JPEG-файлу + +async def send_frames(): + """Подключиться к WebSocket и отправить кадры""" + uri = f"ws://{HOST}:{PORT}/ws/client/{ROOM_ID}/{PASSWORD}" + print(f"[Test] Connecting to: {uri}") + + try: + async with websockets.connect(uri) as ws: + # Получаем ответ сервера + response = await asyncio.wait_for(ws.recv(), timeout=5) + print(f"[Test] ✓ Server response: {response}") + + # Читаем тестовый кадр + if not Path(FRAME_FILE).exists(): + print(f"[Test] File not found: {FRAME_FILE}") + print(f"[Test] Creating a proper JPEG test file with OpenCV...") + if not create_minimal_jpeg(FRAME_FILE): + print(f"[Test] ❌ Failed to create test JPEG") + return + + with open(FRAME_FILE, "rb") as f: + frame_data = f.read() + + print(f"[Test] Frame size: {len(frame_data)} bytes") + + # Отправляем кадры в цикле (30 FPS, 10 секунд) + print(f"[Test] Sending frames (30 FPS for 10 seconds)...") + for i in range(300): # 10 sec * 30 FPS + await ws.send(frame_data) + if (i + 1) % 30 == 0: + print(f"[Test] ✓ Sent {i + 1} frames") + await asyncio.sleep(1 / 30) # ~30 FPS + + print(f"[Test] ✓ Done! Sent 300 frames") + await asyncio.sleep(2) # Пауза перед отключением + + except asyncio.TimeoutError: + print(f"[Test] ❌ Timeout waiting for server response") + except ConnectionRefusedError: + print(f"[Test] ❌ Connection refused. Check HOST/PORT and firewall") + except Exception as e: + print(f"[Test] ❌ Error: {type(e).__name__}: {e}") + +def create_minimal_jpeg(filename): + """Создаёт корректный JPEG-файл (640x480 с синим фоном) для теста""" + # Создаём пустой образ 640x480 (синий) + image = np.zeros((480, 640, 3), dtype=np.uint8) + image[:, :] = (255, 0, 0) # BGR: синий цвет + + # Добавляем текст + cv2.putText(image, "Test Frame", (260, 240), + cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2) + + # Кодируем в JPEG + success, jpeg_data = cv2.imencode('.jpg', image) + if not success: + print(f"[Test] ❌ Failed to create JPEG") + return False + + # Сохраняем в файл + with open(filename, "wb") as f: + f.write(jpeg_data.tobytes()) + + print(f"[Test] ✓ Created test JPEG: {filename} ({len(jpeg_data.tobytes())} bytes)") + return True + +if __name__ == "__main__": + print("=" * 60) + print("WebSocket Frame Sender Test") + print("=" * 60) + print(f"Target: ws://{HOST}:{PORT}/ws/client/{ROOM_ID}/{PASSWORD}") + print() + + try: + asyncio.run(send_frames()) + except KeyboardInterrupt: + print("\n[Test] Interrupted by user")