jukrapopk commited on
Commit
6a298df
·
1 Parent(s): 84f1606

feature: implement session management

Browse files
Files changed (1) hide show
  1. 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
- measured_fps = 0
15
- show_fps = True
16
-
17
- def video_reader():
18
- global latest_frame, lock, measured_fps
19
-
20
- cap = cv2.VideoCapture("videos/classroom.mp4")
21
- framerate = cap.get(cv2.CAP_PROP_FPS)
22
- frame_duration = 1 / framerate
23
-
24
- # variables to measure FPS
25
- frame_count = 0
26
- last_time = time.time()
27
- rolling_duration = 0.5
28
-
29
- # variables to control framerate
30
- next_frame_time = time.time()
31
-
32
- while True:
33
- ret, frame = cap.read()
34
-
35
- if not ret:
36
- cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
37
- continue
38
- else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  with lock:
40
- if show_fps:
41
- cv2.putText(frame, f"{measured_fps: .0f}", (frame.shape[1] - 70, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)
42
- latest_frame = frame
43
-
44
- # measure FPS
45
- frame_count += 1
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 instantiated
75
 
76
- if not instantiated:
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
- @app.route("/toggle_fps")
83
- def toggle_fps():
84
- global show_fps
85
- show_fps = not show_fps
86
- return f"fps counter: {"on" if show_fps else "off"}", 200
 
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)