Feat/ffmpeg with progress (#827)

* FFmpeg with progress bar

* Fix typing

* FFmpeg with progress bar part2

* Restore streaming wording
This commit is contained in:
Henry Ruhs 2024-12-06 11:33:37 +01:00 committed by GitHub
parent 70f0c708e1
commit 0c0062db72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 10 deletions

View File

@ -5,17 +5,42 @@ import tempfile
from typing import List, Optional
import filetype
from tqdm import tqdm
from facefusion import logger, process_manager, state_manager
from facefusion import logger, process_manager, state_manager, wording
from facefusion.filesystem import remove_file
from facefusion.temp_helper import get_temp_file_path, get_temp_frames_pattern
from facefusion.typing import AudioBuffer, Fps, OutputVideoPreset
from facefusion.vision import detect_video_duration, restrict_video_fps
from facefusion.temp_helper import get_temp_file_path, get_temp_frame_paths, get_temp_frames_pattern
from facefusion.typing import AudioBuffer, Fps, OutputVideoPreset, UpdateProgress
from facefusion.vision import count_video_frame_total, detect_video_duration, restrict_video_fps
def run_ffmpeg_with_progress(args: List[str], update_progress : UpdateProgress) -> subprocess.Popen[bytes]:
log_level = state_manager.get_item('log_level')
commands = [ shutil.which('ffmpeg'), '-hide_banner', '-nostats', '-loglevel', 'error', '-progress', '-' ]
commands.extend(args)
process = subprocess.Popen(commands, stderr = subprocess.PIPE, stdout = subprocess.PIPE)
while process_manager.is_processing():
try:
while line := process.stdout.readline().decode():
if 'frame=' in line:
_, frame_number = line.split('frame=')
update_progress(int(frame_number))
if log_level == 'debug':
log_debug(process)
process.wait(timeout = 0.5)
except subprocess.TimeoutExpired:
continue
return process
if process_manager.is_stopping():
process.terminate()
return process
def run_ffmpeg(args : List[str]) -> subprocess.Popen[bytes]:
log_level = state_manager.get_item('log_level')
commands = [ shutil.which('ffmpeg'), '-hide_banner', '-loglevel', 'error' ]
commands = [ shutil.which('ffmpeg'), '-hide_banner', '-nostats', '-loglevel', 'error' ]
commands.extend(args)
process = subprocess.Popen(commands, stderr = subprocess.PIPE, stdout = subprocess.PIPE)
@ -34,7 +59,7 @@ def run_ffmpeg(args : List[str]) -> subprocess.Popen[bytes]:
def open_ffmpeg(args : List[str]) -> subprocess.Popen[bytes]:
commands = [ shutil.which('ffmpeg'), '-hide_banner', '-loglevel', 'quiet' ]
commands = [ shutil.which('ffmpeg'), '-loglevel', 'quiet' ]
commands.extend(args)
return subprocess.Popen(commands, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
@ -49,6 +74,7 @@ def log_debug(process : subprocess.Popen[bytes]) -> None:
def extract_frames(target_path : str, temp_video_resolution : str, temp_video_fps : Fps) -> bool:
extract_frame_total = count_video_frame_total(state_manager.get_item('target_path'))
trim_frame_start = state_manager.get_item('trim_frame_start')
trim_frame_end = state_manager.get_item('trim_frame_end')
temp_frames_pattern = get_temp_frames_pattern(target_path, '%08d')
@ -56,17 +82,24 @@ def extract_frames(target_path : str, temp_video_resolution : str, temp_video_fp
if isinstance(trim_frame_start, int) and isinstance(trim_frame_end, int):
commands.extend([ '-vf', 'trim=start_frame=' + str(trim_frame_start) + ':end_frame=' + str(trim_frame_end) + ',fps=' + str(temp_video_fps) ])
extract_frame_total = trim_frame_end - trim_frame_start
elif isinstance(trim_frame_start, int):
commands.extend([ '-vf', 'trim=start_frame=' + str(trim_frame_start) + ',fps=' + str(temp_video_fps) ])
extract_frame_total -= trim_frame_start
elif isinstance(trim_frame_end, int):
commands.extend([ '-vf', 'trim=end_frame=' + str(trim_frame_end) + ',fps=' + str(temp_video_fps) ])
extract_frame_total -= trim_frame_end
else:
commands.extend([ '-vf', 'fps=' + str(temp_video_fps) ])
commands.extend([ '-vsync', '0', temp_frames_pattern ])
return run_ffmpeg(commands).returncode == 0
with tqdm(total = extract_frame_total, desc = wording.get('extracting'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
process = run_ffmpeg_with_progress(commands, lambda frame_number: progress.update(frame_number - progress.n))
return process.returncode == 0
def merge_video(target_path : str, output_video_resolution : str, output_video_fps : Fps) -> bool:
def merge_video(target_path : str, output_video_resolution : str, output_video_fps: Fps) -> bool:
merge_frame_total = len(get_temp_frame_paths(target_path))
output_video_encoder = state_manager.get_item('output_video_encoder')
output_video_quality = state_manager.get_item('output_video_quality')
output_video_preset = state_manager.get_item('output_video_preset')
@ -93,7 +126,10 @@ def merge_video(target_path : str, output_video_resolution : str, output_video_f
if output_video_encoder in [ 'h264_videotoolbox', 'hevc_videotoolbox' ]:
commands.extend([ '-q:v', str(output_video_quality) ])
commands.extend([ '-vf', 'framerate=fps=' + str(output_video_fps), '-pix_fmt', 'yuv420p', '-colorspace', 'bt709', '-y', temp_file_path ])
return run_ffmpeg(commands).returncode == 0
with tqdm(total = merge_frame_total, desc = wording.get('merging'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress:
process = run_ffmpeg_with_progress(commands, lambda frame_number: progress.update(frame_number - progress.n))
return process.returncode == 0
def concat_video(output_path : str, temp_output_paths : List[str]) -> bool:

View File

@ -11,8 +11,10 @@ WORDING : Dict[str, Any] =\
'extracting_frames_succeed': 'Extracting frames succeed',
'extracting_frames_failed': 'Extracting frames failed',
'analysing': 'Analysing',
'processing': 'Processing',
'extracting': 'Extracting',
'streaming': 'Streaming',
'processing': 'Processing',
'merging': 'Merging',
'downloading': 'Downloading',
'temp_frames_not_found': 'Temporary frames not found',
'copying_image': 'Copying image with a resolution of {resolution}',