Spaces:
Sleeping
Sleeping
feature: implement session management
Browse files- src/app.py +89 -71
src/app.py
CHANGED
@@ -1,89 +1,107 @@
|
|
1 |
-
from flask import Flask, Response, stream_with_context
|
2 |
import cv2
|
|
|
3 |
import threading
|
4 |
import time
|
5 |
import os
|
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
app = Flask(__name__)
|
8 |
PORT = int(os.environ.get("PORT", 5000))
|
9 |
|
10 |
-
instantiated = False
|
11 |
-
|
12 |
-
latest_frame = None
|
13 |
lock = threading.Lock()
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
def
|
18 |
-
global
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
cap.
|
37 |
-
|
38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
with lock:
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
now = time.time()
|
47 |
-
if now - last_time >= rolling_duration:
|
48 |
-
measured_fps = frame_count / (now - last_time)
|
49 |
-
frame_count = 0
|
50 |
-
last_time = now
|
51 |
-
|
52 |
-
# control framerate
|
53 |
-
next_frame_time += frame_duration
|
54 |
-
sleep_time = next_frame_time - time.time()
|
55 |
-
if sleep_time > 0:
|
56 |
-
time.sleep(sleep_time)
|
57 |
-
else:
|
58 |
-
next_frame_time = time.time()
|
59 |
-
|
60 |
-
def generate():
|
61 |
-
while True:
|
62 |
-
if latest_frame is None:
|
63 |
-
continue
|
64 |
-
with lock:
|
65 |
-
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 50]
|
66 |
-
ret, jpeg = cv2.imencode(".jpg", latest_frame, encode_param)
|
67 |
-
frame_bytes = jpeg.tobytes() if ret else None
|
68 |
-
# print(f"frame filesize: {len(frame_bytes) / (1024 * 1024):.4f} MB")
|
69 |
-
if frame_bytes is not None:
|
70 |
-
yield (b"--frame\r\n"b"Content-Type: image/jpeg\r\n\r\n" + frame_bytes + b"\r\n")
|
71 |
|
72 |
@app.route("/")
|
73 |
def video_feed():
|
74 |
-
global
|
75 |
|
76 |
-
|
77 |
-
instantiated = True
|
78 |
-
threading.Thread(target=video_reader, daemon=True).start()
|
79 |
-
|
80 |
-
return Response(stream_with_context(generate()), mimetype="multipart/x-mixed-replace; boundary=frame")
|
81 |
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
|
|
87 |
|
88 |
if __name__ == "__main__":
|
89 |
app.run(host="0.0.0.0", port=PORT, threaded=True)
|
|
|
1 |
+
from flask import Flask, Response, stream_with_context, request
|
2 |
import cv2
|
3 |
+
from cv2.typing import MatLike
|
4 |
import threading
|
5 |
import time
|
6 |
import os
|
7 |
|
8 |
+
class Session:
|
9 |
+
def __init__(self, id: str, latest_frame: MatLike, measured_fps: float, show_fps: bool):
|
10 |
+
self.id = id
|
11 |
+
self.latest_frame: MatLike = latest_frame
|
12 |
+
self.measured_fps = measured_fps
|
13 |
+
self.show_fps = show_fps
|
14 |
+
|
15 |
app = Flask(__name__)
|
16 |
PORT = int(os.environ.get("PORT", 5000))
|
17 |
|
|
|
|
|
|
|
18 |
lock = threading.Lock()
|
19 |
+
|
20 |
+
sessions: set[Session] = set()
|
21 |
+
|
22 |
+
def frame_processor(session_id: str):
|
23 |
+
global sessions
|
24 |
+
|
25 |
+
session: Session = next((s for s in sessions if s.id == session_id), None)
|
26 |
+
|
27 |
+
if session is not None:
|
28 |
+
cap = cv2.VideoCapture("videos/classroom.mp4")
|
29 |
+
framerate = cap.get(cv2.CAP_PROP_FPS)
|
30 |
+
frame_duration = 1 / framerate
|
31 |
+
|
32 |
+
# variables to measure FPS
|
33 |
+
frame_count = 0
|
34 |
+
last_time = time.time()
|
35 |
+
rolling_duration = 0.5
|
36 |
+
|
37 |
+
# variables to control framerate
|
38 |
+
next_frame_time = time.time()
|
39 |
+
|
40 |
+
while True:
|
41 |
+
ret, frame = cap.read()
|
42 |
+
|
43 |
+
if not ret:
|
44 |
+
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
|
45 |
+
continue
|
46 |
+
else:
|
47 |
+
with lock:
|
48 |
+
if session.show_fps:
|
49 |
+
text = f"{session_id}: {session.measured_fps:.0f}"
|
50 |
+
font = cv2.FONT_HERSHEY_SIMPLEX
|
51 |
+
font_scale = 1
|
52 |
+
thickness = 2
|
53 |
+
color = (0, 255, 0)
|
54 |
+
(text_width, text_height), _ = cv2.getTextSize(text, font, font_scale, thickness)
|
55 |
+
x = frame.shape[1] - text_width - 10
|
56 |
+
y = 30
|
57 |
+
cv2.putText(frame, text, (x, y), font, font_scale, color, thickness, cv2.LINE_AA)
|
58 |
+
session.latest_frame = frame
|
59 |
+
|
60 |
+
# measure FPS
|
61 |
+
frame_count += 1
|
62 |
+
now = time.time()
|
63 |
+
if now - last_time >= rolling_duration:
|
64 |
+
session.measured_fps = frame_count / (now - last_time)
|
65 |
+
frame_count = 0
|
66 |
+
last_time = now
|
67 |
+
|
68 |
+
# control framerate
|
69 |
+
next_frame_time += frame_duration
|
70 |
+
sleep_time = next_frame_time - time.time()
|
71 |
+
if sleep_time > 0:
|
72 |
+
time.sleep(sleep_time)
|
73 |
+
else:
|
74 |
+
next_frame_time = time.time()
|
75 |
+
|
76 |
+
def stream_mjpeg(session_id: str):
|
77 |
+
global sessions
|
78 |
+
|
79 |
+
session: Session = next((s for s in sessions if s.id == session_id), None)
|
80 |
+
|
81 |
+
if session is not None:
|
82 |
+
while True:
|
83 |
+
if session.latest_frame is None:
|
84 |
+
continue
|
85 |
with lock:
|
86 |
+
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 50]
|
87 |
+
ret, jpeg = cv2.imencode(".jpg", session.latest_frame, encode_param)
|
88 |
+
frame_bytes = jpeg.tobytes() if ret else None
|
89 |
+
# print(f"frame filesize: {len(frame_bytes) / (1024 * 1024):.4f} MB")
|
90 |
+
if frame_bytes is not None:
|
91 |
+
yield (b"--frame\r\n"b"Content-Type: image/jpeg\r\n\r\n" + frame_bytes + b"\r\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
|
93 |
@app.route("/")
|
94 |
def video_feed():
|
95 |
+
global sessions
|
96 |
|
97 |
+
session_id = request.args.get("id", "default")
|
|
|
|
|
|
|
|
|
98 |
|
99 |
+
if session_id not in [s.id for s in sessions]:
|
100 |
+
session = Session(session_id, None, 0, True)
|
101 |
+
sessions.add(session)
|
102 |
+
threading.Thread(target=frame_processor, args=[session_id], daemon=True).start()
|
103 |
+
|
104 |
+
return Response(stream_with_context(stream_mjpeg(session_id)), mimetype="multipart/x-mixed-replace; boundary=frame")
|
105 |
|
106 |
if __name__ == "__main__":
|
107 |
app.run(host="0.0.0.0", port=PORT, threaded=True)
|