diff --git a/src/Autoload/Export.gd b/src/Autoload/Export.gd index 5a55d831a..15e7293a6 100644 --- a/src/Autoload/Export.gd +++ b/src/Autoload/Export.gd @@ -427,7 +427,7 @@ func export_processed_images( if is_single_file_format(project): if is_using_ffmpeg(project.file_format): - var video_exported := export_video(export_paths) + var video_exported := export_video(export_paths, project) if not video_exported: Global.popup_error( tr("Video failed to export. Ensure that FFMPEG is installed correctly.") @@ -505,8 +505,9 @@ func export_processed_images( ## Uses FFMPEG to export a video -func export_video(export_paths: PackedStringArray) -> bool: +func export_video(export_paths: PackedStringArray, project: Project) -> bool: DirAccess.make_dir_absolute(TEMP_PATH) + var video_duration := 0 var temp_path_real := ProjectSettings.globalize_path(TEMP_PATH) var input_file_path := temp_path_real.path_join("input.txt") var input_file := FileAccess.open(input_file_path, FileAccess.WRITE) @@ -516,25 +517,65 @@ func export_video(export_paths: PackedStringArray) -> bool: processed_images[i].image.save_png(temp_file_path) input_file.store_line("file '" + temp_file_name + "'") input_file.store_line("duration %s" % processed_images[i].duration) + video_duration += processed_images[i].duration input_file.close() + + # ffmpeg -y -f concat -i input.txt output_path var ffmpeg_execute: PackedStringArray = [ "-y", "-f", "concat", "-i", input_file_path, export_paths[0] ] - var output := [] - var success := OS.execute(Global.ffmpeg_path, ffmpeg_execute, output, true) - print(output) - var temp_dir := DirAccess.open(TEMP_PATH) - for file in temp_dir.get_files(): - temp_dir.remove(file) - DirAccess.remove_absolute(TEMP_PATH) + var success := OS.execute(Global.ffmpeg_path, ffmpeg_execute, [], true) if success < 0 or success > 1: var fail_text := """Video failed to export. Make sure you have FFMPEG installed and have set the correct path in the preferences.""" Global.popup_error(tr(fail_text)) + _clear_temp_folder() return false + # ffmpeg -i input1 -i input2 ... -i inputn -filter_complex amix=inputs=n output_path + var ffmpeg_combine_audio: PackedStringArray = ["-y"] + var audio_layer_count := 0 + var max_audio_duration := 0 + for layer in project.layers: + if layer is AudioLayer and layer.audio is AudioStreamMP3: + var temp_file_name := str(audio_layer_count + 1).pad_zeros(number_of_digits) + ".mp3" + var temp_file_path := temp_path_real.path_join(temp_file_name) + var temp_audio_file := FileAccess.open(temp_file_path, FileAccess.WRITE) + temp_audio_file.store_buffer(layer.audio.data) + audio_layer_count += 1 + ffmpeg_combine_audio.append("-i") + ffmpeg_combine_audio.append(temp_file_path) + if layer.audio.get_length() >= max_audio_duration: + max_audio_duration = layer.audio.get_length() + if audio_layer_count > 0: + var amix_inputs_string := "amix=inputs=%s" % audio_layer_count + var audio_file_path := temp_path_real.path_join("audio.mp3") + ffmpeg_combine_audio.append_array( + PackedStringArray(["-filter_complex", amix_inputs_string, audio_file_path]) + ) + OS.execute(Global.ffmpeg_path, ffmpeg_combine_audio, [], true) + var copied_video := temp_path_real.path_join("video." + export_paths[0].get_extension()) + DirAccess.copy_absolute(export_paths[0], copied_video) + # ffmpeg -y -i video_file -i input_audio -c:v copy -map 0:v:0 -map 1:a:0 video_file + var ffmpeg_final_video: PackedStringArray = [ + "-y", "-i", copied_video, "-i", audio_file_path + ] + if max_audio_duration > video_duration: + ffmpeg_final_video.append("-shortest") + ffmpeg_final_video.append_array( + ["-c:v", "copy", "-map", "0:v:0", "-map", "1:a:0", export_paths[0]] + ) + OS.execute(Global.ffmpeg_path, ffmpeg_final_video, [], true) + _clear_temp_folder() return true +func _clear_temp_folder() -> void: + var temp_dir := DirAccess.open(TEMP_PATH) + for file in temp_dir.get_files(): + temp_dir.remove(file) + DirAccess.remove_absolute(TEMP_PATH) + + func export_animated(args: Dictionary) -> void: var project: Project = args["project"] var exporter: AImgIOBaseExporter = args["exporter"]