From b74c6f0ef736141e6ac1cee82538f9f15603315e Mon Sep 17 00:00:00 2001
From: Jerry Yan <792602257@qq.com>
Date: Mon, 10 Oct 2022 14:35:28 +0800
Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81vaapi=E3=80=81=E6=94=AF?=
 =?UTF-8?q?=E6=8C=81=E5=AE=9A=E4=B9=89=E4=BD=BF=E7=94=A8=E5=93=AA=E7=A7=8D?=
 =?UTF-8?q?=E5=BC=B9=E5=B9=95=E8=BD=AC=E6=8D=A2=E5=B7=A5=E5=85=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 config.py                             | 29 +++++++++++++++------
 controller/api/collector_blueprint.py |  4 +--
 templates/index.html                  | 15 +++++++++++
 workflow/danmaku.py                   | 31 +++++++++++++++++-----
 workflow/video.py                     | 37 +++++++++++++++++++++++++--
 5 files changed, 98 insertions(+), 18 deletions(-)

diff --git a/config.py b/config.py
index 035b8ea..c3de16b 100644
--- a/config.py
+++ b/config.py
@@ -3,12 +3,16 @@ import os.path
 
 
 # [danmaku]
+# use_danmu2ass
+DANMAKU_USE_DANMU2ASS = False
+# use_danmakufactory
+DANMAKU_USE_DANMAKUFACTORY = True
 # exec
-DANMAKU_FACTORY_EXEC = "DanmakuFactory"
+DANMAKU_EXEC = "DanmakuFactory"
 # speed
 DANMAKU_SPEED = 12
 # font
-DEFAULT_FONT_NAME = "Sarasa Term SC"
+DANMAKU_FONT_NAME = "Sarasa Term SC"
 # font_size
 DANMAKU_FONT_SIZE = 40
 # resolution
@@ -22,6 +26,8 @@ FFMPEG_USE_HEVC = False
 FFMPEG_USE_NVIDIA_GPU = False
 # intel_gpu
 FFMPEG_USE_INTEL_GPU = False
+# vaapi
+FFMPEG_USE_VAAPI = False
 # bitrate
 VIDEO_BITRATE = "2.5M"
 # crf
@@ -66,10 +72,13 @@ def load_config():
     config.read("config.ini", encoding="utf-8")
     if config.has_section("danmaku"):
         section = config['danmaku']
-        global DANMAKU_FACTORY_EXEC, DANMAKU_SPEED, DEFAULT_FONT_NAME, VIDEO_RESOLUTION, DANMAKU_FONT_SIZE
-        DANMAKU_FACTORY_EXEC = section.get('exec', DANMAKU_FACTORY_EXEC)
+        global DANMAKU_EXEC, DANMAKU_SPEED, DANMAKU_FONT_NAME, VIDEO_RESOLUTION, DANMAKU_FONT_SIZE, \
+            DANMAKU_USE_DANMU2ASS, DANMAKU_USE_DANMAKUFACTORY
+        DANMAKU_USE_DANMU2ASS = section.getboolean('use_danmu2ass', DANMAKU_USE_DANMU2ASS)
+        DANMAKU_USE_DANMAKUFACTORY = section.getboolean('use_danmakufactory', DANMAKU_USE_DANMAKUFACTORY)
+        DANMAKU_EXEC = section.get('exec', DANMAKU_EXEC)
         DANMAKU_SPEED = section.getfloat('speed', DANMAKU_SPEED)
-        DEFAULT_FONT_NAME = section.get('font', DEFAULT_FONT_NAME)
+        DANMAKU_FONT_NAME = section.get('font', DANMAKU_FONT_NAME)
         DANMAKU_FONT_SIZE = section.getint('font_size', DANMAKU_FONT_SIZE)
         VIDEO_RESOLUTION = section.get('resolution', VIDEO_RESOLUTION)
     if config.has_section("video"):
@@ -88,11 +97,12 @@ def load_config():
     if config.has_section("ffmpeg"):
         section = config['ffmpeg']
         global FFMPEG_EXEC, FFMPEG_USE_HEVC, FFMPEG_USE_NVIDIA_GPU, FFMPEG_USE_INTEL_GPU, VIDEO_BITRATE, VIDEO_CRF, \
-            VIDEO_GOP
+            VIDEO_GOP, FFMPEG_USE_VAAPI
         FFMPEG_EXEC = section.get('exec', FFMPEG_EXEC)
         FFMPEG_USE_HEVC = section.getboolean('hevc', FFMPEG_USE_HEVC)
         FFMPEG_USE_NVIDIA_GPU = section.getboolean('nvidia_gpu', FFMPEG_USE_NVIDIA_GPU)
         FFMPEG_USE_INTEL_GPU = section.getboolean('intel_gpu', FFMPEG_USE_INTEL_GPU)
+        FFMPEG_USE_VAAPI = section.getboolean('vaapi', FFMPEG_USE_VAAPI)
         VIDEO_BITRATE = section.get('bitrate', VIDEO_BITRATE)
         VIDEO_CRF = section.getfloat('crf', VIDEO_CRF)
         VIDEO_GOP = section.getfloat('gop', VIDEO_GOP)
@@ -108,9 +118,11 @@ def load_config():
 def get_config():
     config = {
         'danmaku': {
-            'exec': DANMAKU_FACTORY_EXEC,
+            'exec': DANMAKU_EXEC,
+            'use_danmu2ass': DANMAKU_USE_DANMU2ASS,
+            'use_danmakufactory': DANMAKU_USE_DANMAKUFACTORY,
             'speed': DANMAKU_SPEED,
-            'font': DEFAULT_FONT_NAME,
+            'font': DANMAKU_FONT_NAME,
             'font_size': DANMAKU_FONT_SIZE,
             'resolution': VIDEO_RESOLUTION,
         },
@@ -130,6 +142,7 @@ def get_config():
             'hevc': FFMPEG_USE_HEVC,
             'nvidia_gpu': FFMPEG_USE_NVIDIA_GPU,
             'intel_gpu': FFMPEG_USE_INTEL_GPU,
+            'vaapi': FFMPEG_USE_VAAPI,
             'bitrate': VIDEO_BITRATE,
             'crf': VIDEO_CRF,
             'gop': VIDEO_GOP,
diff --git a/controller/api/collector_blueprint.py b/controller/api/collector_blueprint.py
index 98aee72..cec02b3 100644
--- a/controller/api/collector_blueprint.py
+++ b/controller/api/collector_blueprint.py
@@ -4,7 +4,7 @@ import platform
 import psutil
 from flask import Blueprint, jsonify
 
-from config import DANMAKU_FACTORY_EXEC, FFMPEG_EXEC, BILILIVE_RECORDER_DIRECTORY, XIGUALIVE_RECORDER_DIRECTORY, VIDEO_OUTPUT_DIR
+from config import DANMAKU_EXEC, FFMPEG_EXEC, BILILIVE_RECORDER_DIRECTORY, XIGUALIVE_RECORDER_DIRECTORY, VIDEO_OUTPUT_DIR
 from util.system import check_exec
 from workflow.bilibili import IS_LIVING, IS_UPLOADING
 
@@ -60,7 +60,7 @@ def collect_basic_status():
         },
         'exec': {
             'ffmpeg': check_exec(FFMPEG_EXEC),
-            'danmaku': check_exec(DANMAKU_FACTORY_EXEC),
+            'danmaku': check_exec(DANMAKU_EXEC),
         },
         'system': {
             'os': platform.system(),
diff --git a/templates/index.html b/templates/index.html
index 1b92eb7..db17265 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -84,6 +84,10 @@
             <td>HEVC</td>
             <td :class="{warning: !config.ffmpeg.hevc, success: config.ffmpeg.hevc}"></td>
         </tr>
+        <tr>
+            <td>VAAPI</td>
+            <td :class="{warning: !config.ffmpeg.vaapi, success: config.ffmpeg.vaapi}"></td>
+        </tr>
         <tr>
             <td>嘤伟达GPU</td>
             <td :class="{warning: !config.ffmpeg.nvidia_gpu, success: config.ffmpeg.nvidia_gpu}"></td>
@@ -117,6 +121,14 @@
             <td>命令</td>
             <td>{{ config.danmaku.exec }}</td>
         </tr>
+        <tr>
+            <td>DANMU2ASS</td>
+            <td :class="{warning: !config.danmaku.use_danmu2ass, success: config.danmaku.use_danmu2ass}"></td>
+        </tr>
+        <tr>
+            <td>DanmakuFactory</td>
+            <td :class="{warning: !config.danmaku.use_danmakufactory, success: config.danmaku.use_danmakufactory}"></td>
+        </tr>
         <tr>
             <td>滚动速度</td>
             <td>{{ config.danmaku.speed }}</td>
@@ -285,6 +297,8 @@
                 config: {
                     danmaku: {
                         exec: "",
+                        use_danmu2ass: false,
+                        use_danmakufactory: false,
                         speed: 0,
                         font: "",
                         font_size: 0,
@@ -306,6 +320,7 @@
                         hevc: false,
                         nvidia_gpu: false,
                         intel_gpu: false,
+                        vaapi: false,
                         bitrate: "",
                         crf: "",
                         gop: "",
diff --git a/workflow/danmaku.py b/workflow/danmaku.py
index 80bec32..6f5e8b8 100644
--- a/workflow/danmaku.py
+++ b/workflow/danmaku.py
@@ -7,7 +7,8 @@ from typing import Union
 
 from bs4 import BeautifulSoup
 
-from config import DANMAKU_FACTORY_EXEC, VIDEO_RESOLUTION, DANMAKU_SPEED, DEFAULT_FONT_NAME, DANMAKU_FONT_SIZE
+from config import DANMAKU_EXEC, VIDEO_RESOLUTION, DANMAKU_SPEED, DANMAKU_FONT_NAME, DANMAKU_FONT_SIZE, \
+    DANMAKU_USE_DANMU2ASS, DANMAKU_USE_DANMAKUFACTORY
 from exception.danmaku import NoDanmakuException, DanmakuFormatErrorException
 from util.file import check_file_exist
 
@@ -34,15 +35,33 @@ def diff_danmaku_files(base_file: Union[os.PathLike[str], str], file: Union[os.P
 
 def danmaku_to_subtitle(file: Union[os.PathLike[str], str], time_shift: float):
     new_subtitle_name = md5(file.encode("utf-8")).hexdigest() + ".ass"
-    process = subprocess.Popen((
-        DANMAKU_FACTORY_EXEC, "--ignore-warnings",
+    if DANMAKU_USE_DANMAKUFACTORY:
+        process = danmaku_to_subtitle_use_danmaku_factory(file, time_shift, new_subtitle_name)
+    elif DANMAKU_USE_DANMU2ASS:
+        process = danmaku_to_subtitle_use_danmu2ass(file, time_shift, new_subtitle_name)
+    else:
+        return
+    process.wait()
+    return new_subtitle_name
+
+
+def danmaku_to_subtitle_use_danmaku_factory(file: Union[os.PathLike[str], str], time_shift: float, new_subtitle_name: str):
+    return subprocess.Popen((
+        DANMAKU_EXEC, "--ignore-warnings",
         "-r", str(VIDEO_RESOLUTION), "-s", str(DANMAKU_SPEED), "-f", "5",
-        "-S", str(DANMAKU_FONT_SIZE), "-N", str(DEFAULT_FONT_NAME), "--showmsgbox", "FALSE",
+        "-S", str(DANMAKU_FONT_SIZE), "-N", str(DANMAKU_FONT_NAME), "--showmsgbox", "FALSE",
         "-O", "255", "-L", "1", "-D", "0",
         "-o", "ass", new_subtitle_name, "-i", file, "-t", str(time_shift)
     ))
-    process.wait()
-    return new_subtitle_name
+
+
+def danmaku_to_subtitle_use_danmu2ass(file: Union[os.PathLike[str], str], time_shift: float, new_subtitle_name: str):
+    (_w, _h) = VIDEO_RESOLUTION.split("x")
+    return subprocess.Popen((
+        DANMAKU_EXEC, "--force", "-a", "1", "-d", str(DANMAKU_SPEED), "--font", str(DANMAKU_FONT_NAME),
+        "--font-size", str(DANMAKU_FONT_SIZE), "--lane-size", str(DANMAKU_FONT_SIZE), "--width", _w, "--height", _h,
+        "-o", new_subtitle_name, "-p", "1", "--time-offset", str(time_shift), "--width-ratio", "1", file
+    ))
 
 
 if __name__ == '__main__':
diff --git a/workflow/video.py b/workflow/video.py
index ef6cba6..561acda 100644
--- a/workflow/video.py
+++ b/workflow/video.py
@@ -3,8 +3,9 @@ import subprocess
 from datetime import datetime, timedelta
 from typing import IO
 
-from config import FFMPEG_EXEC, FFMPEG_USE_HEVC, VIDEO_BITRATE, FFMPEG_USE_NVIDIA_GPU, VIDEO_CLIP_EACH_SEC, VIDEO_CLIP_OVERFLOW_SEC, \
-    FFMPEG_USE_INTEL_GPU, VIDEO_OUTPUT_DIR, VIDEO_CRF, VIDEO_GOP
+from config import FFMPEG_EXEC, FFMPEG_USE_HEVC, VIDEO_BITRATE, FFMPEG_USE_NVIDIA_GPU, VIDEO_CLIP_EACH_SEC, \
+    VIDEO_CLIP_OVERFLOW_SEC, \
+    FFMPEG_USE_INTEL_GPU, VIDEO_OUTPUT_DIR, VIDEO_CRF, VIDEO_GOP, FFMPEG_USE_VAAPI
 from . import LOGGER
 
 
@@ -29,6 +30,8 @@ def encode_video_with_subtitles(orig_filename: str, subtitles: list[str], base_t
     if FFMPEG_USE_HEVC:
         if FFMPEG_USE_NVIDIA_GPU:
             process = get_encode_hevc_process_use_nvenc(orig_filename, subtitles, new_fullpath)
+        elif FFMPEG_USE_VAAPI:
+            process = get_encode_hevc_process_use_vaapi(orig_filename, subtitles, new_fullpath)
         elif FFMPEG_USE_INTEL_GPU:
             process = get_encode_hevc_process_use_intel(orig_filename, subtitles, new_fullpath)
         else:
@@ -36,6 +39,8 @@ def encode_video_with_subtitles(orig_filename: str, subtitles: list[str], base_t
     else:
         if FFMPEG_USE_NVIDIA_GPU:
             process = get_encode_process_use_nvenc(orig_filename, subtitles, new_fullpath)
+        elif FFMPEG_USE_VAAPI:
+            process = get_encode_process_use_vaapi(orig_filename, subtitles, new_fullpath)
         elif FFMPEG_USE_INTEL_GPU:
             process = get_encode_process_use_intel(orig_filename, subtitles, new_fullpath)
         else:
@@ -73,6 +78,20 @@ def get_encode_process_use_intel(orig_filename: str, subtitles: list[str], new_f
     return encode_process
 
 
+def get_encode_process_use_vaapi(orig_filename: str, subtitles: list[str], new_filename: str):
+    print("[+]Use VAAPI Acceleration")
+    encode_process = subprocess.Popen([
+        FFMPEG_EXEC, *_common_ffmpeg_setting(),
+        "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi", "-i", orig_filename, "-vf",
+        ",".join("subtitles=%s" % i for i in subtitles),
+        "-c:v", "h264_vaapi",
+        *_common_ffmpeg_params(),
+        # "-t", "10",
+        new_filename
+    ], stdout=subprocess.PIPE)
+    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([
@@ -101,6 +120,20 @@ def get_encode_hevc_process_use_nvenc(orig_filename: str, subtitles: list[str],
     return encode_process
 
 
+def get_encode_hevc_process_use_vaapi(orig_filename: str, subtitles: list[str], new_filename: str):
+    print("[+]Use VAAPI Acceleration")
+    encode_process = subprocess.Popen([
+        FFMPEG_EXEC, *_common_ffmpeg_setting(),
+        "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi", "-i", orig_filename, "-vf",
+        ",".join("subtitles=%s" % i for i in subtitles),
+        "-c:v", "hevc_vaapi",
+        *_common_ffmpeg_params(),
+        # "-t", "10",
+        new_filename
+    ], stdout=subprocess.PIPE)
+    return encode_process
+
+
 def get_encode_hevc_process_use_intel(orig_filename: str, subtitles: list[str], new_filename: str):
     print("[+]Use Intel QSV Acceleration")
     encode_process = subprocess.Popen([