Stream video frames to avoid disk I/O
parent
3dda4f2179
commit
cf814bbcee
|
@ -19,7 +19,24 @@ import modules.globals
|
|||
import modules.metadata
|
||||
import modules.ui as ui
|
||||
from modules.processors.frame.core import get_frame_processors_modules
|
||||
from modules.utilities import has_image_extension, is_image, is_video, detect_fps, create_video, extract_frames, get_temp_frame_paths, restore_audio, create_temp, move_temp, clean_temp, normalize_output_path
|
||||
from modules.utilities import (
|
||||
has_image_extension,
|
||||
is_image,
|
||||
is_video,
|
||||
detect_fps,
|
||||
create_video,
|
||||
extract_frames,
|
||||
get_temp_frame_paths,
|
||||
restore_audio,
|
||||
create_temp,
|
||||
move_temp,
|
||||
clean_temp,
|
||||
normalize_output_path,
|
||||
start_ffmpeg_writer,
|
||||
get_temp_output_path,
|
||||
)
|
||||
import cv2
|
||||
from tqdm import tqdm
|
||||
|
||||
if 'ROCMExecutionProvider' in modules.globals.execution_providers:
|
||||
del torch
|
||||
|
@ -175,6 +192,45 @@ def update_status(message: str, scope: str = 'DLC.CORE') -> None:
|
|||
if not modules.globals.headless:
|
||||
ui.update_status(message)
|
||||
|
||||
|
||||
def stream_video() -> None:
|
||||
capture = cv2.VideoCapture(modules.globals.target_path)
|
||||
if not capture.isOpened():
|
||||
update_status('Failed to open video file.')
|
||||
return
|
||||
fps = capture.get(cv2.CAP_PROP_FPS) if modules.globals.keep_fps else 30.0
|
||||
width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
|
||||
height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
||||
total = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
|
||||
|
||||
update_status('Creating temp resources...')
|
||||
create_temp(modules.globals.target_path)
|
||||
temp_output_path = get_temp_output_path(modules.globals.target_path)
|
||||
writer = start_ffmpeg_writer(width, height, fps, temp_output_path)
|
||||
|
||||
progress_bar_format = '{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]'
|
||||
with tqdm(total=total, desc='Processing', unit='frame', dynamic_ncols=True, bar_format=progress_bar_format) as progress:
|
||||
progress.set_postfix({'execution_providers': modules.globals.execution_providers, 'execution_threads': modules.globals.execution_threads, 'max_memory': modules.globals.max_memory})
|
||||
while True:
|
||||
ret, frame = capture.read()
|
||||
if not ret:
|
||||
break
|
||||
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
||||
frame = frame_processor.process_frame_stream(modules.globals.source_path, frame)
|
||||
writer.stdin.write(frame.tobytes())
|
||||
progress.update(1)
|
||||
|
||||
capture.release()
|
||||
writer.stdin.close()
|
||||
writer.wait()
|
||||
|
||||
if modules.globals.keep_audio:
|
||||
update_status('Restoring audio...')
|
||||
restore_audio(modules.globals.target_path, modules.globals.output_path)
|
||||
else:
|
||||
move_temp(modules.globals.target_path, modules.globals.output_path)
|
||||
clean_temp(modules.globals.target_path)
|
||||
|
||||
def start() -> None:
|
||||
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
||||
if not frame_processor.pre_start():
|
||||
|
@ -202,10 +258,17 @@ def start() -> None:
|
|||
return
|
||||
|
||||
if not modules.globals.map_faces:
|
||||
update_status('Creating temp resources...')
|
||||
create_temp(modules.globals.target_path)
|
||||
update_status('Extracting frames...')
|
||||
extract_frames(modules.globals.target_path)
|
||||
stream_video()
|
||||
if is_video(modules.globals.target_path):
|
||||
update_status('Processing to video succeed!')
|
||||
else:
|
||||
update_status('Processing to video failed!')
|
||||
return
|
||||
|
||||
update_status('Creating temp resources...')
|
||||
create_temp(modules.globals.target_path)
|
||||
update_status('Extracting frames...')
|
||||
extract_frames(modules.globals.target_path)
|
||||
|
||||
temp_frame_paths = get_temp_frame_paths(modules.globals.target_path)
|
||||
for frame_processor in get_frame_processors_modules(modules.globals.frame_processors):
|
||||
|
|
|
@ -14,7 +14,8 @@ FRAME_PROCESSORS_INTERFACE = [
|
|||
'pre_start',
|
||||
'process_frame',
|
||||
'process_image',
|
||||
'process_video'
|
||||
'process_video',
|
||||
'process_frame_stream'
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -107,3 +107,8 @@ def process_frame_v2(temp_frame: Frame) -> Frame:
|
|||
if target_face:
|
||||
temp_frame = enhance_face(temp_frame)
|
||||
return temp_frame
|
||||
|
||||
|
||||
def process_frame_stream(source_path: str, frame: Frame) -> Frame:
|
||||
return process_frame(None, frame)
|
||||
|
||||
|
|
|
@ -255,4 +255,21 @@ def process_video(source_path: str, temp_frame_paths: List[str]) -> None:
|
|||
if modules.globals.map_faces and modules.globals.many_faces:
|
||||
update_status('Many faces enabled. Using first source image (if applicable in v2). Processing...', NAME)
|
||||
# The core processing logic is delegated, which is good.
|
||||
modules.processors.frame.core.process_video(source_path, temp_frame_paths, process_frames)
|
||||
modules.processors.frame.core.process_video(source_path, temp_frame_paths, process_frames)
|
||||
|
||||
|
||||
STREAM_SOURCE_FACE = None
|
||||
|
||||
|
||||
def process_frame_stream(source_path: str, frame: Frame) -> Frame:
|
||||
global STREAM_SOURCE_FACE
|
||||
if not modules.globals.map_faces:
|
||||
if STREAM_SOURCE_FACE is None:
|
||||
source_img = cv2.imread(source_path)
|
||||
if source_img is not None:
|
||||
STREAM_SOURCE_FACE = get_one_face(source_img)
|
||||
if STREAM_SOURCE_FACE is not None:
|
||||
return process_frame(STREAM_SOURCE_FACE, frame)
|
||||
return frame
|
||||
else:
|
||||
return process_frame_v2(frame)
|
||||
|
|
|
@ -38,6 +38,38 @@ def run_ffmpeg(args: List[str]) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def start_ffmpeg_writer(width: int, height: int, fps: float, output_path: str) -> subprocess.Popen:
|
||||
commands = [
|
||||
"ffmpeg",
|
||||
"-hide_banner",
|
||||
"-hwaccel",
|
||||
"auto",
|
||||
"-loglevel",
|
||||
modules.globals.log_level,
|
||||
"-f",
|
||||
"rawvideo",
|
||||
"-pix_fmt",
|
||||
"bgr24",
|
||||
"-s",
|
||||
f"{width}x{height}",
|
||||
"-r",
|
||||
str(fps),
|
||||
"-i",
|
||||
"-",
|
||||
"-c:v",
|
||||
modules.globals.video_encoder,
|
||||
"-crf",
|
||||
str(modules.globals.video_quality),
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
"-vf",
|
||||
"colorspace=bt709:iall=bt601-6-625:fast=1",
|
||||
"-y",
|
||||
output_path,
|
||||
]
|
||||
return subprocess.Popen(commands, stdin=subprocess.PIPE)
|
||||
|
||||
|
||||
def detect_fps(target_path: str) -> float:
|
||||
command = [
|
||||
"ffprobe",
|
||||
|
|
Loading…
Reference in New Issue