编辑录播视频
基于录播素材构建片段的食谱。它们嵌入在 montagent 循环的构建步骤中;如果尚未加载 montagent,请先加载。对于人物访谈、演示文稿片段或采访剪辑,请在规划前打开 人物访谈结构。
从素材开始
在编写元素之前,对每个剪辑执行 probe:获取其尺寸、帧率、长度以及是否包含音频。然后根据语音规划整个片段。语音的时间戳是每个字幕、提示和避让效果的时钟基准,因此需要首先检查时间戳(请参阅 字幕与逐词高亮)。
要从长剪辑中选择一个特定时刻(例如,在插播片段中展示屏幕录制的一部分),请将该剪辑单独放入一个临时项目中,并调用 frame --from 0 --to <其长度> --infill-ceiling <毫秒>,其中 ceiling 设置为长度的二十分之一。一次调用会每隔几秒生成一个带标签的图块;对任何你希望更仔细检查的图块使用 frame --at。
食谱
每个食谱提供起始范围,而非最终答案。每个食谱以 检查步骤 结束:加载 montagent-craft,然后在继续之前按照指示检查。
放置素材
{
"frame": {"width": 1080, "height": 1920}, "fps": 30, "background": "#FF5A36", "duration": 6000, "output": "out/presenter.mp4",
"tracks": [
{"name": "presenter", "layer": 10, "elements": [
{"id": "presenter", "type": "video", "start": 0, "end": 6000, "source": "media/clip.mp4", "source_start": 0, "source_end": 6000, "x": 540, "y": 2160, "origin": "bottom-center", "width": 3413, "height": 1920, "fit": "cover", "effects": [{"name": "chroma", "color": "#00FF00", "tolerance": 0.2, "softness": 0.08, "spill": 0.9}]}
]}
]
}
- 宽素材在竖屏画框中: 使元素高度等于画框高度,宽度根据素材宽高比确定(对于 16:9 的素材,1920 高度对应 3413 宽度),然后水平移动元素以保持演讲者居中。
- 眼睛位置: 位于画框从上往下的 25–35% 处。将元素的
bottom-center移至画框底部边缘以下即可实现;腿部被裁剪是正常现象。 - 头部空间: 头部上方保留画框高度的 4–8%。
- 比片段短的镜头:
"overrun": "hold"会冻结最后一帧。保持冻结时间不超过半秒,或者提前结束片段。 - 剪辑内的冻结帧、速度斜坡或倒放: 使用
source_time替代source_start和source_end:一个数值会冻结某一时刻,而将源毫秒时间与时间线t配对的键值对则可实现速度变化(平坦的一对冻结,下降的一对倒放)。对于慢动作 (speed< 1.0) 或速度斜坡 (source_time) 的剪辑,设置"frame_blend": "mix"以在连续源帧之间进行交叉溶解,而不是在最近帧之间跳转 ("none")。要对素材实现抽帧(定格动画)效果,请使用阶梯式的source_time关键帧,使每个目标源时间戳在多个时间线帧上保持。使用source_time的剪辑在volume为 0 时静音,因此请将其音频放在独立的audio元素上。 - 镜头的自带音频: 语音音量保持
volume1.0。其他所有声音都混合在它下面。 - 检查: 在开始、中间和最后一帧处执行
frame,检查眼睛是否位于指定区间内,以及画框两侧没有重要内容被裁剪。
绿幕抠像
对素材元素应用 chroma 效果即可进行抠像。起始参数如下:
color: 屏幕的纯色,绿色屏幕为#00FF00。稍微偏蓝或偏黄的照明屏幕在相同容差下也能正常抠像,因此无需取样。tolerance: 0.2(范围 0.18–0.25)。高于约 0.3 时,抠像会吃掉黑色头发、白色衣领和深色衣物。softness: 0.08(范围 0.05–0.1)。spill: 0.9(范围 0.9–1.0)。
在叠加任何其他内容之前进行检查:
- 三次无损裁剪。 在头发、肩膀或袖口边缘、以及手部位置,对将用作背景的区域执行
frame --crop <x,y,w,h> --png。裁剪结果以真实比例返回,因此 200–400 像素的框就足够了。 - 已经完成了? 如果头发、衣领和手部区域实心,边缘最多有 1–2 像素的深色、橄榄色或棕褐色线条:抠像已完成,继续下一步。那条线是 边缘底线:从 0.12 到 0.3 的每个设置都会留下它,而
spill已将其从绿色变为棕褐色,因此在其上读取的绿色像素计数为零。 - 否则,每个缺陷进行一次修复。 背景透过头发的缝隙显示出来、衣领或深色衣物边缘有绿色泄漏:将
tolerance降低 0.05。出现比边缘底线更宽的苍白或绿色光晕:将tolerance提高 0.03,或将softness提高 0.03。再次裁剪并返回步骤 2。最多在同一个抠像元素上调整三次设置:按区域分割抠像副本会成倍增加渲染时间,并且仍然会留下接缝。 - 漂移。 对抠像元素执行
measure。其不透明部分在整段镜头中应保持在 1-2 个百分点内。出现阶梯变化意味着光照在此处发生了变化:在阶梯前后为tolerance添加关键帧,或者在该帧处切割元素并分别抠像每个部分。
- 检查: 在演讲者运动幅度最大的两个时刻执行
frame检查整体画面,确保轮廓保持清晰。
字幕与逐词高亮
字幕来自提供的词语时间戳,通过内置脚本自动生成,而非手动添加。python3 scripts/captions.py <project> <words> <spec>(使用 --help 查看 spec 参数)会检查时间戳,使用 measure 将词语分页为一行或两行,为每个口语词分配一个 highlight 窗口,并打印出添加了字幕的项目。它会将每项修复报告输出到 stderr。阅读该报告:这就是时间戳检查。
- 检查内容: 在语音已经开始后才开始短语的词语(典型的例子是提供的 "Now" 晚了 110 毫秒且持续了 20 毫秒)、位于静音段内的词语、重叠的词语以及太短而无法看到的词语。短词语会被延长至
min_word(150 ms):首先占用其后的停顿,然后占用下一个词语的时间。报告中出现位于静音段内的词语意味着该文件在此处对齐错误:修复文件,然后重新运行。 - 尺寸: 在 1080 宽的画框中为 56–72 px,粗体,使用一种浅色表示整行文字,使用品牌的强调色表示正在说的词。
- 行数: 最多两行,
width约为画框宽度的 80%。在句子结束或停顿 400 毫秒或以上处分页。 - 背景: 使用品牌最深色的
pill样式,透明度为 80–85% (CC–D9),可在任何素材上清晰阅读。pad横向 28–36 px,纵向 14–20 px,radius20–28。 - 放置位置: 下巴下方,并避开竖屏画框底部 15–20% 的区域(这是播放器控件区域)。在 1080×1920 画框中,中心大约在
y1350–1450 处。在宽屏画框中,将其居中放置在距离底部 10–14% 的位置。 - 页面 保持显示直到下一页开始,或者在较长的停顿后,在最后一个词语结束后再保持 1 秒。
它会生成真实的字幕,因此请根据 validate 对字幕提出的发现意见采取行动。
- 检查: 在一页内容上执行
frame --from --to:每个高亮的词语都会生成一个单独的图块。检查每个图块是否按说话顺序恰好高亮一个词语,并且框体保持远离面部。然后,在最长的停顿后的第一帧执行frame --at,检查新页面是否已显示且没有词语被提前高亮。
素材上的名称条
一个从边缘划入划出的下三分之一标题栏。由于素材不是纯色,所以来自 montagent-motion 的遮盖划变无法直接用来揭示标题栏本身。相反,标题栏 从边缘生长出来,其文本由一层 与标题栏颜色相同(纯色)的遮盖层来揭示。
{
"frame": {"width": 1080, "height": 1920}, "fps": 30, "background": "#6E7B8B", "duration": 7000, "output": "out/namebar.mp4",
"fonts": {"bold": [{"file": "fonts/Inter-Bold.ttf"}]},
"fontVendor": {"fonts/Inter-Bold.ttf": {"licence": "OFL-1.1", "source": "https://github.com/rsms/inter", "sha256": "288316099b1e0a47a4716d159098005eef7c0066921f34e3200393dbdb01947f"}},
"tracks": [
{"name": "bar", "layer": 20, "elements": [
{"id": "bar", "type": "rect", "start": 1000, "end": 6534, "x": 64, "y": 1160, "origin": "center-left", "width": 760, "height": 160, "fill": "#101418", "radius": 8, "scale": [{"t": 1000, "v": [0.0, 1.0]}, {"t": 1300, "v": [1.0, 1.0], "ease": [0.65, 0, 0.35, 1]}, {"t": 6266, "v": [1.0, 1.0], "ease": "linear"}, {"t": 6533, "v": [0.0, 1.0], "ease": [0.65, 0, 0.35, 1]}]}
]},
{"name": "bar-edge", "layer": 21, "elements": [
{"id": "bar-edge", "type": "rect", "start": 1000, "end": 6534, "x": 64, "y": 1160, "origin": "center-left", "width": 12, "height": 160, "fill": "#FF5A36"}
]},
{"name": "bar-name", "layer": 22, "elements": [
{"id": "bar-name", "type": "text", "start": 1300, "end": 6266, "x": 108, "y": 1128, "origin": "center-left", "width": 400, "height": 70, "font": "bold", "size": 58, "color": "#F5F0E6", "runs": [{"text": "Montagent"}], "caption": false}
]},
{"name": "bar-line", "layer": 22, "elements": [
{"id": "bar-line", "type": "text", "start": 1300, "end": 6266, "x": 108, "y": 1196, "origin": "center-left", "width": 560, "height": 44, "font": "bold", "size": 34, "color": "#F5F0E6", "runs": [{"text": "Video your agent can read"}], "caption": false}
]},
{"name": "bar-cover", "layer": 23, "elements": [
{"id": "bar-cover-on", "type": "rect", "start": 1300, "end": 1700, "x": 800, "y": 1160, "origin": "center-right", "width": 716, "height": 140, "fill": "#101418", "scale": [{"t": 1300, "v": [1.0, 1.0]}, {"t": 1666, "v": [0.0, 1.0], "ease": [0.65, 0, 0.35, 1]}]},
{"id": "bar-cover-off", "type": "rect", "start": 6000, "end": 6266, "x": 84, "y": 1160, "origin": "center-left", "width": 716, "height": 140, "fill": "#101418", "scale": [{"t": 6000, "v": [0.0, 1.0]}, {"t": 6233, "v": [1.0, 1.0], "ease": [0.65, 0, 0.35, 1]}]}
]}
]
}
- 进入: 标题栏在 250–350 毫秒内从其左边缘生长出来,缓动曲线
[0.65, 0, 0.35, 1]。文本及其遮盖层在标题栏完全展开的同一帧开始(绝不能更早,否则遮盖层会显示在素材上)。遮盖层在其远边缘处收缩,持续 300–400 毫秒,按阅读顺序揭示文本。 - 退出, 从提示点开始:遮盖层在 200–300 毫秒内重新覆盖文本,然后标题栏在 250–300 毫秒内收缩回其边缘。整个标题栏在提示点后 450–600 毫秒内完全消失。
- 尺寸: 在 1080 宽的画框中,标题栏高度 130–180 px,名称字号 52–64 px,第二行字号 30–38 px。前缘有一条 8–16 px 宽的强调色条纹。
- 遮盖层: 颜色必须与标题栏的
fill完全一致,边缘距离标题栏两端 20 px、上下 10 px,位于文本上方。 - 放置: 距离画框侧边 48–80 px,与字幕框和演讲者面部保持距离,通常在竖屏画框高度的 55–65% 处。
- 检查: 从每个提示点开始,在 1 秒内执行
frame --from --to --keyframes,检查在任何图块上遮盖层都没有超出标题栏边界。在标题栏静止时执行frame --at,检查其左边缘是否与字幕框的边距对齐。
圆形画中画
在一个圆形中显示第二个剪辑:一个带有圆形 mask 的视频元素、一个背景环以及一个带有过冲贝塞尔曲线的弹出动画。
{
"frame": {"width": 1080, "height": 1920}, "fps": 30, "background": "#6E7B8B", "duration": 6000, "output": "out/pip.mp4",
"tracks": [
{"name": "pip-ring", "layer": 30, "elements": [
{"id": "pip-ring", "type": "ellipse", "start": 1000, "end": 5500, "x": 800, "y": 900, "origin": "center", "width": 384, "height": 384, "fill": "#F5F0E6", "scale": [{"t": 1000, "v": [0.0, 0.0]}, {"t": 1400, "v": [1.0, 1.0], "ease": [0.34, 1.56, 0.64, 1]}, {"t": 5233, "v": [1.0, 1.0], "ease": "linear"}, {"t": 5466, "v": [0.0, 0.0], "ease": "ease-in"}], "effects": [{"name": "shadow", "dx": 0, "dy": 16, "radius": 40, "color": "#101418", "opacity": 0.35}]}
]},
{"name": "pip", "layer": 31, "elements": [
{"id": "pip", "type": "video", "start": 1000, "end": 5500, "source": "media/clip.mp4", "source_start": 10000, "source_end": 14500, "volume": 0, "x": 800, "y": 900, "origin": "center", "width": 640, "height": 360, "fit": "contain", "scale": [{"t": 1000, "v": [0.0, 0.0]}, {"t": 1400, "v": [1.0, 1.0], "ease": [0.34, 1.56, 0.64, 1]}, {"t": 5233, "v": [1.0, 1.0], "ease": "linear"}, {"t": 5466, "v": [0.0, 0.0], "ease": "ease-in"}], "effects": [{"name": "mask", "shape": "circle", "x": 140, "y": 0, "width": 360, "height": 360}]}
]}
]
}
- 圆形的矩形包裹盒: 一个与元素高度相等的正方形。其
x值决定显示画面的哪一部分。居中位置是(width - height) / 2,并且应该使用这个值:弹出动画围绕元素的origin进行缩放,因此偏离中心的圆形会在放大时产生侧向漂移。要显示画面的其他部分,请选择一个使该部分位于中间位置的source_start,或者增大元素的尺寸并设置其x值,使该部分落在圆形内,然后按照弹出曲线为x设置关键帧,以使圆心保持静止。 - 尺寸: 宽度约为画框宽度的 30–40%。背景环比圆形宽 12–24 px,使用背景中最浅的颜色,并应用柔和的
shadow。 - 弹出动画: 两者都在 350–450 毫秒内使用缓动曲线
[0.34, 1.56, 0.64, 1],从 scale 0 开始,在相同的关键帧上完成。退出动画在 200–300 毫秒内,使用ease-in,回到 scale 0。 - 时机: 在演讲者首次提到所展示内容时进入,在结束前一个节拍处退出,以确保关闭干净利落。
- 放置: 放在演讲者旁边,胸部高度,远离面部和字幕框,距离画框边缘 40 像素或更多。
- 音频:
volume设置为 0,除非剪辑自身的声音是重点。 - 检查: 在弹出动画峰值时(开始后加上其长度的 40%)执行
frame,检查圆形是否远离面部和画框边缘。在静止时执行frame,检查重要部分是否在圆形内部。
语音避让背景音乐
音乐在语音说话时自动降低音量,在停顿期间恢复。scripts/duck.py 脚本会根据语音的词语时间戳(--words,一个 JSON 文件)将普通的 volume 关键帧写入音乐元素。
-
关键帧写入 之前 和 之后 的混合时间。在语音开始前 100–200 毫秒开始降低音量,在语音结束后 100–200 毫秒开始恢复。这样切换听起来很自然。
-
语音进行中的音量:音乐音量保持在一个较低的水平(
volume0.15–0.25),以免分散注意力。 -
如果音乐是节奏感强的类型,在语音间隙音量可以稍微恢复(0.4–0.6)。
-
如何使用:
python3 scripts/duck.py --project <project> --words <words.json> --music-volume 0.2 --pause-volume 0.4 --attack 100 --release 200。运行后检查生成的volume关键帧是否符合预期。 -
检查: 播放片段,感受音乐的避让效果是否自然,音量变化是否平滑。确保音乐在最响亮的时刻也不会盖过语音。
检查步骤:最终审查
在所有效果都应用后,执行一个完整的审查:
- 从头到尾播放整个片段,注意字幕、标题栏和画中画的时机。
- 检查绿幕抠像是否有闪烁或边缘问题。
- 验证语音避让是否在所有需要的地方都起作用。
- 最后,输出最终版本。
提示:montagent 循环中的构建步骤会处理所有这些食谱。根据你的具体素材微调参数。