tsi-org commited on
Commit
32aeeee
·
verified ·
1 Parent(s): 37ce3cf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -16
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': '23',
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 imageio to write the video file
444
  try:
445
- writer = imageio.get_writer(temp_file, fps=fps, codec='h264', quality=9)
446
- for frame in frames:
447
- writer.append_data(frame)
448
- writer.close()
449
- return temp_file
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
  except Exception as e:
451
- print(f"Error saving video: {e}")
452
- return None
 
 
 
 
 
 
 
 
 
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],