multimodalart HF Staff commited on
Commit
c551a63
·
verified ·
1 Parent(s): ee11946

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -20
app.py CHANGED
@@ -1,32 +1,58 @@
 
1
  import numpy as np
2
  import gradio as gr
3
  import spaces
4
- from fastrtc import WebRTC, get_turn_credentials
 
5
 
6
- @spaces.GPU
7
- def flip_frame_handler(video_frame):
8
  """
9
- Flips the incoming video frame both vertically and horizontally.
10
- This function now uses 'return' because it processes one frame at a time.
 
 
 
 
11
  """
12
- if video_frame is not None:
13
- # A single call to np.flip with axis=(0, 1) flips both vertically and horizontally.
14
- return np.flip(video_frame, axis=(0, 1))
 
 
 
 
 
15
 
16
- # Return None if no frame is received to handle the case gracefully.
17
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  # --- Gradio UI Layout ---
20
  with gr.Blocks(theme=gr.themes.Soft(), title="FastRTC Webcam Double Flipper") as demo:
21
- gr.Markdown("# 🚀 FastRTC Webcam Double Flipper (Side-by-Side)")
22
  gr.Markdown(
23
- "*This demo takes your webcam feed from the left, flips it both horizontally and vertically on the server, and streams it back to the right panel in real-time.*"
24
  )
25
 
26
  with gr.Row():
27
  with gr.Column():
28
  gr.Markdown("### 1. Your Webcam Feed (Input)")
29
- # This component is configured only to send video to the server.
30
  webcam_input = WebRTC(
31
  label="Webcam Input",
32
  modality="video",
@@ -38,7 +64,6 @@ with gr.Blocks(theme=gr.themes.Soft(), title="FastRTC Webcam Double Flipper") as
38
 
39
  with gr.Column():
40
  gr.Markdown("### 2. Flipped Video (Output)")
41
- # This component is configured only to receive video from the server.
42
  video_output = WebRTC(
43
  label="Flipped Output Stream",
44
  modality="video",
@@ -47,13 +72,16 @@ with gr.Blocks(theme=gr.themes.Soft(), title="FastRTC Webcam Double Flipper") as
47
  height=480,
48
  rtc_configuration=get_turn_credentials(),
49
  )
50
-
51
- # The `stream` event is triggered by new frames from the `webcam_input` component.
 
 
 
52
  webcam_input.stream(
53
- fn=flip_frame_handler,
54
- inputs=[webcam_input], # The input to our function is the webcam feed.
55
- outputs=[video_output], # The output is sent to the separate 'receive' component.
56
- time_limit=60,
57
  )
58
 
59
  if __name__ == "__main__":
 
1
+ import asyncio
2
  import numpy as np
3
  import gradio as gr
4
  import spaces
5
+ from fastrtc import WebRTC, get_turn_credentials, AsyncStreamHandler
6
+ from fastrtc.utils import wait_for_item
7
 
8
+ class FrameFlipperHandler(AsyncStreamHandler):
 
9
  """
10
+ A persistent, reactive handler for flipping video frames.
11
+
12
+ This class creates a long-running process for each user connection.
13
+ - The `receive` method is called by the backend whenever a new frame arrives from the user.
14
+ - The `emit` method is called by the backend in a loop to get processed frames to send back.
15
+ - An internal `asyncio.Queue` connects these two methods for reactive, low-latency processing.
16
  """
17
+ def __init__(self):
18
+ super().__init__()
19
+ self.frame_queue = asyncio.Queue()
20
+
21
+ async def receive(self, frame: tuple[int, np.ndarray]) -> None:
22
+ """Called for each frame received from the client's webcam."""
23
+ # This is non-blocking and instantly puts the frame into our processing queue.
24
+ self.frame_queue.put_nowait(frame)
25
 
26
+ @spaces.GPU
27
+ async def emit(self):
28
+ """Called in a loop to get the next frame to send to the client."""
29
+ # This waits for a frame to be available in the queue with a small timeout.
30
+ frame_data = await wait_for_item(self.frame_queue, timeout=0.01)
31
+
32
+ if frame_data:
33
+ _rate, frame_array = frame_data
34
+ if frame_array is not None:
35
+ # Flip the frame both vertically and horizontally
36
+ flipped_frame = np.flip(frame_array, axis=(0, 1))
37
+ return (None, flipped_frame) # The sample rate is ignored for video
38
+
39
+ return None
40
+
41
+ def copy(self):
42
+ """Creates a new instance of this handler for each new connection."""
43
+ return FrameFlipperHandler()
44
+
45
 
46
  # --- Gradio UI Layout ---
47
  with gr.Blocks(theme=gr.themes.Soft(), title="FastRTC Webcam Double Flipper") as demo:
48
+ gr.Markdown("# 🚀 FastRTC Webcam Double Flipper (Reactive)")
49
  gr.Markdown(
50
+ "*This version uses a persistent `AsyncStreamHandler` for low-latency, reactive streaming, ideal for environments like ZeroGPU.*"
51
  )
52
 
53
  with gr.Row():
54
  with gr.Column():
55
  gr.Markdown("### 1. Your Webcam Feed (Input)")
 
56
  webcam_input = WebRTC(
57
  label="Webcam Input",
58
  modality="video",
 
64
 
65
  with gr.Column():
66
  gr.Markdown("### 2. Flipped Video (Output)")
 
67
  video_output = WebRTC(
68
  label="Flipped Output Stream",
69
  modality="video",
 
72
  height=480,
73
  rtc_configuration=get_turn_credentials(),
74
  )
75
+
76
+ # Instantiate our custom handler
77
+ handler = FrameFlipperHandler()
78
+
79
+ # Pass the handler instance to the stream method
80
  webcam_input.stream(
81
+ fn=handler,
82
+ inputs=[webcam_input],
83
+ outputs=[video_output],
84
+ time_limit=120,
85
  )
86
 
87
  if __name__ == "__main__":