Update app.py
Browse files
app.py
CHANGED
@@ -67,12 +67,13 @@ def export_to_video_file(video_frames, output_video_path=None, fps=10):
|
|
67 |
# those are way too slow for a AiTube which needs things to be as fast as possible
|
68 |
# -----------------------------------------------------------------------------------
|
69 |
|
70 |
-
def interpolate_video_frames(
|
71 |
"""
|
72 |
Interpolates frames in a video file to adjust frame rate and duration using ffmpeg's minterpolate.
|
73 |
|
74 |
Parameters:
|
75 |
-
|
|
|
76 |
output_fps (int): Target frames per second for the output video.
|
77 |
desired_duration (int): Desired duration of the video in seconds.
|
78 |
|
@@ -80,7 +81,7 @@ def interpolate_video_frames(file_path, output_fps=10, desired_duration=2):
|
|
80 |
str: The file path of the modified video.
|
81 |
"""
|
82 |
# Calculate the input fps required to stretch the video to the desired duration
|
83 |
-
input_fps = find_input_fps(
|
84 |
|
85 |
# Construct the ffmpeg command for interpolation
|
86 |
cmd = [
|
@@ -88,19 +89,18 @@ def interpolate_video_frames(file_path, output_fps=10, desired_duration=2):
|
|
88 |
'-i', file_path, # input file
|
89 |
'-filter:v', f'minterpolate=fps={output_fps}', # minterpolate filter options
|
90 |
'-r', str(output_fps), # output frame rate
|
91 |
-
|
92 |
-
file_path # Output file (Overwrites the original)
|
93 |
]
|
94 |
|
95 |
# Execute the command
|
96 |
try:
|
97 |
subprocess.run(cmd, check=True)
|
98 |
print("Video interpolation successful.")
|
|
|
99 |
except subprocess.CalledProcessError as e:
|
100 |
print("Failed to interpolate video. Error:", e)
|
|
|
101 |
|
102 |
-
return file_path
|
103 |
-
|
104 |
def find_input_fps(file_path, desired_duration):
|
105 |
"""
|
106 |
Determine the input fps that, when stretched to the desired duration, matches the original video length.
|
@@ -168,28 +168,41 @@ def generate_image(secret_token, prompt, base, width, height, motion, step, desi
|
|
168 |
guidance_scale=1.0,
|
169 |
num_inference_steps=step,
|
170 |
)
|
171 |
-
|
172 |
-
name = str(uuid.uuid4()).replace("-", "")
|
173 |
-
path = f"/tmp/{name}.webm"
|
174 |
|
175 |
-
|
176 |
-
|
177 |
-
|
|
|
178 |
|
179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
if desired_duration != 2 or desired_fps != 10:
|
181 |
-
|
182 |
|
183 |
# Read the content of the video file and encode it to base64
|
184 |
-
with open(
|
185 |
video_base64 = base64.b64encode(video_file.read()).decode('utf-8')
|
186 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
# Prepend the appropriate data URI header with MIME type
|
188 |
video_data_uri = 'data:video/webm;base64,' + video_base64
|
189 |
-
|
190 |
-
# clean-up (otherwise there is always a risk of "ghosting", eg. someone seeing the previous generated video",
|
191 |
-
# of one of the steps go wrong)
|
192 |
-
os.remove(path)
|
193 |
|
194 |
return video_data_uri
|
195 |
|
|
|
67 |
# those are way too slow for a AiTube which needs things to be as fast as possible
|
68 |
# -----------------------------------------------------------------------------------
|
69 |
|
70 |
+
def interpolate_video_frames(input_file_path, output_file_path, output_fps=10, desired_duration=2):
|
71 |
"""
|
72 |
Interpolates frames in a video file to adjust frame rate and duration using ffmpeg's minterpolate.
|
73 |
|
74 |
Parameters:
|
75 |
+
input_file_path (str): Path to the input video file.
|
76 |
+
output_file_path (str): Path to the output video file.
|
77 |
output_fps (int): Target frames per second for the output video.
|
78 |
desired_duration (int): Desired duration of the video in seconds.
|
79 |
|
|
|
81 |
str: The file path of the modified video.
|
82 |
"""
|
83 |
# Calculate the input fps required to stretch the video to the desired duration
|
84 |
+
input_fps = find_input_fps(input_file_path, desired_duration)
|
85 |
|
86 |
# Construct the ffmpeg command for interpolation
|
87 |
cmd = [
|
|
|
89 |
'-i', file_path, # input file
|
90 |
'-filter:v', f'minterpolate=fps={output_fps}', # minterpolate filter options
|
91 |
'-r', str(output_fps), # output frame rate
|
92 |
+
output_file_path # Output file
|
|
|
93 |
]
|
94 |
|
95 |
# Execute the command
|
96 |
try:
|
97 |
subprocess.run(cmd, check=True)
|
98 |
print("Video interpolation successful.")
|
99 |
+
return input_file_path
|
100 |
except subprocess.CalledProcessError as e:
|
101 |
print("Failed to interpolate video. Error:", e)
|
102 |
+
return output_file_path
|
103 |
|
|
|
|
|
104 |
def find_input_fps(file_path, desired_duration):
|
105 |
"""
|
106 |
Determine the input fps that, when stretched to the desired duration, matches the original video length.
|
|
|
168 |
guidance_scale=1.0,
|
169 |
num_inference_steps=step,
|
170 |
)
|
|
|
|
|
|
|
171 |
|
172 |
+
video_uuid = str(uuid.uuid4()).replace("-", "")
|
173 |
+
raw_video_path = f"/tmp/{video_uuid}_raw.webm"
|
174 |
+
enhanced_video_path = f"/tmp/{video_uuid}_enhanced.webm"
|
175 |
+
|
176 |
|
177 |
+
# note the fps is hardcoded, this is a limitation from AnimateDiff I think?
|
178 |
+
# (could we change this?)
|
179 |
+
#
|
180 |
+
# maybe to make things faster, we could *not* encode the video (as this uses files and external processes, which can be slow)
|
181 |
+
# and instead return the unencoded frames to the frontend renderer?
|
182 |
+
raw_video_path = export_to_video_file(output.frames[0], path, fps=10)
|
183 |
+
|
184 |
+
final_video_path = raw_video_path
|
185 |
+
|
186 |
+
# Optional frame interpolation
|
187 |
if desired_duration != 2 or desired_fps != 10:
|
188 |
+
final_video_path = interpolate_video_frames(raw_video_path, enhanced_video_path, output_fps=desired_fps, desired_duration=desired_duration)
|
189 |
|
190 |
# Read the content of the video file and encode it to base64
|
191 |
+
with open(final_video_path, "rb") as video_file:
|
192 |
video_base64 = base64.b64encode(video_file.read()).decode('utf-8')
|
193 |
|
194 |
+
# clean-up (otherwise there is always a risk of "ghosting", eg. someone seeing the previous generated video,
|
195 |
+
# of one of the steps go wrong - also we need to absolutely delete videos as we generate random files,
|
196 |
+
# we can't afford to get a "tmp disk full" error)
|
197 |
+
try:
|
198 |
+
os.remove(raw_video_path):
|
199 |
+
if final_video_path != raw_video_path:
|
200 |
+
os.remove(final_video_path)
|
201 |
+
except Exception as e:
|
202 |
+
print("Failed to delete a video path:", e)
|
203 |
+
|
204 |
# Prepend the appropriate data URI header with MIME type
|
205 |
video_data_uri = 'data:video/webm;base64,' + video_base64
|
|
|
|
|
|
|
|
|
206 |
|
207 |
return video_data_uri
|
208 |
|