From abefe569d2ddc5f731d820f8e1a4cfac17a5f413 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Tue, 31 May 2022 11:05:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8E=8B=E5=88=B6=E6=96=B9?= =?UTF-8?q?=E6=B3=95=EF=BC=8C=E5=85=88=E8=AE=BE=E7=BD=AEgop=EF=BC=8C?= =?UTF-8?q?=E7=84=B6=E5=90=8E=E5=86=8D=E5=88=87=E5=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- danmaku_workflow.py | 272 ++++++++++++++------------------------------ 1 file changed, 87 insertions(+), 185 deletions(-) diff --git a/danmaku_workflow.py b/danmaku_workflow.py index fe19586..e4fae5a 100644 --- a/danmaku_workflow.py +++ b/danmaku_workflow.py @@ -165,12 +165,8 @@ class HomePage(QWidget): self.labelDestroy.connect(self.on_label_destroy) self.processCurTime.connect(self.on_process_cur_time_change) self.processSpeed.connect(self.on_process_speed_change) - self.processCurTime2.connect(self.on_process_cur_time2_change) - self.processSpeed2.connect(self.on_process_speed2_change) self.process_cur_time = "-" self.process_speed = "-" - self.process_cur_time2 = "" - self.process_speed2 = "" self.cur_clip_duration = 0 self.init_ui() @@ -247,28 +243,11 @@ class HomePage(QWidget): self.process_speed = s self.update_btn_process_text() - def on_process_cur_time2_change(self, s: str) -> None: - if self.process_cur_time2 == s: - return - self.process_cur_time2 = s - self.update_btn_process_text() - - def on_process_speed2_change(self, s: str) -> None: - if self.process_speed2 == s: - return - self.process_speed2 = s - self.update_btn_process_text() - def update_btn_process_text(self): - _t = [] if self.process_cur_time != "" and self.process_speed != "": - _t.append("{}@{}".format(self.process_cur_time, self.process_speed)) - if self.process_cur_time2 != "" and self.process_speed2 != "": - _t.append("{}@{}".format(self.process_cur_time2, self.process_speed2)) - if len(_t) == 0: - self.btn_start.setText("Working") + self.btn_start.setText("{}@{}".format(self.process_cur_time, self.process_speed)) else: - self.btn_start.setText("|".join(_t)) + self.btn_start.setText("Working") def on_worker_stop(self): self.btn_start.setDisabled(False) @@ -323,157 +302,28 @@ class WorkerThread(QThread): new_subtitle_name = danmaku_to_subtitle(danmaku, time_shift) job.subtitles.append(new_subtitle_name) # 压制 - split_files = self.multi_gpu_encode_video_with_subtitles(job.video, job.subtitles, base_start_ts) - for file in split_files: - self.quick_split_video(file) + files_need_split = self.encode_video_with_subtitles(job.video, job.subtitles, base_start_ts) + for _f in files_need_split: + self.quick_split_video(_f) for _f in job.subtitles: if os.path.isfile(_f): os.remove(_f) - for _f in split_files: + for _f in files_need_split: if os.path.isfile(_f): os.remove(_f) - def encode_video_with_subtitles(self, orig_filename: str, subtitles: list[str], new_filename: str): - if FFMPEG_USE_NVIDIA_GPU: - print("[+]Use Nvidia NvEnc Acceleration") - encode_process = subprocess.Popen([ - FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y", - "-i", orig_filename, "-vf", - ",".join("subtitles=%s" % i for i in subtitles) + ",hwupload_cuda", - "-c:a", "copy", "-c:v", "h264_nvenc", - "-f", "mp4", "-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq", - *_common_ffmpeg_params(), - # "-t", "10", - new_filename - ], **subprocess_args(True)) - elif FFMPEG_USE_INTEL_GPU: - if platform.system().lower() == "windows": - print("[+]Use Intel QSV Acceleration") - encode_process = subprocess.Popen([ - FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y", - "-hwaccel", "qsv", "-i", orig_filename, "-vf", - ",".join("subtitles=%s" % i for i in subtitles), - "-c:a", "copy", "-c:v", "h264_qsv", - "-f", "mp4", "-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq", - *_common_ffmpeg_params(), - # "-t", "10", - new_filename - ], **subprocess_args(True)) - else: - print("[+]Use Intel VAAPI Acceleration") - encode_process = subprocess.Popen([ - FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y", - "-hwaccel", "vaapi", "-i", orig_filename, "-vf", - ",".join("subtitles=%s" % i for i in subtitles) + ",hwupload", - "-c:a", "copy", "-c:v", "h264_vaapi", - "-f", "mp4", "-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq", - *_common_ffmpeg_params(), - # "-t", "10", - new_filename - ], **subprocess_args(True)) - else: - print("[+]Use CPU Encode") - encode_process = subprocess.Popen([ - FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y", - "-i", orig_filename, "-vf", - ",".join("subtitles=%s" % i for i in subtitles), - "-c:a", "copy", "-c:v", "h264", - "-f", "mp4", "-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq", - *_common_ffmpeg_params(), - # "-t", "10", - new_filename - ], **subprocess_args(True)) - self.handle_ffmpeg_output(encode_process.stdout) - return encode_process.wait() - - def multi_gpu_encode_video_with_subtitles(self, orig_filename: str, subtitles: list[str], base_ts: float): - new_filename = base_ts_to_filename(base_ts) + def encode_video_with_subtitles(self, orig_filename: str, subtitles: list[str], base_ts: float): + new_filename = base_ts_to_filename(base_ts, False) new_fullpath = os.path.join(VIDEO_OUTPUT_DIR, new_filename) - if not (FFMPEG_USE_NVIDIA_GPU and FFMPEG_USE_INTEL_GPU): - print("[!]Not Enabled Both GPU") - self.encode_video_with_subtitles(orig_filename, subtitles, new_fullpath) - return [new_fullpath] - duration = self.get_video_real_duration(orig_filename) - print("[ ]Video Duration:", duration) - if duration > (VIDEO_CLIP_EACH_SEC * 5): - # qsv 压制前2段,剩余交由nvenc压制 - _slices = int(duration / VIDEO_CLIP_EACH_SEC) - new_filename0 = base_ts_to_filename(base_ts + (_slices - 1) * VIDEO_CLIP_EACH_SEC) - new_filename1 = base_ts_to_filename(base_ts) - new_fullpath0 = os.path.join(VIDEO_OUTPUT_DIR, new_filename0) - new_fullpath1 = os.path.join(VIDEO_OUTPUT_DIR, new_filename1) - print("[+]Use Intel QSV Acceleration") - encode_process0 = subprocess.Popen([ - FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y", - "-hwaccel", "qsv", "-ss", str((_slices - 1) * VIDEO_CLIP_EACH_SEC), - "-copyts", "-i", orig_filename, "-vf", - ",".join("subtitles=%s" % i for i in subtitles), - "-ss", str((_slices - 1) * VIDEO_CLIP_EACH_SEC), - "-c:a", "copy", "-c:v", "h264_qsv", - "-f", "mp4", "-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq", - *_common_ffmpeg_params(), - # "-t", "10", - new_fullpath0 - ], **subprocess_args(True)) - print("[+]Use Nvidia NvEnc Acceleration") - encode_process1 = subprocess.Popen([ - FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y", - "-t", str((_slices - 1) * VIDEO_CLIP_EACH_SEC + (VIDEO_CLIP_OVERFLOW_SEC * 0.8)), - "-i", orig_filename, "-vf", - ",".join("subtitles=%s" % i for i in subtitles), - "-c:a", "copy", "-c:v", "h264_nvenc", - "-f", "mp4", "-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq", - *_common_ffmpeg_params(), - # "-t", "10", - new_fullpath1 - ], **subprocess_args(True)) - threading.Thread(target=self.handle_ffmpeg_output, args=(encode_process0.stdout, True,)).start() - threading.Thread(target=self.handle_ffmpeg_output, args=(encode_process1.stdout,)).start() - encode_process0.wait() - encode_process1.wait() - return [new_filename0, new_filename1] - elif duration > (VIDEO_CLIP_EACH_SEC * 3): - # 至少也要能切2片,才用双GPU加速 - _slices = int(duration / VIDEO_CLIP_EACH_SEC) - new_filename0 = base_ts_to_filename(base_ts + _slices * VIDEO_CLIP_EACH_SEC, True) - new_filename1 = base_ts_to_filename(base_ts) - new_fullpath0 = os.path.join(VIDEO_OUTPUT_DIR, new_filename0) - new_fullpath1 = os.path.join(VIDEO_OUTPUT_DIR, new_filename1) - print("[+]Use Intel QSV Acceleration") - encode_process0 = subprocess.Popen([ - FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y", - "-hwaccel", "qsv", "-ss", str(_slices * VIDEO_CLIP_EACH_SEC), - "-copyts", "-i", orig_filename, "-vf", - ",".join("subtitles=%s" % i for i in subtitles), - "-ss", str(_slices * VIDEO_CLIP_EACH_SEC), - "-c:a", "copy", "-c:v", "h264_qsv", - "-f", "mp4", "-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq", - *_common_ffmpeg_params(), - # "-t", "10", - new_fullpath0 - ], **subprocess_args(True)) - print("[+]Use Nvidia NvEnc Acceleration") - encode_process1 = subprocess.Popen([ - FFMPEG_EXEC, "-hide_banner", "-progress", "-", "-loglevel", "error", "-y", - "-t", str(_slices * VIDEO_CLIP_EACH_SEC + (VIDEO_CLIP_OVERFLOW_SEC * 0.8)), - "-i", orig_filename, "-vf", - ",".join("subtitles=%s" % i for i in subtitles), - "-c:a", "copy", "-c:v", "h264_nvenc", - "-f", "mp4", "-b:v", VIDEO_BITRATE, "-rc:v", "vbr", "-tune:v", "hq", - *_common_ffmpeg_params(), - # "-t", "10", - new_fullpath1 - ], **subprocess_args(True)) - threading.Thread(target=self.handle_ffmpeg_output, args=(encode_process0.stdout, True,)).start() - threading.Thread(target=self.handle_ffmpeg_output, args=(encode_process1.stdout,)).start() - encode_process1.wait() - encode_process0.wait() - return [new_filename1] + if FFMPEG_USE_NVIDIA_GPU: + process = get_encode_process_use_nvenc(orig_filename, subtitles, new_fullpath) + elif FFMPEG_USE_INTEL_GPU: + process = get_encode_process_use_intel(orig_filename, subtitles, new_fullpath) else: - print("[-]VideoClip to short,", duration) - print("[-]Fallback to normal") - self.encode_video_with_subtitles(orig_filename, subtitles, new_fullpath) - return [new_fullpath] + process = get_encode_process_use_cpu(orig_filename, subtitles, new_fullpath) + self.handle_ffmpeg_output(process.stdout) + process.wait() + return [new_fullpath] def quick_split_video(self, file): if not os.path.isfile(file): @@ -502,9 +352,10 @@ class WorkerThread(QThread): split_process.wait() current_sec += VIDEO_CLIP_EACH_SEC - def handle_ffmpeg_output(self, stdout: Optional[IO[bytes]], second=False) -> str: + def handle_ffmpeg_output(self, stdout: Optional[IO[bytes]]) -> str: out_time = "0:0:0.0" if stdout is None: + print("[!]STDOUT is null") return out_time speed = "0" while True: @@ -516,22 +367,13 @@ class WorkerThread(QThread): break if line.startswith(b"out_time="): out_time = line.replace(b"out_time=", b"").decode().strip() - if second: - self.app.processCurTime2.emit(out_time) - else: - self.app.processCurTime.emit(out_time) + self.app.processCurTime.emit(out_time) if line.startswith(b"speed="): speed = line.replace(b"speed=", b"").decode().strip() - if second: - self.app.processSpeed2.emit(speed) - else: - self.app.processSpeed.emit(speed) - if second: - self.app.processSpeed2.emit("") - self.app.processCurTime2.emit("") - else: - self.app.processSpeed.emit("") - self.app.processCurTime.emit("") + self.app.processSpeed.emit(speed) + self.app.processSpeed.emit("") + self.app.processCurTime.emit("") + print("[ ]Speed:", out_time, "@", speed) return out_time @@ -573,6 +415,60 @@ def danmaku_to_subtitle(file: Union[os.PathLike[str], str], time_shift: float): return new_subtitle_name +def get_encode_process_use_nvenc(orig_filename: str, subtitles: list[str], new_filename: str): + print("[+]Use Nvidia NvEnc Acceleration") + encode_process = subprocess.Popen([ + FFMPEG_EXEC, *_common_ffmpeg_setting(), + "-i", orig_filename, "-vf", + ",".join("subtitles=%s" % i for i in subtitles) + ",hwupload_cuda", + "-c:v", "h264_nvenc", "-rc:v", "vbr", + *_common_ffmpeg_params(), + # "-t", "10", + new_filename + ], **subprocess_args(True)) + return encode_process + + +def get_encode_process_use_intel(orig_filename: str, subtitles: list[str], new_filename: str): + if platform.system().lower() == "windows": + print("[+]Use Intel QSV Acceleration") + encode_process = subprocess.Popen([ + FFMPEG_EXEC, *_common_ffmpeg_setting(), + "-hwaccel", "qsv", "-i", orig_filename, "-vf", + ",".join("subtitles=%s" % i for i in subtitles), + "-c:v", "h264_qsv", "-rc:v", "vbr", + *_common_ffmpeg_params(), + # "-t", "10", + new_filename + ], **subprocess_args(True)) + else: + print("[+]Use Intel VAAPI Acceleration") + encode_process = subprocess.Popen([ + FFMPEG_EXEC, *_common_ffmpeg_setting(), + "-hwaccel", "vaapi", "-i", orig_filename, "-vf", + ",".join("subtitles=%s" % i for i in subtitles) + ",hwupload", + "-c:v", "h264_vaapi", "-rc:v", "vbr", + *_common_ffmpeg_params(), + # "-t", "10", + new_filename + ], **subprocess_args(True)) + return encode_process + + +def get_encode_process_use_cpu(orig_filename: str, subtitles: list[str], new_filename: str): + print("[+]Use CPU Encode") + encode_process = subprocess.Popen([ + FFMPEG_EXEC, *_common_ffmpeg_setting(), + "-i", orig_filename, "-vf", + ",".join("subtitles=%s" % i for i in subtitles), + "-c:v", "h264", + *_common_ffmpeg_params(), + # "-t", "10", + new_filename + ], **subprocess_args(True)) + return encode_process + + # Create a set of arguments which make a ``subprocess.Popen`` (and # variants) call work with or without Pyinstaller, ``--noconsole`` or # not, on Windows and Linux. Typical use:: @@ -618,7 +514,6 @@ def subprocess_args(include_stdout=True): # (stdin, stdout, stderr) to avoid an OSError exception # "[Error 6] the handle is invalid." ret.update({'stdin': subprocess.PIPE, - 'stderr': subprocess.PIPE, 'startupinfo': si, 'env': env}) return ret @@ -658,11 +553,18 @@ def check_all_prerequisite(): exit(1) +def _common_ffmpeg_setting(): + return ( + "-y", "-hide_banner", "-progress", "-", "-loglevel", "error", + ) + + def _common_ffmpeg_params(): return ( - "-preset:v", "fast", "-profile:v", "high", "-level", "4.1", - "-qmin", "10", "-qmax", "48", "-crf", "26", - "-fflags", "+genpts", "-shortest" + "-f", "mp4", "-b:v", VIDEO_BITRATE, "-c:a", "copy", + "-preset:v", "fast", "-profile:v", "main", "-avoid_negative_ts", "1", + "-qmin", "12", "-qmax", "34", "-crf", "26", "-g:v", "60", + "-fflags", "+genpts", "-shortest", "-movflags", "faststart" )