/hyperframes-best-practices
當您需要將原始螢幕錄影、談話頭錄影或 OBS 輸出轉變為精緻的 HyperFrames 產品影片時,請使用此技能。它包含了兩個層次的知識:
- HyperFrames 基礎知識 — HeyGen 在
github.com/heygen-com/hyperframes維護的上游規則檔案(創作合成、GSAP 時間軸、data-*屬性、開發循環 CLI)。透過npx vskill i heygen-com/hyperframes安裝。 - 生產工藝規則 — 從為一個實際的 SpecWeave 產品影片(來自一個 8 分 47 秒的單一 OBS 錄影)進行的長期迭代循環中學到的經驗教訓。遵循這些規則能產生可實際發布的影片;違反它們則會導致影片出現嗶聲、詞語中間被剪斷、空白場景以及遮擋說話者面部鏡頭的疊加層。
在開始創作之前,請先閱讀這兩個層次。
範圍與適用性
以下第二層工藝規則是源自一個特定項目:將一個 8 分 47 秒的 SpecWeave 介紹錄影(Anton Abyzov 錄製,OBS 捕捉,談話頭 + 螢幕分享,2026 年 5 月)轉變為一個 6 分鐘、可投入生產的宣傳影片。 它們反映了該產品的外觀與風格 — Studio 的著色卡片美學、SpecWeave 的紫/綠色調、iOS 卡片疊加風格,以及 OBS 錄影的技術現實(開始/停止的滴答聲、錄影恢復時的瞬態噪音、因重複錄製同一行三次而產生的重複片段)。
確切的常數是項目特定的 — 根據特定面部鏡頭位置決定的疊加位置、
Boris Cherny / Anthropic的修正文字、600+/500+/3K+的統計數據 — 請進行轉換,不要直接複製。將此層次作為基礎技術的參考: 基於轉錄文本的智慧剪輯演算法、用於抑制瞬態噪音的每個片段淡入效果、避免連接混流器導致點擊聲的
concat濾鏡渲染管線、手術級靜音消除嗶聲的流程、iOS 卡片疊加模式、原始時間 → 剪輯時間的映射、Studio 波形快取失效的注意事項 — 這些都是可以通用的。
何時使用
當以下任何一種情況適用時,請觸發此技能:
- 用戶有原始的螢幕錄影或 OBS 錄影,並希望得到一個經過精簡、添加字幕和品牌化的影片。
- 來源素材包含空白時間、停頓的沉默,或重複的「讓我再試一次」片段。
- 來源素材在錄影邊界處有咔嗒聲、嗶聲或瞬態偽影。
- 用戶想要圖形疊加(標註、統計卡片、品牌揭示、工具標誌),且這些疊加不能遮擋說話者的面部鏡頭。
- 用戶說過「讓這個可以發布」或「準備好發布」這樣的話。
- 用戶提到想要「Apple 簡報風格」或「iOS 懸停卡片」或「毛玻璃效果」。
如果存在上述任何信號,請遵循此工作流程。如果用戶只想在一個乾淨的來源上添加一個簡單的標題卡,那麼僅使用上游的 hyperframes 技能就足夠了。
流水線(10 個階段)
┌──────────────────────────────────────────────────────────────────┐
│ 階段 0 盤點工具 │
│ 階段 1 探測來源(時長、編解碼器、音頻採樣率) │
│ 階段 2 使用 Whisper 進行轉錄(單詞級時間戳) │
│ 階段 3 檢測靜音 + 場景變化 │
│ 階段 4 構建智慧剪輯列表(包含單詞邊界緩衝區) │
│ 階段 5 清除瞬態嗶聲(手術級靜音 + adeclick) │
│ 階段 6 透過 concat 濾鏡渲染主版本(單次遍歷) │
│ 階段 7 生成英雄圖片(Gemini 3 Pro Image) │
│ 階段 8 創作 HyperFrames 合成(iOS 卡片) │
│ 階段 9 在 Studio 中進行 Lint 檢查 + 驗證 │
└──────────────────────────────────────────────────────────────────┘
請按順序執行各個階段。在一個階段內進行迭代,而不是跨階段迭代 — 一個糟糕的剪輯列表會破壞下游所有的疊加時序,您將需要花費一個小時重新解析錨點。首先確保剪輯列表正確,然後渲染一次,最後根據最終的時間軸構建疊加層。
階段 0 — 盤點工具
在開始之前,請確認以下工具可用。此技能假設在 macOS / Linux 上運行;在 Windows 上請使用 WSL。
| 工具 | 用途 | 安裝方式 |
|---|---|---|
ffmpeg (>= 6.0) | 所有影片/音頻處理 | brew install ffmpeg |
ffprobe | 串流檢查 | 與 ffmpeg 捆綁 |
whisper (OpenAI Python 套件) | 帶有詞語時間戳的轉錄 | pip install openai-whisper |
python3 | 剪輯列表 / 嗶聲清除腳本 | 系統自帶 |
npx hyperframes@latest | 合成 CLI(預覽、渲染、Lint) | npx hyperframes init <project> |
| Gemini 3 Pro Image API | 英雄圖片(可選) | 環境變數 GEMINI_API_KEY |
vskill (>= 1.0) | 技能安裝 | npm i -g vskill |
如果未安裝 Whisper,請運行 pip install openai-whisper 進行安裝。不要在 CPU 上使用 large-v3 或 large-v3-turbo — 它們處理 8 分鐘的音頻需要 10-15 分鐘。對於英文技術獨白,請使用 base.en:約需 30 秒,且對於詞語邊界來說足夠準確。
如果用戶的機器上安裝了 auto-editor,請忽略它 — 它可以進行靜音剪輯,但不能進行嗶聲清除或詞語邊界緩衝,並且會生成粗糙的、包含 50 多個片段的剪輯。我們手動操作會更好。
階段 1 — 探測來源
在進行任何其他操作之前,先了解檔案的形狀:
ffprobe -v error -show_entries stream=index,codec_type,codec_name,width,height,r_frame_rate,duration,sample_rate,channels \
-show_entries format=duration,size,bit_rate -of json "$SRC" | tee source_meta.json
您必須知道的事項:
- 總時長 — 驅動所有剪輯計算。
- 幀率 — OBS 通常以 60 fps 錄製;HyperFrames Studio 在內部以 30 fps 進行預覽。要計算 Studio 幀數,請將秒數乘以 30,而不是乘以來源的幀率。這經常會讓人犯錯。
- 音頻編解碼器與採樣率 — OBS 通常以 48 kHz 錄製
pcm_s24le。為交付而重新編碼為 AAC 192k;保留 48 kHz 以匹配所有下游工具。 - 容器 — DaVinci 輸出
.mov,OBS 輸出.mkv— 兩者都可以,ffmpeg 都能讀取。
將探測結果儲存到磁碟,以便後續階段可以重新讀取,而無需重新探測。
階段 2 — 使用 Whisper 進行轉錄
詞語級時間戳是基礎。沒有它們,您將無法:
- 將剪輯邊界對齊到詞語邊緣(避免在詞語中間剪斷)。
- 檢測重複片段(同一句話說了 2-3 次)。
- 將疊加層錨點對齊到語音時刻,而不是會漂移的剪輯時間點。
首先提取音頻,這樣 Whisper 就能獲得一個乾淨的單聲道 16 kHz WAV(與全品質相比,可節省 80% 的 CPU):
mkdir -p .work
ffmpeg -hide_banner -y -i "$SRC" -vn -ac 1 -ar 16000 -c:a pcm_s16le .work/source.wav
使用詞語時間戳和 JSON 輸出運行 Whisper:
whisper .work/source.wav \
--model base.en \
--language en \
--word_timestamps True \
--output_dir .work \
--output_format json \
--verbose False
⚠️ Whisper CLI 的注意事項:在較舊版本中,傳遞 --output_format json --output_format srt 只會保留最後一個標誌。如果您需要多種格式,請使用 --output_format all(會寫出 JSON + SRT + VTT + TSV + TXT)。
驗證輸出:.work/source.json 應包含 segments[].words[],並且每個詞語有 start、end、word。如果 words 為空,表示您忘記了 --word_timestamps True。
在此階段閱讀完整的轉錄文本。 不要跳過。您會發現:
- 品牌名稱被誤聽。 Whisper 有 90% 的機率會將 "Claude Code at Anthropic" 聽成 "Cloud Code at Infropic"。"SpecWeave" 會被輸出為 "spec with" 或 "specs"。這些需要疊加層進行修正,而不是重新錄製。
- 重複片段。 尋找完全相同的重複句子。在我們的項目中,"And those systems cannot produce skills as you work" 在 01:14、01:30、01:47 出現了三次。刪除其中兩個。
- 填充性的重新開始。 單詞片段,如 "And…"、"And…",出現在實際句子之前。剪輯腳本會將這些捕獲為「幽靈」片段 — 我們在階段 4 中會將其刪除。
階段 3 — 檢測靜音與場景變化
兩個並行的檢測為剪輯演算法提供資料:
3a. 靜音檢測
ffmpeg -hide_banner -nostats -i "$SRC" -af "silencedetect=noise=-30dB:duration=1.2" -f null - 2>&1 \
| grep -E "silence_(start|end)"
-30 dB 和 1.2s 是最佳參數。更寬鬆的設置(-25 dB、0.8s)會捕捉到您不想剪掉的呼吸間隙。更嚴格的設置(-35 dB、2s)則會錯過真正的空白時間。
將 start/end 對解析為 [(s, e, dur), ...] 並寫入 silences.json。請參閱 scripts/build_cuts.py。
3b. 場景變化檢測
ffmpeg -hide_banner -nostats -i "$SRC" -filter:v "select='gt(scene,0.05)',showinfo" -f null - 2>&1 \
| grep "showinfo" | grep -oE "pts_time:[0-9.]+" | sed 's/pts_time://' > scenes.txt
scene>0.05 足夠靈敏,可以捕捉到說話者打開新的瀏覽器分頁、切換應用程式或進行顯著滾動。不要低於 0.03 — 否則會因為游標閃爍而產生誤報。
這兩個檢測結果結合起來,讓您可以做出智慧剪輯決策:靜音 + 無場景變化 = 純粹的空白時間,剪掉它。靜音 + 場景變化 = 說話者正在螢幕上展示某些東西,保留它(觀眾應該看螢幕,而不是聽旁白)。
階段 4 — 構建智慧剪輯列表
剪輯演算法有 5 個硬性規則,這些規則是透過痛苦的經歷得來的:
規則 1 — 純粹的空白時間應被剪掉,但不能全部剪掉
對於每個大於 MIN_CUT_LEN(1.5 秒)且內部沒有場景變化的靜音片段:剪掉除 BREATH(0.30 秒)以外的所有內容。保留呼吸以保持自然節奏 — 完全消除所有靜音會使語音聽起來像機器編輯過的。
if not in_scenes and (cut_end - cut_start) > MIN_CUT_LEN:
cuts.append((cut_start + 0.30, cut_end, f"dead air {dur:.1f}s"))
規則 2 — 帶有螢幕活動的靜音應保留(最長 3.5 秒)
如果靜音片段內有場景變化,表示說話者正在讓觀眾閱讀。保留從 first_scene - 0.4s 到 last_scene + 0.8s 的視窗,但絕不超過 3.5 秒 — 讓觀眾盯著同一畫面超過 3.5 秒會使他們失去注意力。
規則 3 — 對齊到詞語邊界
在計算出粗略的保留範圍後,將每個邊界對齊到 Whisper 提供的最接近的詞語邊緣,容忍度為 0.4 秒:
def snap(t, words):
best, bd = t, 0.4
for s, e, _ in words:
for cand in (s, e):
if abs(cand - t) < bd:
best, bd = cand, abs(cand - t)
return best
這可以防止剪輯在詞語中間切斷。沒有這個步驟,幾乎每個剪輯都會切斷末尾的 's' 或呼吸輔音。
規則 4 — 添加詞語邊界緩衝區(這是使用者會注意到的)
即使進行了詞語邊緣對齊,末尾輔音的持續時間會略微超過 Whisper 報告的詞語結束時間戳。Whisper 計時的範圍是「母音開始到母音結束」,而不是輔音尾部。因此,嚴格在 word.end 處進行剪輯會切斷末尾的 's'、呼吸聲或 'k'。
解決方案:延長每個保留範圍:
- 結束端: 在最後一個詞語結束後
+ 0.30s,上限為到下一個保留範圍間隙的一半。 - 開始端: 在第一個詞語開始前
- 0.10s,上限為到前一個保留範圍間隙的一半。
END_BUF, START_BUF = 0.30, 0.10
for i, (s, e) in enumerate(keep):
gap_next = keep[i+1][0] - e if i+1 < len(keep) else TOTAL - e
gap_prev = s - keep[i-1][1] if i > 0 else s
add_end = min(END_BUF, max(0, gap_next * 0.5))
add_start = min(START_BUF, max(0, gap_prev * 0.5))
if gap_next < 0.6: add_end = max(add_end, gap_next - 0.05) # 吃掉微小間隙
if gap_prev < 0.6: add_start = max(add_start, gap_prev - 0.05)
final.append([s - add_start, e + add_end])
結果:剪輯 "find the tickets" 會從原始時間 08:09.26 延伸到 08:09.56 — 末尾的 's' 得以保留。沒有這個,使用者會告訴您音頻「聽起來很倉促」或「吞掉了結尾」。
規則 5 — 手動移除重複片段
程式化地去重相同的 n-gram 是脆弱且有風險的(可能會刪除合法的重複內容)。相反:閱讀轉錄文本,自己找到完全相同的重複內容,並在原始時間中添加明確的刪除範圍:
DUP_RANGES = [
(78.86, 113.82), # 01:18.86 - 01:53.82: "skills as you work" 的三次錄製中的兩次
]
for ds, de in DUP_RANGES:
# 修剪每個與此範圍重疊的保留區域
...
這會將剪輯節省的比例從 35% 降低到 32%(重複內容包含靜音間隙 + 語音),但結果聽起來非常乾淨。
輸出
腳本會寫出:
cuts.json— 保留範圍 + 剪輯範圍 + 摘要統計 + 原始時間映射(階段 8 用於疊加層錨點)。edl.txt— 人類可讀的 EDL,每個保留片段附帶轉錄文本片段(在渲染之前請仔細檢查此檔案)。
完整實現請參閱 scripts/build_cuts.py。
階段 5 — 清除瞬態嗶聲
OBS 錄影通常包含短暫的瞬態偽影 — 啟動嗶聲、按鍵切換的咔嗒聲、撞擊到高輸入的麥克風爆破音。這些聲音會伴隨著靜音剪輯的決策而保留下來,因為它們聲音很大(所以靜音檢測會忽略它們)且被安靜的環境包圍。它們在最終影片的 4:11 或 0:52 等位置聽起來像「滴答」聲。
不要嘗試使用全域噪聲門或降噪器來解決此問題。 這會使輔音模糊,讓語音聽起來像在水下。相反:檢測瞬態噪音,並對每個 60-100ms 的視窗進行手術級靜音。
檢測演算法(不會將語音靜音的演算法)
瞬態噪音(「嗶聲」)成立的條件:
RMS(window 5ms) > BEEP_RMS_MIN (例如 > 800)
RMS(preceding 150ms) < PRE_QUIET (例如 < 200)
RMS(following 200ms) < POST_QUIET (例如 < 350)
第三個條件至關重要。沒有它,您會捕捉到語音的輔音起始('p', 't', 'k'),因為它們在前 5ms 內看起來完全相同 — 但是語音之後會跟著一個持續的母音,而嗶聲之後會回到靜音。透過 post_avg < 350 進行過濾,您只會對孤立的瞬態噪音進行靜音。
同時,將靜音的時間跨度限制在 0.30 秒以內 — 即使誤分類也永遠不會抹掉一個完整的詞語。
應用手術級靜音
構建一個包含目標範圍的 ffmpeg volume 濾鏡表達式:
volume=enable='between(t,52.110,52.180)+between(t,169.850,169.920)+between(t,251.245,251.315)+...':volume=0
將此應用到受影響的剪輯範圍。不要全局應用 — 只將濾鏡應用於包含瞬態噪音的剪輯片段。
請參閱 scripts/scrub_beeps.py 以了解完整的檢測和濾鏡構建邏輯。
階段 6 — 透過 concat 濾鏡渲染主版本(單次遍歷)
不要使用 ffmpeg concat demuxer。 Concat demuxer 會重新封裝而不重新編碼,這會從剪輯邊界繼承原始音頻的格式和時序問題(包括滴答聲)。使用 concat 濾鏡可以確保所有內容都透過同一編解碼器路徑進行重新編碼。
構建一個複雜的濾鏡圖:
# 構建保持片段的 concat 描述
KEEPS=$(python3 -c "
import json
j = json.load(open('cuts.json'))
for s, e, _ in j['keeps']:
print(f'[{s}:{e}]')
" | tr '\n' ' ')
ffmpeg -hide_banner -i "$SRC" -filter_complex "
$(for i, (s, e) in enumerate(keeps); do
echo "[0:v]trim=${s}:${e},setpts=PTS-STARTPTS[v${i}];"
echo "[0:a]atrim=${s}:${e},asetpts=PTS-STARTPTS[a${i}];"
done)
$(echo -n "${KEEPS[@]}" | sed 's/ / /g')
concat=n=${#KEEPS[@]}:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" -c:v libx264 -preset medium -crf 18 -c:a aac -b:a 192k -pix_fmt yuv420p "$OUTPUT"
請參閱 scripts/render.py 以了解完整的渲染腳本。
階段 7 — 生成英雄圖片(可選)
使用 Gemini 3 Pro Image 生成高品質的品牌英雄圖片,用於章節標題或場景過渡:
python3 scripts/generate_hero.py --prompt "A futuristic dashboard with purple and green lighting, glow effects, and code overlays in a dark theme" --output hero.png
將圖片的 URL 或路徑儲存起來,以便在 HyperFrames 合成中使用。
階段 8 — 創作 HyperFrames 合成(iOS 卡片)
使用 HyperFrames 的 data-* 屬性系統來創建 iOS 風格的毛玻璃疊加卡片。這些卡片漂浮在全螢幕影片之上,不會遮擋說話者的面部鏡頭。
關鍵模式:
- 毛玻璃效果: 使用
data-hf-glass="true"和backdrop-filter: blur(10px)。 - 品牌圖標: 從您的品牌庫中導入真實的 SVG 或 PNG 圖標。
- 著色卡片: 使用 Studio 的
tinted-card模式,配合background: rgba(128, 0, 128, 0.3)等顏色。 - Apple 簡報風格場景過渡: 使用
data-hf-transition="hero"並由階段 7 生成的圖片驅動。 - 錨點映射: 使用
cuts.json中的source_time_to_cut_time映射,將疊加層對齊到原始語音時刻,而不是剪輯後的時間點。
範例卡片結構:
<div data-hf-glass="true" data-hf-position="bottom-left" data-hf-enter="fade" data-hf-start="10.5" data-hf-end="15.2">
<div style="backdrop-filter: blur(10px); background: rgba(128, 0, 128, 0.2); border-radius: 20px; padding: 16px;">
<img src="logos/specweave.svg" width="32" height="32" />
<h3>SpecWeave</h3>
<p>Build skills, not slides</p>
</div>
</div>
請參閱上游 HyperFrames 文件以了解完整的屬性 API。
階段 9 — 在 Studio 中進行 Lint 檢查 + 驗證
在發布之前:
- 運行
npx hyperframes lint以捕獲常見錯誤(缺失的屬性、損壞的引用、圖像路徑錯誤)。 - 在 HyperFrames Studio 中預覽合成 — 特別檢查:
- 卡片是否與面部鏡頭重疊。
- 場景過渡是否平穩。
- 音頻中是否有殘留的滴答聲或嗶聲。
- 驗證波形快取 — 如果 Studio 顯示的波形與渲染的音頻不同步,請手動清除 Studio 的波形快取(
rm -rf ~/.hyperframes/cache/waveforms)。 - 導出最終版本 並以目標格式交付(.mp4, .mov 等)。
腳本與工具
此技能依賴於 scripts/ 目錄中的 Python 腳本:
build_cuts.py— 解析轉錄文本、靜音和場景數據,輸出cuts.json。scrub_beeps.py— 檢測瞬態噪音並輸出 ffmpegvolume濾鏡參數。render.py— 使用concat濾鏡構建並運行最終的 ffmpeg 命令。generate_hero.py— 使用 Gemini API 生成 AI 圖像。
安裝依賴項:
pip install openai-whisper pillow requests
常見陷阱(「踩坑指南」)
- 詞語中間被剪斷(Clipped Words): 總是使用規則 3 和 4(詞語邊界 + 緩衝區)。這是新手最常見的抱怨。
- OBS 滴答聲(OBS Ticks): 錄製開始/結束時的 100ms 瞬態噪音。階段 5 會處理這些,但您必須在剪輯後於影片中識別它們的位置。
- 疊加層漂移(Overlay Drift): 如果您在剪輯前將疊加層錨定到原始時間,然後進行剪輯,錨點將會偏移。始終使用
source_time_to_cut_time映射(來自cuts.json)。 - 幀數混淆(Frame Count Confusion): 不要使用
duration * source_fps來獲得 Studio 幀數。使用duration * 30。 - Whisper 轉錄錯誤(Whisper Hallucinations): 尤其對於品牌名稱和技術術語。在階段 2 中閱讀轉錄文本,並在疊加層中進行修正。