Browse Source

feat: 透明通道实测入册(ProRes4444/webm/png-seq) + sfx-cues.sh cue表打点工具

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
alchain 2 months ago
parent
commit
7c1c370eb8
2 changed files with 46 additions and 1 deletions
  1. 5 1
      references/hyperframes-backend.md
  2. 41 0
      scripts/sfx-cues.sh

+ 5 - 1
references/hyperframes-backend.md

@@ -68,9 +68,13 @@ npx -y hyperframes@<pin版本> render --fps 60   # 终渲;默认 30fps
 - **两级渲染**:先默认 30fps 快速出片,肉眼+截帧检查通过后再 `--fps 60` 终渲。60fps 600 帧 1080p 实测约 20 秒
 - 渲染产物侧校验(audio stream / 黑帧 / 响度 / 时长)用 `scripts/verify-video.sh`(见 verification.md)
 
+## 透明通道(overlay花字/贴片直接叠剪辑轨)
+
+`npx hyperframes render --format mov` 输出 ProRes 4444(yuva444p12le,带alpha,2026-07-17实测叠色底连软阴影都正确半透);`--format webm` 同样带透明、体积小;`--format png-sequence` 出RGBA帧序列给AE/达芬奇。合成侧要点:html/body背景设 `transparent`、不铺底色。花字/角标/lower-third这类overlay素材从此直接进剪辑轨,不用抠像。注意MOV体积大(ProRes无损级,4秒15MB量级),交付剪辑用;网络传输用webm。
+
 ## 音频
 
-HyperFrames 合成里 `<audio>` 元素可直接进时间轴(BGM/解说随片渲染)。当前音频流程不变:SFX/BGM 双轨制照 audio-design-rules.md,用 add-music.sh / mix-voiceover.sh 后期混流也可以。哪条路更好在实战中定,先不强制。
+HyperFrames 合成里 `<audio>` 元素可直接进时间轴(BGM/解说随片渲染)。当前音频流程不变:SFX/BGM 双轨制照 audio-design-rules.md,用 add-music.sh / mix-voiceover.sh 后期混流也可以。哪条路更好在实战中定,先不强制。SFX打点用 `scripts/sfx-cues.sh <视频> <cue表.tsv> <输出>`(cue表=秒数/sfx路径/音量dB三列,B00实战沉淀,改表重跑10秒出片)。
 
 ## pitfalls 增量(相对自研管线)
 

+ 41 - 0
scripts/sfx-cues.sh

@@ -0,0 +1,41 @@
+#!/bin/bash
+# sfx-cues.sh — 按cue表给无声视频打SFX点(B00阶跃b-roll实战沉淀,2026-07-17)
+#
+# 用法:bash sfx-cues.sh <无声视频.mp4> <cue表.tsv> <输出.mp4> [--dur=秒]
+#
+# cue表格式(TSV,#开头为注释):
+#   秒数<TAB>sfx相对路径(相对assets/sfx/)<TAB>音量dB
+#   例:63.0	impact/brand-stamp.mp3	-13
+#
+# 音量基准(轻SFX垫口播下):whoosh类-16 / tick类-15 / impact类-12;纯动画成品可整体+4dB
+# cue密度参考 audio-design-rules.md 配方(b-roll垫底≈1个/9s,只打结构性节点)
+
+set -e
+SFX_DIR="$(cd "$(dirname "$0")/../assets/sfx" && pwd)"
+IN="${1:?用法: bash sfx-cues.sh in.mp4 cues.tsv out.mp4 [--dur=210]}"
+TABLE="${2:?缺cue表}"
+OUT="${3:?缺输出路径}"
+DUR=""
+for a in "$@"; do case "$a" in --dur=*) DUR="${a#*=}";; esac; done
+[ -z "$DUR" ] && DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$IN" | cut -d. -f1)
+
+INPUTS=(-i "$IN")
+FILTER=""; MIX=""; i=1
+while IFS=$'\t' read -r t f db; do
+  [ -z "$t" ] && continue
+  case "$t" in \#*) continue;; esac
+  [ ! -f "$SFX_DIR/$f" ] && { echo "✗ SFX不存在: $f"; exit 1; }
+  INPUTS+=(-i "$SFX_DIR/$f")
+  ms=$(python3 -c "print(int(float('$t')*1000))")
+  FILTER+="[$i:a]adelay=${ms}:all=1,volume=${db}dB[s$i];"
+  MIX+="[s$i]"
+  i=$((i+1))
+done < "$TABLE"
+N=$((i-1))
+[ "$N" = "0" ] && { echo "✗ cue表为空"; exit 1; }
+
+ffmpeg -y -loglevel error "${INPUTS[@]}" \
+  -filter_complex "${FILTER}${MIX}amix=inputs=${N}:normalize=0,apad=whole_dur=${DUR}[aout]" \
+  -map 0:v -map "[aout]" -c:v copy -c:a aac -b:a 192k -shortest "$OUT"
+
+echo "✓ ${N}个cue → $OUT"