From 8a12f987623ad8fbfcdb0b79319043f882877c15 Mon Sep 17 00:00:00 2001 From: henryruhs Date: Sat, 25 Jul 2026 16:10:55 +0200 Subject: [PATCH] multi reader approach --- facefusion/content_analyser.py | 10 +++++---- facefusion/core.py | 2 +- facefusion/video_manager.py | 34 ++++++++++++----------------- facefusion/vision.py | 6 +++--- tests/test_video_manager.py | 39 ++++++++++------------------------ 5 files changed, 34 insertions(+), 57 deletions(-) diff --git a/facefusion/content_analyser.py b/facefusion/content_analyser.py index cf21f06f..a53a291f 100644 --- a/facefusion/content_analyser.py +++ b/facefusion/content_analyser.py @@ -4,12 +4,12 @@ from typing import Tuple import numpy from tqdm import tqdm -from facefusion import inference_manager, state_manager, translator +from facefusion import inference_manager, state_manager, translator, video_manager from facefusion.download import conditional_download_hashes, conditional_download_sources, resolve_download_url from facefusion.filesystem import resolve_relative_path from facefusion.thread_helper import conditional_thread_semaphore from facefusion.types import Detection, DownloadScope, DownloadSet, Fps, InferencePool, ModelSet, VisionFrame -from facefusion.vision import detect_video_fps, fit_contain_frame, read_image, read_video_frame +from facefusion.vision import detect_video_fps, fit_contain_frame, read_image STREAM_COUNTER = 0 @@ -161,13 +161,15 @@ def analyse_video(video_path : str, trim_frame_start : int, trim_frame_end : int rate = 0.0 total = 0 counter = 0 + video_reader = video_manager.get_reader(video_path, 'analyse_video') + video_manager.seek_video_reader(video_reader, trim_frame_start) with tqdm(total = len(frame_range), desc = translator.get('analysing'), unit = 'frame', ascii = ' =', disable = state_manager.get_item('log_level') in [ 'warn', 'error' ]) as progress: for frame_number in frame_range: - if frame_number % int(video_fps) == 0: - vision_frame = read_video_frame(video_path, frame_number) + vision_frame = video_manager.read_video_frame(video_reader) + if frame_number % int(video_fps) == 0: if numpy.any(vision_frame): total += 1 diff --git a/facefusion/core.py b/facefusion/core.py index 058eb86f..ec55a4fe 100755 --- a/facefusion/core.py +++ b/facefusion/core.py @@ -100,7 +100,7 @@ def pre_check() -> bool: def common_pre_check() -> bool: content_analyser_content = inspect.getsource(content_analyser).encode() - return hash_helper.create_hash(content_analyser_content) == '975d67d6' + return hash_helper.create_hash(content_analyser_content) == '5f04e67c' def processors_pre_check() -> bool: diff --git a/facefusion/video_manager.py b/facefusion/video_manager.py index 6a41b05e..8e144961 100644 --- a/facefusion/video_manager.py +++ b/facefusion/video_manager.py @@ -1,3 +1,4 @@ +import hashlib import uuid from typing import Optional @@ -14,40 +15,31 @@ VIDEO_POOL_SET : VideoPoolSet =\ } -def get_reader(video_path : str) -> VideoReader: - if video_path not in VIDEO_POOL_SET.get('reader'): +def get_reader(video_path : str, context : str) -> VideoReader: + reader_id = hashlib.sha1((video_path + '_' + context).encode()).hexdigest() + + if reader_id not in VIDEO_POOL_SET.get('reader'): video_metadata = ffprobe.extract_static_video_metadata(video_path) - VIDEO_POOL_SET['reader'][video_path] =\ + VIDEO_POOL_SET['reader'][reader_id] =\ { - 'id': uuid.uuid4().hex, + 'id': reader_id, 'file_path': video_path, 'process': ffmpeg.create_video_reader(video_path, 0, video_metadata), 'metadata': video_metadata, 'frame_number': 0 } - return VIDEO_POOL_SET.get('reader').get(video_path) - - -def conditional_seek_video_reader(video_reader : VideoReader, frame_number : int = 0) -> None: - frame_number = min(video_reader.get('metadata').get('frame_total'), frame_number) - skip_total = frame_number - video_reader.get('frame_number') - skip_margin = 128 - - if 0 < skip_total <= skip_margin: - for _ in range(skip_total): - read_video_frame(video_reader) - - if not video_reader.get('frame_number') == frame_number: - seek_video_reader(video_reader, frame_number) + return VIDEO_POOL_SET.get('reader').get(reader_id) def seek_video_reader(video_reader : VideoReader, frame_number : int = 0) -> None: - close_video_reader(video_reader) + frame_number = min(video_reader.get('metadata').get('frame_total'), frame_number) - video_reader['process'] = ffmpeg.create_video_reader(video_reader.get('file_path'), frame_number, video_reader.get('metadata')) - video_reader['frame_number'] = frame_number + if not video_reader.get('frame_number') == frame_number: + close_video_reader(video_reader) + video_reader['process'] = ffmpeg.create_video_reader(video_reader.get('file_path'), frame_number, video_reader.get('metadata')) + video_reader['frame_number'] = frame_number #todo: needs review - [decoding] [critical: high] partial pipe read returns none and desyncs position from the actual stream diff --git a/facefusion/vision.py b/facefusion/vision.py index 8cdb134c..af93c843 100644 --- a/facefusion/vision.py +++ b/facefusion/vision.py @@ -77,10 +77,10 @@ def read_static_video_frame(video_path : str, frame_number : int = 0) -> Optiona def read_video_frame(video_path : str, frame_number : int = 0) -> Optional[VisionFrame]: if is_video(video_path): - video_reader = video_manager.get_reader(video_path) + video_reader = video_manager.get_reader(video_path, 'read_video_frame') with thread_semaphore(): - video_manager.conditional_seek_video_reader(video_reader, frame_number) + video_manager.seek_video_reader(video_reader, frame_number) return video_manager.read_video_frame(video_reader) return None @@ -93,7 +93,7 @@ def select_video_frames(video_path : str, frame_number : int = 0, frame_offset : if is_video(video_path): with thread_lock(): - video_reader = video_manager.get_reader(video_path) + video_reader = video_manager.get_reader(video_path, 'select_video_frames') frame_set = video_manager.read_video_frames(video_reader, max(frame_start, 0), frame_end) for frame_number in range(frame_start, frame_end + 1): diff --git a/tests/test_video_manager.py b/tests/test_video_manager.py index e133f422..79819186 100644 --- a/tests/test_video_manager.py +++ b/tests/test_video_manager.py @@ -9,7 +9,7 @@ from facefusion.download import conditional_download from facefusion.ffprobe import extract_video_metadata from facefusion.frame_store import get_frame_store from facefusion.temp_helper import create_temp_directory, get_temp_file_path -from facefusion.video_manager import clear_video_pool, close_video_writer, conditional_seek_video_reader, get_reader, get_writer, read_video_frame, read_video_frames, seek_video_reader, write_video_frame +from facefusion.video_manager import clear_video_pool, close_video_writer, get_reader, get_writer, read_video_frame, read_video_frames, seek_video_reader, write_video_frame from .helper import get_test_example_file, get_test_examples_directory @@ -46,38 +46,21 @@ def before_each() -> None: #todo: needs review - [testing] question if the assertions are good #todo: run mutation testing, strip down to the minimum, test with real data def test_get_reader() -> None: - video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4')) + video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame') video_metadata = video_reader.get('metadata') assert video_metadata.get('resolution') == (426, 226) assert video_metadata.get('fps') == 25.0 assert video_metadata.get('frame_total') == 270 assert video_reader.get('frame_number') == 0 - assert get_reader(get_test_example_file('target-240p-25fps.mp4')) is video_reader - - -#todo: needs review - [testing] question if the assertions are good -#todo: run mutation testing, strip down to the minimum, test with real data -def test_conditional_seek_video_reader() -> None: - video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4')) - - conditional_seek_video_reader(video_reader, 50) - - assert video_reader.get('frame_number') == 50 - - conditional_seek_video_reader(video_reader, 10) - - assert video_reader.get('frame_number') == 10 - - conditional_seek_video_reader(video_reader, 200) - - assert video_reader.get('frame_number') == 200 + assert get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame') is video_reader + assert not get_reader(get_test_example_file('target-240p-25fps.mp4'), 'select_video_frames').get('id') == video_reader.get('id') #todo: needs review - [testing] question if the assertions are good #todo: run mutation testing, strip down to the minimum, test with real data def test_seek_video_reader() -> None: - video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4')) + video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame') sequential_frames = {} for frame_number in range(30): @@ -93,13 +76,13 @@ def test_seek_video_reader() -> None: #todo: needs review - [testing] question if the assertions are good #todo: run mutation testing, strip down to the minimum, test with real data def test_read_video_frame() -> None: - video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4')) + video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame') vision_frame = read_video_frame(video_reader) assert vision_frame.shape == (226, 426, 3) assert video_reader.get('frame_number') == 1 - conditional_seek_video_reader(video_reader, 269) + seek_video_reader(video_reader, 269) vision_frame = read_video_frame(video_reader) assert vision_frame.shape == (226, 426, 3) @@ -109,7 +92,7 @@ def test_read_video_frame() -> None: #todo: needs review - [testing] question if the assertions are good #todo: run mutation testing, strip down to the minimum, test with real data def test_read_video_frames() -> None: - video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4')) + video_reader = get_reader(get_test_example_file('target-240p-25fps.mp4'), 'read_video_frame') frame_set = read_video_frames(video_reader, 0, 4) assert sorted(frame_set) == [ 0, 1, 2, 3, 4 ] @@ -145,7 +128,7 @@ def test_get_writer() -> None: def test_write_video_frame() -> None: target_path = get_test_example_file('target-240p-25fps.mp4') create_temp_directory(target_path) - video_reader = get_reader(target_path) + video_reader = get_reader(target_path, 'read_video_frame') video_writer = get_writer(target_path, 25.0, (426, 226), (426, 226), 25.0) for frame_number in range(25): @@ -168,7 +151,7 @@ def test_write_video_frame() -> None: def test_close_video_writer() -> None: target_path = get_test_example_file('target-240p-30fps.mp4') create_temp_directory(target_path) - video_reader = get_reader(target_path) + video_reader = get_reader(target_path, 'read_video_frame') video_writer = get_writer(target_path, 30.0, (426, 226), (426, 226), 30.0) vision_frame = read_video_frame(video_reader) write_video_frame(video_writer, vision_frame) @@ -181,7 +164,7 @@ def test_close_video_writer() -> None: def test_clear_video_pool() -> None: target_path = get_test_example_file('target-240p-25fps.mp4') create_temp_directory(target_path) - video_reader = get_reader(target_path) + video_reader = get_reader(target_path, 'read_video_frame') video_writer = get_writer(target_path, 25.0, (426, 226), (426, 226), 25.0) vision_frame = read_video_frame(video_reader) write_video_frame(video_writer, vision_frame)