Skip to content
AstroPaper
Go back

Clip DJI Action4 Video by FFMPEG

Updated:
Edit page

如果Cyclemetry和Action 4 的视频帧率不相同,可以合并吗?并且如何实现智能剪辑,比如骑行视频中遇到红灯停车或者休息的部分自动加速,并且有高光瞬间?

Table of contents

Open Table of contents

Intro

一、 帧率不相同可以合并吗?

要点概括可以合并,但强烈建议在合并前将帧率统一,以防止音画不同步或视觉微卡顿。

原理分析: 当两个视频源(如原视频 60fps,数据图层 30fps)进入 FFmpeg 进行图层叠加(overlay)时,FFmpeg 默认会以底层背景视频(即你的原片 1080p60)的主时间轴为基准。


二、 智能剪辑:自动加速停顿与高光检测

要实现“遇到红灯自动加速”以及“识别高光瞬间”的智能剪辑,由于你的视频没有任何剪辑软件的标记,最硬核且精准的极客做法是:不依赖画面图像识别,而是直接利用我们之前已经生成的“超级富数据 TCX 文件”作为数据驱动源(Data-Driven Editing)

💡 算法实现逻辑:

  1. 自动加速(停顿检测):通过 Python 解析 TCX 文件的 TimeSpeed 字段。当速度低于 $3\text{ km/h}$ 超过 5 秒时(红灯或休息),记录该时间段的起始点。
  2. 高光瞬间检测:通过算法筛选出两类黄金瞬时数据:
  1. 动态时间线切片(Dynamic Slicing):Python 脚本在计算出这些时间节点后,自动将原视频切成若干片段:普通骑行(1倍速)、红灯停顿(8倍速/16倍速快进)、高光瞬间(保持1倍速并增加视觉高亮或慢动作),最后由 FFmpeg 拼接成一个成品。

💻 终极极客脚本:数据驱动型智能变速合并工具

这段完整的 Python 脚本集成了帧率强制统一(60fps)10-Bit 色彩保护M4 硬件加速以及自动红灯加速算法。它会扫描你的 TCX 数据,自动生成变速策略并一键压制成片。

import os
import subprocess
import xml.etree.ElementTree as ET
from datetime import datetime

# ==================== 智能剪辑策略配置 ====================
TARGET_FPS = 60          # 强行统一输出帧率为 60fps,确保绝对丝滑
STOP_SPEED_THRESHOLD = 0.83  # 停顿速度阈值,低于 0.83 m/s (约 3 km/h) 判定为停顿休息
IDLE_ACCEL_FACTOR = 8.0   # 遇到红灯/休息部分的快进倍速 (如 8 倍速快进)
NORMAL_SPEED_FACTOR = 1.0 # 正常骑行倍速
# ========================================================

def parse_tcx_timeline(tcx_path):
    """解析 TCX 文件,提取每秒的时间戳和速度,判定每个时间点的速度状态"""
    ns = {'tcx': 'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2'}
    tree = ET.parse(tcx_path)
    root = tree.getroot()
    trackpoints = root.findall('.//tcx:Trackpoint', ns)
    
    timeline = []
    start_time = None
    
    for i, tp in enumerate(trackpoints):
        time_str = tp.find('tcx:Time', ns).text
        dt = datetime.fromisoformat(time_str.replace('Z', '+00:00'))
        
        if i == 0:
            start_time = dt
            speed = 0.0
        else:
            # 优先读取 TCX 中已有的速度标签,若无则可通过位置动态计算
            # 这里简化为读取上一节注入或手环自带的速度
            speed_node = tp.find('.//tcx:Extensions//*{http://www.garmin.com/xmlschemas/ActivityExtension/v2}Speed', ns)
            speed = float(speed_node.text) if speed_node is not None else 5.0 
            
        relative_sec = (dt - start_time).total_seconds()
        timeline.append({'sec': relative_sec, 'speed': speed})
        
    return timeline

def build_filter_complex(timeline, total_duration):
    """
    极客核心:根据数据时间轴,动态生成 FFmpeg 复杂的剪辑状态机滤镜字符串。
    将视频切分成若干区间,不同区间应用不同的 setpts (变速) 逻辑。
    """
    video_segments = []
    audio_segments = []
    
    current_state = "NORMAL" # NORMAL 或 IDLE
    segment_start = 0.0
    
    # 简单状态机:合并连续的停顿或骑行区间
    intervals = []
    for point in timeline:
        sec = point['sec']
        if sec >= total_duration: break
            
        state = "IDLE" if point['speed'] < STOP_SPEED_THRESHOLD else "NORMAL"
        
        if state != current_state:
            intervals.append({'start': segment_start, 'end': sec, 'type': current_state})
            segment_start = sec
            current_state = state
    intervals.append({'start': segment_start, 'end': total_duration, 'type': current_state})
    
    # 构建 FFmpeg 复杂的 select/trim 滤镜图
    filter_str = ""
    v_maps = ""
    a_maps = ""
    
    for idx, inter in enumerate(intervals):
        start = inter['start']
        end = inter['end']
        factor = IDLE_ACCEL_FACTOR if inter['type'] == "IDLE" else NORMAL_SPEED_FACTOR
        pts_scale = 1.0 / factor
        
        # 对背景视频[0:v]和仪表盘[1:v]同时进行精准时间段裁剪(trim)与变速(setpts)
        filter_str += f"[0:v]trim=start={start}:end={end},setpts={pts_scale}*PTS[v_bg_{idx}];"
        filter_str += f"[1:v]trim=start={start}:end={end},setpts={pts_scale}*PTS[v_tl_{idx}];"
        # 两个变速后的图层立刻进行 overlay 融合
        filter_str += f"[v_bg_{idx}][v_tl_{idx}]overlay=0:0[v_mix_{idx}];"
        v_maps += f"[v_mix_{idx}]"
        
        # 音频同步裁剪与变调
        if factor == 1.0:
            filter_str += f"[0:a]atrim=start={start}:end={end},asetpts=PTS[a_mix_{idx}];"
        else:
            filter_str += f"[0:a]atrim=start={start}:end={end},asetpts=PTS,atempo={factor}[a_mix_{idx}];"
        a_maps += f"[a_mix_{idx}]"
        
    # 将所有揉好的片段按顺序拼接(concat)起来,形成单轨输出
    num_seg = len(intervals)
    filter_str += f"{v_maps}concat=n={num_seg}:v=1:a=0[outv];{a_maps}concat=n={num_seg}:v=0:a=1[outa]"
    
    return filter_str

def smart_compress_and_merge(bg_video, tl_video, tcx_path, output_video, total_duration, is_10bit=True):
    print("⚡ 正在解析 TCX 轨迹数据线...")
    timeline = parse_tcx_timeline(tcx_path)
    
    print("🛠️ 正在构建智能数据驱动型变速滤镜图 (FFmpeg Filter Graph)...")
    filter_complex_script = build_filter_complex(timeline, total_duration)
    
    cmd = [
        'ffmpeg',
        '-hwaccel', 'videotoolbox',  # M4 硬件解码
        '-i', bg_video,
        '-i', tl_video,
        '-filter_complex', filter_complex_script,
        '-map', '[outv]',
        '-map', '[outa]',
        '-r', str(TARGET_FPS),       # 💡 强制统一并输出 60 帧,解决帧率不一致引起的卡顿
        '-c:v', 'hevc_videotoolbox',
        '-b:v', '12M',               # 提升至 12M 应对快进画面带来的高码率需求
        '-maxrate', '18M',
        '-bufsize', '24M',
        '-tag:v', 'hvc1',
        '-c:a', 'aac', '-b:a', '192k'
    ]
    
    if is_10bit:
        cmd.extend(['-profile:v', 'main10', '-pix_fmt', 'yuv420p10le'])
        
    cmd.extend([output_video, '-y'])
    
    print("🎬 正在启动 M4 核心硬件流水线,执行智能切片、变速、图层融合压制...")
    subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
    print(f"🎉 智能短片制作成功!成品已保存至: {output_video}")

if __name__ == "__main__":
    # 实操时请换成你的真实文件名和原视频的精确总时长(秒)
    smart_compress_and_merge(
        bg_video="DJI_0011_D.MP4", 
        tl_video="cyclemetry_output.mov", 
        tcx_path="rich_telemetry_dashboard.tcx", 
        output_video="Smart_Ride_Vlog.mp4",
        total_duration=2400.0  # 假设视频长 40 分钟 (2400秒)
    )

💡 极客调教指南(关于高光的进阶打法):

上面的代码已经实现了完美统一帧率(解决帧率不同)红灯自动快进。如果你想在脚本中加入高光瞬间的慢动作或者视觉特效,你只需要在 build_filter_complex 函数的状态机里加一条分支:

  1. 寻找高光点:在 Python 遍历 timeline 时,找出 speed 或者是 power 最大那一秒。假设是第 600 秒。
  2. 切出高光区间:将 595 秒到 605 秒(高光前后共 10 秒)定义为 HIGHLIGHT 状态。
  3. 注入慢动作滤镜:在构建滤镜时,一旦判断为 HIGHLIGHT 状态,将 factor 设为 0.5(即慢动作)。同时将对应的视频 setpts 设为 2.0*PTS,音频 atempo 设为 0.5
  4. 最终视觉体验:这样压出来的视频,观众会看到你等红灯时画面以 8倍速疯狂快进,绿灯亮起瞬间恢复正常,而当你下坡冲刺到极速的那 10 秒钟,画面和冰蓝色的时速表指针会瞬间进入大片般的“2倍速慢动作延时”,极具视觉冲击力!

Edit page
Share this post:

Next Post
U探店闯关问答