jbilcke-hf HF Staff commited on
Commit
3cb8cb6
·
1 Parent(s): 9d13d0d

performance optimization

Browse files
Files changed (2) hide show
  1. app.py +13 -20
  2. example_pyav.md +91 -0
app.py CHANGED
@@ -39,8 +39,7 @@ from tqdm import tqdm
39
  import imageio
40
  import av
41
  import uuid
42
- import tempfile
43
- import shutil
44
  from pathlib import Path
45
  from typing import Dict, Any, List, Optional, Tuple, Union
46
 
@@ -171,7 +170,7 @@ if not APP_STATE["torch_compile_applied"] and ENABLE_TORCH_COMPILATION:
171
 
172
  def frames_to_mp4_base64(frames, fps = 15):
173
  """
174
- Convert frames directly to base64 data URI using PyAV.
175
 
176
  Args:
177
  frames: List of numpy arrays (HWC, RGB, uint8)
@@ -185,14 +184,12 @@ def frames_to_mp4_base64(frames, fps = 15):
185
 
186
  height, width = frames[0].shape[:2]
187
 
188
- # Create temporary file for MP4 encoding
189
- temp_file = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
190
- temp_filepath = temp_file.name
191
- temp_file.close()
192
 
193
  try:
194
- # Create container for MP4 format
195
- container = av.open(temp_filepath, mode='w', format='mp4')
196
 
197
  # Add video stream with fast settings
198
  stream = container.add_stream('h264', rate=fps)
@@ -222,18 +219,14 @@ def frames_to_mp4_base64(frames, fps = 15):
222
  finally:
223
  container.close()
224
 
225
- # Read the MP4 file and encode to base64
226
- with open(temp_filepath, 'rb') as f:
227
- video_data = f.read()
228
- base64_data = base64.b64encode(video_data).decode('utf-8')
229
- return f"data:video/mp4;base64,{base64_data}"
230
 
231
- finally:
232
- # Clean up temporary file
233
- if os.path.exists(temp_filepath):
234
- os.unlink(temp_filepath)
235
-
236
- return "data:video/mp4;base64,"
237
 
238
  # note: we set use_taehv to be able to use other resolutions
239
  # this might impact performance
 
39
  import imageio
40
  import av
41
  import uuid
42
+ import io
 
43
  from pathlib import Path
44
  from typing import Dict, Any, List, Optional, Tuple, Union
45
 
 
170
 
171
  def frames_to_mp4_base64(frames, fps = 15):
172
  """
173
+ Convert frames directly to base64 data URI using PyAV with in-memory file.
174
 
175
  Args:
176
  frames: List of numpy arrays (HWC, RGB, uint8)
 
184
 
185
  height, width = frames[0].shape[:2]
186
 
187
+ # Create BytesIO "in memory file"
188
+ output_memory_file = io.BytesIO()
 
 
189
 
190
  try:
191
+ # Create container for MP4 format using in-memory file
192
+ container = av.open(output_memory_file, mode='w', format='mp4')
193
 
194
  # Add video stream with fast settings
195
  stream = container.add_stream('h264', rate=fps)
 
219
  finally:
220
  container.close()
221
 
222
+ # Get video data from in-memory file and encode to base64
223
+ video_data = output_memory_file.getbuffer()
224
+ base64_data = base64.b64encode(video_data).decode('utf-8')
225
+ return f"data:video/mp4;base64,{base64_data}"
 
226
 
227
+ except Exception as e:
228
+ print(f"Error encoding video: {e}")
229
+ return "data:video/mp4;base64,"
 
 
 
230
 
231
  # note: we set use_taehv to be able to use other resolutions
232
  # this might impact performance
example_pyav.md ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ We may use [PyAV](https://github.com/PyAV-Org/PyAV) for encoding "in memory file".
2
+
3
+ PyAV is a Pythonic binding for the [FFmpeg](https://ffmpeg.org/) libraries.
4
+ The interface is relatively low level, but it allows us to do things that are not possible using other FFmpeg bindings.
5
+
6
+ Here are the main stages for creating MP4 in memory using PyAV:
7
+
8
+ * Create BytesIO "in memory file":
9
+
10
+ output_memory_file = io.BytesIO()
11
+
12
+
13
+ * Use PyAV to open "in memory file" as MP4 video output file:
14
+
15
+ output = av.open(output_memory_file, 'w', format="mp4")
16
+
17
+
18
+ * Add H.264 video stream to the MP4 container, and set codec parameters:
19
+
20
+ stream = output.add_stream('h264', str(fps))
21
+ stream.width = width
22
+ stream.height = height
23
+ stream.pix_fmt = 'yuv444p'
24
+ stream.options = {'crf': '17'}
25
+
26
+
27
+ * Iterate the OpenCV images, convert image to PyAV `VideoFrame`, encode, and "Mux":
28
+
29
+ for i in range(n_frmaes):
30
+ img = make_sample_image(i) # Create OpenCV image for testing (resolution 192x108, pixel format BGR).
31
+ frame = av.VideoFrame.from_ndarray(img, format='bgr24')
32
+ packet = stream.encode(frame)
33
+ output.mux(packet)
34
+
35
+
36
+ * Flush the encoder and close the "in memory" file:
37
+
38
+ packet = stream.encode(None)
39
+ output.mux(packet)
40
+ output.close()
41
+
42
+
43
+
44
+ * * *
45
+
46
+ The following code samples encode 100 synthetic images to "in memory" MP4 memory file.
47
+ Each synthetic image applies OpenCV image, with sequential blue frame number (used for testing).
48
+ At the end, the memory file is written to `output.mp4` file for testing.
49
+
50
+ import numpy as np
51
+ import cv2
52
+ import av
53
+ import io
54
+
55
+ n_frmaes = 100 # Select number of frames (for testing).
56
+ width, height, fps = 192, 108, 23.976 # Select video resolution and framerate.
57
+
58
+ output_memory_file = io.BytesIO() # Create BytesIO "in memory file".
59
+
60
+ output = av.open(output_memory_file, 'w', format="mp4") # Open "in memory file" as MP4 video output
61
+ stream = output.add_stream('h264', str(fps)) # Add H.264 video stream to the MP4 container, with framerate = fps.
62
+ stream.width = width # Set frame width
63
+ stream.height = height # Set frame height
64
+ stream.pix_fmt = 'yuv444p' # Select yuv444p pixel format (better quality than default yuv420p).
65
+ stream.options = {'crf': '17'} # Select low crf for high quality (the price is larger file size).
66
+
67
+
68
+ def make_sample_image(i):
69
+ """ Build synthetic "raw BGR" image for testing """
70
+ p = width//60
71
+ img = np.full((height, width, 3), 60, np.uint8)
72
+ cv2.putText(img, str(i+1), (width//2-p*10*len(str(i+1)), height//2+p*10), cv2.FONT_HERSHEY_DUPLEX, p, (255, 30, 30), p*2) # Blue number
73
+ return img
74
+
75
+
76
+ # Iterate the created images, encode and write to MP4 memory file.
77
+ for i in range(n_frmaes):
78
+ img = make_sample_image(i) # Create OpenCV image for testing (resolution 192x108, pixel format BGR).
79
+ frame = av.VideoFrame.from_ndarray(img, format='bgr24') # Convert image from NumPy Array to frame.
80
+ packet = stream.encode(frame) # Encode video frame
81
+ output.mux(packet) # "Mux" the encoded frame (add the encoded frame to MP4 file).
82
+
83
+ # Flush the encoder
84
+ packet = stream.encode(None)
85
+ output.mux(packet)
86
+
87
+ output.close()
88
+
89
+ # Write BytesIO from RAM to file, for testing
90
+ with open("output.mp4", "wb") as f:
91
+ f.write(output_memory_file.getbuffer())