У меня есть приложение на FastApi, которое трекает правильность выполнения упражнения с помощью mediapipe. Я устанавливаю соединение клиента с сервером через websocket, но спустя какое-то время соединение разрывается. На сервер я передаю json, в котором передаются некоторые данные и зашифрованные в Base64 фреймы с камеры пользователя. Обратно сервер передает количество повторений на момент фрейма.
Так выглядит мой веб сокет:
@router.websocket("")
async def workout_connection(websocket: WebSocket):
global connections
connection_id = str(uuid.uuid4())
await websocket.accept()
connections[connection_id] = {"websocket": websocket, "video_frames": []}
logger.info(f"New WebSocket connection: {connection_id}")
try:
while True:
data_json = await websocket.receive_json()
if "type" not in data_json:
continue
if data_json["type"] == "reset":
connections[connection_id]["repetitions_count"] = 0
connections[connection_id]["video_frames"] = []
await websocket.send_json(
{
"type": "reset",
"connection_id": connection_id,
}
)
continue
if data_json.get("is_resting", False):
await websocket.send_json(
{
"type": "rest",
"connection_id": connection_id,
}
)
continue
exercise_type = data_json["type"]
if data_json.get("is_downloading", False):
continue
video_b64 = data_json["data"]
video_bytes = base64.b64decode(video_b64)
np_array = np.frombuffer(video_bytes, np.uint8)
img = cv2.imdecode(np_array, cv2.IMREAD_COLOR)
if exercise_type == "high_knees":
frame, _, repetitions_count = process_high_knees(
img, connections[connection_id]
)
elif exercise_type == "jumping_jacks":
print(f"outside excercis = {connections[connection_id]}")
frame, _, repetitions_count = process_jumping_jacks(
img, connections[connection_id]
)
elif exercise_type == "side_lunge":
frame, _, repetitions_count = side_lunge(
img, connections[connection_id]
)
elif exercise_type == "side_kick":
frame, _, repetitions_count = side_kick(
img, connections[connection_id]
)
elif exercise_type == "bycicle":
frame, _, repetitions_count = bycicle(
img, connections[connection_id]
)
elif exercise_type == "leg_swings":
frame, _, repetitions_count = leg_swings(
img, connections[connection_id]
)
elif exercise_type == "melnica":
frame, _, repetitions_count = melnica(
img, connections[connection_id]
)
elif exercise_type == "leg_abduption":
frame, _, repetitions_count = leg_abduption(
img, connections[connection_id]
)
elif exercise_type == "pushups":
frame, _, repetitions_count = pushups(
img, connections[connection_id]
)
elif exercise_type == "move_plank":
frame, _, repetitions_count = move_plank(
img, connections[connection_id]
)
elif exercise_type == "plank":
frame, _, repetitions_count = plank(
img, connections[connection_id]
)
elif exercise_type == "climbers_steps":
frame, _, repetitions_count = climbers_steps(
img, connections[connection_id]
)
elif exercise_type == "kick":
print(f"outside excercis = {connections[connection_id]}")
frame, _, repetitions_count = kick(
img, connections[connection_id]
)
elif exercise_type == "standing_curls":
frame, _, repetitions_count = standing_curls(
img, connections[connection_id]
)
elif exercise_type == "pelvic_lift":
frame, _, repetitions_count = pelvic_lift(
img, connections[connection_id]
)
elif exercise_type == "pelvic_static":
frame, _, repetitions_count = pelvic_static(
img, connections[connection_id]
)
elif exercise_type == "leg_raises_elbow_rest":
frame, _, repetitions_count = leg_raises_elbow_rest(
img, connections[connection_id]
)
elif exercise_type == "sqats":
frame, _, repetitions_count = sqats(
img, connections[connection_id]
)
elif exercise_type == "sqats_static":
frame, _, repetitions_count = sqats_static(
img, connections[connection_id]
)
elif exercise_type == "press":
frame, _, repetitions_count = press(
img, connections[connection_id]
)
elif exercise_type == "upor_lezha":
frame, _, repetitions_count = upor_lezha(
img, connections[connection_id]
)
else:
frame, _, repetitions_count = process_jumping_jacks(
img, connections[connection_id]
)
await websocket.send_json(
{
"type": "count",
"data": repetitions_count,
"connection_id": connection_id,
}
)
except WebSocketDisconnect:
del connections[connection_id]
logger.info(f"WebSocket connection closed: {connection_id}")
print(f"WebSocket connection closed: {connection_id}")
try:
await websocket.close()
except:
pass
Вот что как отправляет данные JS:
function bufferToBase64(buffer) {
let binary = "";
let bytes = new Uint8Array(buffer);
let len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
function startVideoProcessing() {
startTime = Date.now();
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
function sendFrame() {
if (video.paused || video.ended) return;
const aspectRatio = video.videoWidth / video.videoHeight;
const targetHeight = 360;
const targetWidth = aspectRatio * targetHeight;
canvas.width = targetWidth;
canvas.height = targetHeight;
context.drawImage(video, 0, 0, targetWidth, targetHeight);
canvas.toBlob(
(blob) => {
if (blob) {
blob.arrayBuffer().then((buffer) => {
let b64Data = isResting ? null : bufferToBase64(buffer);
const data = JSON.stringify({
type: currentExercise.exercise_id,
data: b64Data,
is_resting: isResting,
is_downloading: isDownloading,
is_completed: isCompleted,
});
ws.send(data);
});
}
},
"image/jpeg",
0.5
);
setTimeout(sendFrame, interval);
}
setInterval(updateTimer, 1000);
sendFrame();
}
Вот какую ошибку вижу в логах контейнера:
[2024-09-05 01:29:16 +0000] [18] [ERROR] Exception in ASGI application
Traceback (most recent call last):
...
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
Понятно как бы, что ожидаются двойные кавычки, но почему ошибка не всплывает сразу, а через некоторое время, и где там может не хватать кавычек?
Вот такого рода json приходят:
{"type":"sqats","data":"/9j/4AAQSkZJRgA...(Зашифрованный фрейм)","is_resting": false, "is_downloading": false, "is_completed": false}