diff --git a/biz/ffmpeg.py b/biz/ffmpeg.py
index 04c4235..ffebb4f 100644
--- a/biz/ffmpeg.py
+++ b/biz/ffmpeg.py
@@ -1,3 +1,5 @@
+import os.path
+
 from entity.ffmpeg import FfmpegTask
 import logging
 
@@ -10,32 +12,32 @@ def parse_ffmpeg_task(task_info, template_info):
     tasks = []
     # 中间片段
     for part in template_info.get("video_parts"):
-        sub_ffmpeg_task = parse_video_part(part, task_info)
-        if not sub_ffmpeg_task:
+        source = select_video_if_needed(part.get('source'), task_info, template_info)
+        if not source:
+            logger.warning("no video found for part: " + str(part))
             continue
+        sub_ffmpeg_task = FfmpegTask(source)
+        sub_ffmpeg_task.frame_rate = template_info.get("frame_rate", 25)
+        for lut in part.get('filters', []):
+            sub_ffmpeg_task.add_lut(os.path.join(template_info.get("local_path"), lut))
+        for audio in part.get('audios', []):
+            sub_ffmpeg_task.add_audios(os.path.join(template_info.get("local_path"), audio))
+        for overlay in part.get('overlays', []):
+            sub_ffmpeg_task.add_overlay(os.path.join(template_info.get("local_path"), overlay))
         tasks.append(sub_ffmpeg_task)
     task = FfmpegTask(tasks, output_file="test.mp4")
-    task.correct_task_type()
     overall = template_info.get("overall_template")
-    task.add_lut(*overall.get('luts', []))
-    task.add_audios(*overall.get('audios', []))
-    task.add_overlay(*overall.get('overlays', []))
+    task.frame_rate = template_info.get("frame_rate", 25)
+    for lut in overall.get('filters', []):
+        task.add_lut(os.path.join(template_info.get("local_path"), lut))
+    for audio in overall.get('audios', []):
+        task.add_audios(os.path.join(template_info.get("local_path"), audio))
+    for overlay in overall.get('overlays', []):
+        task.add_overlay(os.path.join(template_info.get("local_path"), overlay))
     return task
 
 
-def parse_video_part(video_part, task_info):
-    source = select_video_if_needed(video_part.get('source'), task_info)
-    if not source:
-        logger.warning("no video found for part: " + str(video_part))
-        return None
-    task = FfmpegTask(source)
-    task.add_lut(*video_part.get('luts', []))
-    task.add_audios(*video_part.get('audios', []))
-    task.add_overlay(*video_part.get('overlays', []))
-    return task
-
-
-def select_video_if_needed(source, task_info):
+def select_video_if_needed(source, task_info, template_info):
     if source.startswith('PLACEHOLDER_'):
         placeholder_id = source.replace('PLACEHOLDER_', '')
         new_sources = task_info.get('user_videos', {}).get(placeholder_id, [])
@@ -47,7 +49,7 @@ def select_video_if_needed(source, task_info):
                 # TODO: Random Pick / Policy Pick
                 new_sources = new_sources[0]
         return new_sources
-    return source
+    return os.path.join(template_info.get("local_path"), source)
 
 
 def start_ffmpeg_task(ffmpeg_task):
diff --git a/entity/ffmpeg.py b/entity/ffmpeg.py
index d5383fd..9419a3b 100644
--- a/entity/ffmpeg.py
+++ b/entity/ffmpeg.py
@@ -12,6 +12,7 @@ class FfmpegTask(object):
         self.output_file = output_file
         self.mute = True
         self.speed = 1
+        self.frame_rate = 25
         self.subtitles = []
         self.luts = []
         self.audios = []
@@ -36,7 +37,7 @@ class FfmpegTask(object):
         for i in self.input_file:
             if type(i) is str:
                 continue
-            elif type(i) is FfmpegTask:
+            elif isinstance(i, FfmpegTask):
                 if i.need_run():
                     yield i
 
@@ -69,7 +70,9 @@ class FfmpegTask(object):
 
     def get_output_file(self):
         if self.task_type == 'copy':
-            return self.input_file
+            return self.input_file[0]
+        if self.output_file == '':
+            self.set_output_file()
         return self.output_file
 
     def correct_task_type(self):
@@ -109,3 +112,103 @@ class FfmpegTask(object):
     def check_audio_track(self):
         if len(self.audios) > 0:
             self.mute = False
+
+    def get_ffmpeg_args(self):
+        args = ['-y', '-hide_banner']
+        video_output_str = "[0:v]"
+        if self.task_type == 'encode':
+            input_args = []
+            filter_args = []
+            output_args = []
+            video_output_str = "[0:v]"
+            audio_output_str = "[0:v]"
+            video_input_count = 0
+            audio_input_count = 0
+            for input_file in self.input_file:
+                input_args.append("-i")
+                if type(input_file) is str:
+                    input_args.append(input_file)
+                elif isinstance(input_file, FfmpegTask):
+                    input_args.append(input_file.get_output_file())
+            for lut in self.luts:
+                filter_args.append("[0:v]lut3d=file=" + lut + "[0:v]")
+            for overlay in self.overlays:
+                input_index = input_args.count("-i")
+                input_args.append("-i")
+                input_args.append(overlay)
+                filter_args.append(f"{video_output_str}[{input_index}:v]scale=rw:rh[v]")
+                filter_args.append(f"[v][{input_index}:v]overlay=1:eof_action=endall[v]")
+                video_output_str = "[v]"
+            output_args.append("-map")
+            output_args.append(video_output_str)
+            output_args.append("-r")
+            output_args.append(f"{self.frame_rate}")
+            if self.mute:
+                output_args.append("-an")
+            else:
+                input_index = 0
+                for audio in self.audios:
+                    input_index = input_args.count("-i")
+                    input_args.append("-i")
+                    input_args.append(audio.replace("\\", "/"))
+                    if audio_input_count > 0:
+                        filter_args.append(f"{audio_output_str}[{input_index}:a]amix[a]")
+                        audio_output_str = "[a]"
+                    else:
+                        audio_output_str = f"[{input_index}:a]"
+                    audio_input_count += 1
+                if audio_input_count == 1:
+                    audio_output_str = f"{input_index}"
+                output_args.append(f"-map")
+                output_args.append(audio_output_str)
+            return args + input_args + ["-filter_complex", ";".join(filter_args)] + output_args + [self.get_output_file()]
+        elif self.task_type == 'concat':
+            input_args = []
+            output_args = []
+            filter_args = []
+            video_output_str = "[0:v]"
+            audio_output_str = "[0:v]"
+            video_input_count = 0
+            audio_input_count = 0
+            for input_file in self.input_file:
+                input_index = input_args.count("-i")
+                input_args.append("-i")
+                if type(input_file) is str:
+                    input_args.append(input_file.replace("\\", "/"))
+                elif isinstance(input_file, FfmpegTask):
+                    input_args.append(input_file.get_output_file().replace("\\", "/"))
+                if video_input_count > 0:
+                    filter_args.append(f"{video_output_str}[{input_index}:v]concat=n=2:v=1:a=0[v]")
+                    video_output_str = "[v]"
+                else:
+                    video_output_str = f"[{input_index}:v]"
+                video_input_count += 1
+            output_args.append("-map")
+            output_args.append(video_output_str)
+            if self.mute:
+                output_args.append("-an")
+            else:
+                input_index = 0
+                for audio in self.audios:
+                    input_index = input_args.count("-i")
+                    input_args.append("-i")
+                    input_args.append(audio.replace("\\", "/"))
+                    if audio_input_count > 0:
+                        filter_args.append(f"{audio_output_str}[{input_index}:a]amix[a]")
+                        audio_output_str = "[a]"
+                    else:
+                        audio_output_str = f"[{input_index}:a]"
+                    audio_input_count += 1
+                if audio_input_count == 1:
+                    audio_output_str = f"{input_index}"
+                output_args.append(f"-map")
+                output_args.append(audio_output_str)
+            return args + input_args + ["-filter_complex", ";".join(filter_args)] +  output_args + [self.get_output_file()]
+
+    def set_output_file(self, file=None):
+        if file is None:
+            if self.output_file == '':
+                # TODO: Random Filename
+                self.output_file = "rand.mp4"
+        else:
+            self.output_file = file
\ No newline at end of file
diff --git a/util/ffmpeg.py b/util/ffmpeg.py
index e377b7a..678c164 100644
--- a/util/ffmpeg.py
+++ b/util/ffmpeg.py
@@ -1,3 +1,5 @@
+import os
+from datetime import datetime
 from typing import Optional, IO
 
 from entity.ffmpeg import FfmpegTask
@@ -5,6 +7,8 @@ from entity.ffmpeg import FfmpegTask
 
 def start_render(ffmpeg_task: FfmpegTask):
     print(ffmpeg_task)
+    print(ffmpeg_task.get_ffmpeg_args())
+    os.system("ffmpeg.exe "+" ".join(ffmpeg_task.get_ffmpeg_args()))
 
 def handle_ffmpeg_output(stdout: Optional[IO[bytes]]) -> str:
     out_time = "0:0:0.0"
@@ -24,4 +28,8 @@ def handle_ffmpeg_output(stdout: Optional[IO[bytes]]) -> str:
         if line.startswith(b"speed="):
             speed = line.replace(b"speed=", b"").decode().strip()
     print("[ ]Speed:", out_time, "@", speed)
-    return out_time
\ No newline at end of file
+    return out_time
+
+def duration_str_to_float(duration_str: str) -> float:
+    _duration = datetime.strptime(duration_str, "%H:%M:%S.%f") - datetime(1900, 1, 1)
+    return _duration.total_seconds()