Spaces:
Running
on
Zero
Running
on
Zero
File size: 3,214 Bytes
c551a63 6f9faf9 379cab9 c551a63 6f9faf9 c551a63 7e0b27a c551a63 7e0b27a c551a63 ee11946 c551a63 6f9faf9 ee11946 c551a63 ee11946 c551a63 ee11946 6f9faf9 7e0b27a ee11946 7e0b27a 6f9faf9 7e0b27a 6f9faf9 ee11946 bd6580c 6f9faf9 c551a63 7e0b27a c551a63 6f9faf9 7e0b27a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
import asyncio
import numpy as np
import gradio as gr
import spaces
from fastrtc import WebRTC, get_turn_credentials, AsyncStreamHandler
from fastrtc.utils import wait_for_item
class FrameFlipperHandler(AsyncStreamHandler):
"""
A persistent, reactive handler for flipping video frames.
This class creates a long-running process for each user connection.
- The `receive` method is called by the backend whenever a new frame arrives from the user.
- The `emit` method is called by the backend in a loop to get processed frames to send back.
- An internal `asyncio.Queue` connects these two methods for reactive, low-latency processing.
"""
def __init__(self):
super().__init__()
self.frame_queue = asyncio.Queue()
async def receive(self, frame: tuple[int, np.ndarray]) -> None:
"""Called for each frame received from the client's webcam."""
# This is non-blocking and instantly puts the frame into our processing queue.
self.frame_queue.put_nowait(frame)
@spaces.GPU
async def emit(self):
"""Called in a loop to get the next frame to send to the client."""
# This waits for a frame to be available in the queue with a small timeout.
frame_data = await wait_for_item(self.frame_queue, timeout=0.01)
if frame_data:
_rate, frame_array = frame_data
if frame_array is not None:
# Flip the frame both vertically and horizontally
flipped_frame = np.flip(frame_array, axis=(0, 1))
return (None, flipped_frame) # The sample rate is ignored for video
return None
def copy(self):
"""Creates a new instance of this handler for each new connection."""
return FrameFlipperHandler()
# --- Gradio UI Layout ---
with gr.Blocks(theme=gr.themes.Soft(), title="FastRTC Webcam Double Flipper") as demo:
gr.Markdown("# 🚀 FastRTC Webcam Double Flipper (Reactive)")
gr.Markdown(
"*This version uses a persistent `AsyncStreamHandler` for low-latency, reactive streaming, ideal for environments like ZeroGPU.*"
)
with gr.Row():
with gr.Column():
gr.Markdown("### 1. Your Webcam Feed (Input)")
webcam_input = WebRTC(
label="Webcam Input",
modality="video",
mode="send",
width=640,
height=480,
rtc_configuration=get_turn_credentials(),
)
with gr.Column():
gr.Markdown("### 2. Flipped Video (Output)")
video_output = WebRTC(
label="Flipped Output Stream",
modality="video",
mode="receive",
width=640,
height=480,
rtc_configuration=get_turn_credentials(),
)
# Instantiate our custom handler
handler = FrameFlipperHandler()
# Pass the handler instance to the stream method
webcam_input.stream(
fn=handler,
inputs=[webcam_input],
outputs=[video_output],
time_limit=120,
)
if __name__ == "__main__":
demo.queue().launch() |