Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
@@ -176,13 +176,18 @@ def frames_to_ts_file(frames, filepath, fps = 15):
|
|
176 |
stream.height = height
|
177 |
stream.pix_fmt = 'yuv420p'
|
178 |
|
179 |
-
# Optimize for low latency streaming
|
180 |
stream.options = {
|
181 |
-
'preset': 'ultrafast',
|
182 |
-
'tune': 'zerolatency',
|
183 |
-
'crf': '
|
184 |
-
'profile': 'baseline',
|
185 |
-
'level': '3.0'
|
|
|
|
|
|
|
|
|
|
|
186 |
}
|
187 |
|
188 |
try:
|
@@ -423,6 +428,7 @@ def video_generation_handler_streaming(prompt, seed=42, fps=15, save_frames=True
|
|
423 |
yield None, final_status_html
|
424 |
print(f" PyAV streaming complete! {total_frames_yielded} frames across {num_blocks} blocks")
|
425 |
|
|
|
426 |
def save_frames_as_video(frames, fps=15):
|
427 |
"""
|
428 |
Convert frames to a downloadable MP4 video file.
|
@@ -435,21 +441,66 @@ def save_frames_as_video(frames, fps=15):
|
|
435 |
Path to the saved video file
|
436 |
"""
|
437 |
if not frames:
|
|
|
438 |
return None
|
439 |
|
440 |
# Create a temporary file with a unique name
|
441 |
temp_file = os.path.join("gradio_tmp", f"download_{uuid.uuid4()}.mp4")
|
442 |
|
443 |
-
# Use
|
444 |
try:
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
450 |
except Exception as e:
|
451 |
-
|
452 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
453 |
|
454 |
# --- Gradio UI Layout ---
|
455 |
with gr.Blocks(title="Self-Forcing Streaming Demo") as demo:
|
@@ -506,7 +557,8 @@ with gr.Blocks(title="Self-Forcing Streaming Demo") as demo:
|
|
506 |
loop=True,
|
507 |
height=400,
|
508 |
autoplay=True,
|
509 |
-
show_label=False
|
|
|
510 |
)
|
511 |
|
512 |
status_display = gr.HTML(
|
@@ -559,9 +611,13 @@ with gr.Blocks(title="Self-Forcing Streaming Demo") as demo:
|
|
559 |
fn=download_video,
|
560 |
inputs=[fps],
|
561 |
outputs=[download_output],
|
562 |
-
show_progress=True
|
|
|
563 |
)
|
564 |
|
|
|
|
|
|
|
565 |
enhance_button.click(
|
566 |
fn=enhance_prompt,
|
567 |
inputs=[prompt],
|
|
|
176 |
stream.height = height
|
177 |
stream.pix_fmt = 'yuv420p'
|
178 |
|
179 |
+
# Optimize for low latency streaming with better buffering
|
180 |
stream.options = {
|
181 |
+
'preset': 'ultrafast', # Speed over quality for real-time
|
182 |
+
'tune': 'zerolatency', # Reduce latency
|
183 |
+
'crf': '28', # Slightly lower quality (higher number) for better throughput
|
184 |
+
'profile': 'baseline', # Simpler profile for better compatibility
|
185 |
+
'level': '3.0', # Compatibility level
|
186 |
+
'g': '15', # Keyframe interval matching fps for better seeking
|
187 |
+
'b:v': '2000k', # Target bitrate - reducing for smoother playback
|
188 |
+
'maxrate': '2500k', # Maximum bitrate
|
189 |
+
'bufsize': '5000k', # Larger buffer size
|
190 |
+
'sc_threshold': '0' # Disable scene detection for smoother streaming
|
191 |
}
|
192 |
|
193 |
try:
|
|
|
428 |
yield None, final_status_html
|
429 |
print(f" PyAV streaming complete! {total_frames_yielded} frames across {num_blocks} blocks")
|
430 |
|
431 |
+
# Function to save frames as downloadable video
|
432 |
def save_frames_as_video(frames, fps=15):
|
433 |
"""
|
434 |
Convert frames to a downloadable MP4 video file.
|
|
|
441 |
Path to the saved video file
|
442 |
"""
|
443 |
if not frames:
|
444 |
+
print("No frames available to save")
|
445 |
return None
|
446 |
|
447 |
# Create a temporary file with a unique name
|
448 |
temp_file = os.path.join("gradio_tmp", f"download_{uuid.uuid4()}.mp4")
|
449 |
|
450 |
+
# Use PyAV for better quality and reliability
|
451 |
try:
|
452 |
+
# First try PyAV which has better compatibility
|
453 |
+
container = av.open(temp_file, mode='w')
|
454 |
+
stream = container.add_stream('h264', rate=fps)
|
455 |
+
|
456 |
+
# Get dimensions from first frame
|
457 |
+
height, width = frames[0].shape[:2]
|
458 |
+
stream.width = width
|
459 |
+
stream.height = height
|
460 |
+
stream.pix_fmt = 'yuv420p'
|
461 |
+
|
462 |
+
# Use higher quality for downloads
|
463 |
+
stream.options = {
|
464 |
+
'preset': 'medium', # Better quality than ultrafast
|
465 |
+
'crf': '23', # Better quality than streaming
|
466 |
+
'profile': 'high', # Higher quality profile
|
467 |
+
'g': f'{fps*2}', # GOP size
|
468 |
+
'b:v': '4000k', # Higher bitrate for downloads
|
469 |
+
'refs': '3' # Number of reference frames
|
470 |
+
}
|
471 |
+
|
472 |
+
print(f"Saving video with {len(frames)} frames at {fps} FPS")
|
473 |
+
for frame_np in frames:
|
474 |
+
frame = av.VideoFrame.from_ndarray(frame_np, format='rgb24')
|
475 |
+
for packet in stream.encode(frame):
|
476 |
+
container.mux(packet)
|
477 |
+
|
478 |
+
# Flush the stream
|
479 |
+
for packet in stream.encode():
|
480 |
+
container.mux(packet)
|
481 |
+
|
482 |
+
container.close()
|
483 |
+
|
484 |
+
# Verify the file exists and has content
|
485 |
+
if os.path.exists(temp_file) and os.path.getsize(temp_file) > 0:
|
486 |
+
print(f"Video saved successfully: {temp_file} ({os.path.getsize(temp_file)} bytes)")
|
487 |
+
return temp_file
|
488 |
+
else:
|
489 |
+
print("Video file is empty or missing, falling back to imageio")
|
490 |
+
raise RuntimeError("Empty file created")
|
491 |
+
|
492 |
except Exception as e:
|
493 |
+
# Fall back to imageio if PyAV fails
|
494 |
+
print(f"PyAV encoding failed: {e}, falling back to imageio")
|
495 |
+
try:
|
496 |
+
writer = imageio.get_writer(temp_file, fps=fps, codec='h264', quality=9, bitrate='4000k')
|
497 |
+
for frame in frames:
|
498 |
+
writer.append_data(frame)
|
499 |
+
writer.close()
|
500 |
+
return temp_file
|
501 |
+
except Exception as e2:
|
502 |
+
print(f"Error saving video with imageio: {e2}")
|
503 |
+
return None
|
504 |
|
505 |
# --- Gradio UI Layout ---
|
506 |
with gr.Blocks(title="Self-Forcing Streaming Demo") as demo:
|
|
|
557 |
loop=True,
|
558 |
height=400,
|
559 |
autoplay=True,
|
560 |
+
show_label=False,
|
561 |
+
format="mp4" # Use more stable mp4 format when possible
|
562 |
)
|
563 |
|
564 |
status_display = gr.HTML(
|
|
|
611 |
fn=download_video,
|
612 |
inputs=[fps],
|
613 |
outputs=[download_output],
|
614 |
+
show_progress=True,
|
615 |
+
api_name="download_video" # Make it accessible via API
|
616 |
)
|
617 |
|
618 |
+
# Make the FPS slider visible for download quality control
|
619 |
+
fps.visible = True
|
620 |
+
|
621 |
enhance_button.click(
|
622 |
fn=enhance_prompt,
|
623 |
inputs=[prompt],
|