Spaces:
Runtime error
Runtime error
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],
|