multimodalart's picture
Update app.py
c551a63 verified
raw
history blame
3.21 kB
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()