From 9e133d71a2ae3d1ad0ef3040b606438e2d9e071c Mon Sep 17 00:00:00 2001 From: puke Date: Mon, 27 Oct 2025 10:57:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=9F=B3=E7=94=BB=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reelforge/services/video.py | 11 ++++++++--- reelforge/services/video/compositor.py | 10 ++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/reelforge/services/video.py b/reelforge/services/video.py index 34a5a53..9896458 100644 --- a/reelforge/services/video.py +++ b/reelforge/services/video.py @@ -345,33 +345,38 @@ class VideoService: logger.info("Creating video from image and audio") try: + # Get audio duration to ensure exact video duration match + probe = ffmpeg.probe(audio) + audio_duration = float(probe['format']['duration']) + logger.debug(f"Audio duration: {audio_duration:.3f}s") + # Input image with loop (loop=1 means loop indefinitely) # Use framerate to set input framerate input_image = ffmpeg.input(image, loop=1, framerate=fps) input_audio = ffmpeg.input(audio) # Combine image and audio - # Use -shortest to match audio duration + # Use -t to explicitly set video duration = audio duration ( ffmpeg .output( input_image, input_audio, output, + t=audio_duration, # Force video duration to match audio exactly vcodec='libx264', acodec='aac', pix_fmt='yuv420p', audio_bitrate='192k', preset='medium', crf=23, - shortest=None, # Duration = shortest input (audio) **{'b:v': '2M'} # Video bitrate ) .overwrite_output() .run(capture_stdout=True, capture_stderr=True) ) - logger.success(f"Video created from image: {output}") + logger.success(f"Video created from image: {output} (duration: {audio_duration:.3f}s)") return output except ffmpeg.Error as e: error_msg = e.stderr.decode() if e.stderr else str(e) diff --git a/reelforge/services/video/compositor.py b/reelforge/services/video/compositor.py index ac3e1c8..d09a1e0 100644 --- a/reelforge/services/video/compositor.py +++ b/reelforge/services/video/compositor.py @@ -286,29 +286,35 @@ class VideoCompositor: logger.info("Creating video from image and audio") try: + # Get audio duration to ensure exact video duration match + probe = ffmpeg.probe(audio) + audio_duration = float(probe['format']['duration']) + logger.debug(f"Audio duration: {audio_duration:.3f}s") + # Input image with loop input_image = ffmpeg.input(image, loop=1, framerate=fps) input_audio = ffmpeg.input(audio) # Combine image and audio + # Use -t to explicitly set video duration = audio duration ( ffmpeg .output( input_image.video, input_audio.audio, output, + t=audio_duration, # Force video duration to match audio exactly vcodec='libx264', acodec='aac', pix_fmt='yuv420p', audio_bitrate='192k', - shortest=None, # Duration = audio duration **{'b:v': '2M'} # Video bitrate ) .overwrite_output() .run(capture_stdout=True, capture_stderr=True) ) - logger.success(f"Video created from image: {output}") + logger.success(f"Video created from image: {output} (duration: {audio_duration:.3f}s)") return output except ffmpeg.Error as e: error_msg = e.stderr.decode() if e.stderr else str(e)