File size: 5,495 Bytes
8dd41a8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import roop.globals

from threading import Thread
from chain_img_processor import ChainImgProcessor

class ThreadWithReturnValue(Thread):

    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, Verbose=None):
        Thread.__init__(self, group, target, name, args, kwargs)
        self._return = None

    def run(self):
        if self._target is not None:
            self._return = self._target(*self._args,
                                        **self._kwargs)

    def join(self, *args):
        Thread.join(self, *args)
        return self._return


# in beta
class ChainVideoProcessor(ChainImgProcessor):
    def __init__(self):
        ChainImgProcessor.__init__(self)

        self.video_save_codec = "libx264"
        self.video_save_crf = 14

    def init_with_plugins(self):
        self.init_plugins(["core","core_video"])
        self.display_init_info()

        init_on_start_arr = self.init_on_start.split(",")
        for proc_id in init_on_start_arr:
            self.init_processor(proc_id)

    def run_video_chain(self, source_video, target_video, fps, threads:int = 1, chain = None, params_frame_gen_func = None, video_audio = None):
        import cv2
        from tqdm import tqdm
        from chain_img_processor.ffmpeg_writer import FFMPEG_VideoWriter # ffmpeg install needed

        cap = cv2.VideoCapture(source_video)
        # width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        # height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

        # first frame do manually - because upscale may happen, we need to estimate width/height
        ret, frame = cap.read()
        if params_frame_gen_func is not None:
            params = params_frame_gen_func(self, frame)
        else:
            params = {}
        params["original_frame"] = frame
        frame_processed, params = self.run_chain(frame,params,chain)
        height, width, channels = frame_processed.shape

        self.fill_processors_for_thread_chains(threads,chain)
        #print(self.processors_objects)
        #import threading
        #locks:list[threading.Lock] = []
        locks: list[bool] = []
        for i in range(threads):
            #locks.append(threading.Lock())
            locks.append(False)

        temp = []
        with FFMPEG_VideoWriter(target_video, (width, height), fps, codec=roop.globals.video_encoder, crf=roop.globals.video_quality, audiofile=video_audio) as output_video_ff:
            with tqdm(total=frame_count, desc='Processing', unit="frame", dynamic_ncols=True,
                      bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]') as progress:

                # do first frame
                output_video_ff.write_frame(frame_processed)
                progress.update(1) #
                cnt_frames = 0

                # do rest frames
                while True:
                    # getting frame
                    ret, frame = cap.read()

                    if not ret:
                        break
                    cnt_frames+=1
                    thread_ind = cnt_frames % threads
                    # we are having an array of length %gpu_threads%, running in parallel
                    # so if array is equal or longer than gpu threads, waiting
                    #while len(temp) >= threads:
                    while locks[thread_ind]:
                        #print('WAIT', thread_ind)
                        # we are order dependent, so we are forced to wait for first element to finish. When finished removing thread from the list
                        frame_processed, params = temp.pop(0).join()
                        locks[params["_thread_index"]] = False
                        #print('OFF',cnt_frames,locks[params["_thread_index"]],locks)
                        # writing into output
                        output_video_ff.write_frame(frame_processed)
                        # updating the status
                        progress.update(1)

                    # calc params for frame
                    if params_frame_gen_func is not None:
                        params = params_frame_gen_func(self,frame)
                    else:
                        params = {}

                    # adding new frame to the list and starting it
                    locks[thread_ind] = True
                    #print('ON', cnt_frames, thread_ind, locks)
                    params["original_frame"] = frame
                    temp.append(
                        ThreadWithReturnValue(target=self.run_chain, args=(frame, params, chain, thread_ind)))
                    temp[-1].start()

                while len(temp) > 0:
                    # we are order dependent, so we are forced to wait for first element to finish. When finished removing thread from the list
                    frame_processed, params = temp.pop(0).join()
                    locks[params["_thread_index"]] = False
                    # writing into output
                    output_video_ff.write_frame(frame_processed)

                    progress.update(1)

                #print("FINAL", locks)

_video_processor:ChainVideoProcessor = None
def get_single_video_processor() -> ChainVideoProcessor:
    global _video_processor
    if _video_processor is None:
        _video_processor = ChainVideoProcessor()
        _video_processor.init_with_plugins()
    return _video_processor