Skip to content

Commit 1eebfb2

Browse files
committed
Оптимизация финальной склейки
1 parent 0190ccf commit 1eebfb2

File tree

9 files changed

+218
-150
lines changed

9 files changed

+218
-150
lines changed

.env

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ START_BATCH_TO_UPSCALE=1
44
END_BATCH_TO_UPSCALE=0
55
STEP_PER_BATCH=6
66
FRAMES_PER_BATCH=1000
7-
ALLOWED_THREADS=12
7+
ALLOWED_CPU_THREADS=12
8+
ALLOWED_GPU_THREADS=2
89

910
# Настройка апскейла
1011
MODEL_NAME=realesr-animevideov3

main.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import os
3+
import re
34
from datetime import datetime
45

56
from src.audio.audio_handling import AudioHandler
@@ -28,23 +29,25 @@ async def main():
2829
start_time = datetime.now()
2930
print_header("запуск обработки видео")
3031

31-
print("\n📹 Получаем FPS исходного видео...")
3232
fps = await asyncio.to_thread(get_fps_accurate, ORIGINAL_VIDEO)
33-
print(f"✅ FPS видео: {fps}")
34-
3533
audio = AudioHandler()
3634
video = VideoHandler(fps=fps)
3735

38-
print("\n🔊 Извлекаем аудиодорожку...")
3936
await asyncio.create_task(audio.extract_audio())
4037
await asyncio.to_thread(extract_frames_to_batches)
41-
print("✅ Кадры успешно извлечены")
4238

4339
# Определяем диапазон батчей для обработки
4440
start_batch = START_BATCH_TO_UPSCALE
4541
end_batch = 0
4642
if END_BATCH_TO_UPSCALE == 0:
47-
end_batch_to_upscale = len(os.listdir(INPUT_BATCHES_DIR)) - 1 # -1 для .gitkeep
43+
batch_name_pattern = re.compile(r"batch_(\d+)")
44+
end_batch_to_upscale = len(
45+
[
46+
batch
47+
for batch in os.listdir(INPUT_BATCHES_DIR)
48+
if batch_name_pattern.match(batch)
49+
]
50+
)
4851
else:
4952
end_batch_to_upscale = END_BATCH_TO_UPSCALE
5053
print(f"\nВсего батчей для обработки: {end_batch_to_upscale}")
@@ -65,16 +68,16 @@ async def main():
6568
print(f"✅ Батчи {start_batch}-{end_batch} успешно апскейлены")
6669

6770
batches_to_perform = [f"batch_{i}" for i in range(start_batch, end_batch + 1)]
68-
short_video = await video.process_frames_to_video(batches_to_perform)
71+
short_video = await video.build_short_video(batches_to_perform)
6972
if short_video:
73+
await asyncio.to_thread(delete_frames, del_upscaled=False)
7074
await asyncio.to_thread(delete_frames, del_upscaled=True)
7175
print(f"🎥 Видео собрано: {short_video}")
7276
print(f"🗑️ Обработанные кадры удалены из батчей {start_batch}-{end_batch}")
7377

7478
start_batch += STEP_PER_BATCH
7579

7680
print_header("финальная сборка видео")
77-
await asyncio.to_thread(delete_frames, del_upscaled=False)
7881
# Сборка финального видео из временных видео
7982
final_merge = video.build_final_video()
8083

src/audio/audio_handling.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
run_ffmpeg_command_with_progress,
1010
)
1111
from src.config.settings import (
12-
ALLOWED_THREADS,
12+
ALLOWED_CPU_THREADS,
1313
AUDIO_PATH,
1414
FINAL_VIDEO,
1515
ORIGINAL_VIDEO,
1616
RESOLUTION,
17-
TMP_VIDEO,
17+
TMP_VIDEO_PATH,
1818
)
1919
from src.utils.file_utils import delete_file
2020
from src.video.video_helpers import get_video_duration
@@ -32,7 +32,7 @@ class AudioHandler:
3232
def __init__(
3333
self,
3434
input_video_path: str = ORIGINAL_VIDEO,
35-
merged_video_path: str = TMP_VIDEO,
35+
merged_video_path: str = TMP_VIDEO_PATH,
3636
output_video_path: str = FINAL_VIDEO,
3737
audio_path: str = AUDIO_PATH,
3838
audio_format: str = "mp3",
@@ -52,20 +52,6 @@ def __init__(
5252
print(f"\tЧастота дискретизации: {self.SAMPLE_FREQ} Hz")
5353
print(f"\tКаналы: {self.CANALS} (стерео)")
5454

55-
def check_audio_extracted(self, audio_file) -> None:
56-
"""
57-
Проверяет, было ли аудио успешно извлечено из видеофайла.
58-
Возвращает True, если аудио существует, иначе False.
59-
"""
60-
if audio_file and os.path.exists(audio_file):
61-
print(f"✅ Аудио успешно извлечено: {audio_file}\n")
62-
self.audio_path = audio_file
63-
else:
64-
print("⚠️ Аудио не найдено или не было извлечено.")
65-
raise FileNotFoundError(
66-
"Аудиофайл не найден. Проверьте, было ли аудио успешно извлечено."
67-
)
68-
6955
async def extract_audio(self) -> Optional[str]:
7056
"""
7157
Извлекает аудио из видеофайла и сохраняет его как отдельный аудиофайл.
@@ -86,7 +72,7 @@ def __sync_extract():
8672
"-ac", self.CANALS,
8773
"-b:a", self.BITRATE,
8874
"-progress", "-",
89-
"-threads", str(ALLOWED_THREADS),
75+
"-threads", str(ALLOWED_CPU_THREADS),
9076
"-loglevel", "error",
9177
audio_file,
9278
]
@@ -97,7 +83,8 @@ def __sync_extract():
9783
except subprocess.CalledProcessError as e:
9884
print(f"🚨 Ошибка при извлечении аудио: {e}")
9985
return None
100-
self.check_audio_extracted(audio_file)
86+
self.__check_audio_extracted(audio_file)
87+
return None
10188

10289
loop = asyncio.get_event_loop()
10390
return await loop.run_in_executor(None, __sync_extract)
@@ -119,7 +106,7 @@ def __sync_insert():
119106
print(f"\tКодек аудио: исходный ({self.audio_format})")
120107
print(f"\tДлительность видео: {duration:.2f} сек")
121108
print(f"\tFPS видео: {fps}")
122-
print(f"\tПотоков: {ALLOWED_THREADS}")
109+
print(f"\tПотоков: {ALLOWED_CPU_THREADS}")
123110
print(f"\tРазрешение: {self.resolution}")
124111

125112
cmd = [
@@ -130,7 +117,7 @@ def __sync_insert():
130117
"-map", "0:v:0",
131118
"-map", "1:a:0",
132119
"-shortest", "-progress", "-",
133-
"-threads", str(ALLOWED_THREADS),
120+
"-threads", str(ALLOWED_CPU_THREADS),
134121
"-nostats", "-loglevel", "error",
135122
self.out_video_path,
136123
]
@@ -147,6 +134,20 @@ def __sync_insert():
147134
loop = asyncio.get_event_loop()
148135
await loop.run_in_executor(None, __sync_insert)
149136

137+
def __check_audio_extracted(self, audio_file) -> None:
138+
"""
139+
Проверяет, было ли аудио успешно извлечено из видеофайла.
140+
Возвращает True, если аудио существует, иначе False.
141+
"""
142+
if audio_file and os.path.exists(audio_file):
143+
print(f"✅ Аудио успешно извлечено: {audio_file}\n")
144+
self.audio_path = audio_file
145+
else:
146+
print("⚠️ Аудио не найдено или не было извлечено.")
147+
raise FileNotFoundError(
148+
"Аудиофайл не найден. Проверьте, было ли аудио успешно извлечено."
149+
)
150+
150151
def __str__(self):
151152
return (
152153
f"AudioHandler(in_video_path={self.in_video_path}, "

src/config/settings.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# Пути к файлам
99
ORIGINAL_VIDEO = os.path.join(ROOT_DIR, "data", "input_video", "naruto_test.mp4")
1010
AUDIO_PATH = os.path.join(ROOT_DIR, "data", "audio")
11-
TMP_VIDEO = os.path.join(ROOT_DIR, "data", "tmp_video", "final_merge.mp4")
11+
TMP_VIDEO_PATH = os.path.join(ROOT_DIR, "data", "tmp_video")
1212
FINAL_VIDEO = os.path.join(ROOT_DIR, "data", "output_video", "final_video.mp4")
1313
BATCH_VIDEO_PATH = os.path.join(ROOT_DIR, "data", "video_batches")
1414
INPUT_BATCHES_DIR = os.path.join(ROOT_DIR, "data", "default_frame_batches")
@@ -20,7 +20,8 @@
2020
END_BATCH_TO_UPSCALE = int(os.getenv("END_BATCH_TO_UPSCALE", 0))
2121
STEP_PER_BATCH = int(os.getenv("STEP_PER_BATCH", 6))
2222
FRAMES_PER_BATCH = int(os.getenv("FRAMES_PER_BATCH", 1000))
23-
ALLOWED_THREADS = int(os.getenv("ALLOWED_THREADS", 6))
23+
ALLOWED_CPU_THREADS = int(os.getenv("ALLOWED_THREADS", 6))
24+
ALLowed_GPU_THREADS = int(os.getenv("ALLOWED_GPU_THREADS", 1))
2425

2526
# Настройка апскейла
2627
MODEL_DIR = os.path.join(ROOT_DIR, "src", "utils", "realesrgan", "models")

src/frames/frames_helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from tqdm import tqdm
99

1010
from src.config.settings import (
11-
ALLOWED_THREADS,
11+
ALLOWED_CPU_THREADS,
1212
FRAMES_PER_BATCH,
1313
INPUT_BATCHES_DIR,
1414
ORIGINAL_VIDEO,
@@ -80,7 +80,7 @@ def extract_frames_to_batches(
8080
colour="green",
8181
file=sys.stdout,
8282
) as pbar:
83-
with ThreadPoolExecutor(max_workers=ALLOWED_THREADS) as executor:
83+
with ThreadPoolExecutor(max_workers=ALLOWED_CPU_THREADS) as executor:
8484
current_batch_dir = make_default_batch_dir(output_dir)
8585

8686
while True:

src/frames/upscale.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from tqdm import tqdm
1111

1212
from src.config.settings import (
13-
ALLOWED_THREADS,
13+
ALLOWED_CPU_THREADS,
1414
INPUT_BATCHES_DIR,
1515
MODEL_DIR,
1616
MODEL_NAME,
@@ -118,7 +118,7 @@ async def upscale_batches(start_batch: int, end_batch: int):
118118
# Запускаем обработку батчей с использованием ProcessPoolExecutor
119119
loop = asyncio.get_event_loop()
120120

121-
with ProcessPoolExecutor(max_workers=ALLOWED_THREADS) as executor:
121+
with ProcessPoolExecutor(max_workers=ALLOWED_CPU_THREADS) as executor:
122122
tasks = [
123123
loop.run_in_executor(executor, _upscale, batch_num)
124124
for batch_num in batches_range

src/video/video_exceptions.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class VideoDoesNotExist(Exception):
2+
def __init__(self, video_path: str):
3+
super().__init__(
4+
f"Видео '{video_path}' не найдено. Проверьте правильность пути."
5+
)
6+
7+
8+
class VideoMergingError(Exception):
9+
def __init__(self, error):
10+
super().__init__(f"Ошибка при объединении видео.\nПричина: {error}")
11+
12+
13+
class VideoReadFrameError(Exception):
14+
def __init__(self, video_path: str):
15+
super().__init__(
16+
f"Не удалось прочитать кадр {1} из видео '{video_path}'."
17+
)

0 commit comments

Comments
 (0)