HeyGen 数字人视频与 Remotion 合成集成实战指南(OpenMontage remotion-composer 实践)
2026/9/10 15:47:38 网站建设 项目流程

HeyGen 数字人视频与 Remotion 合成集成实战指南(OpenMontage remotion-composer 实践)

【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage

导读

本文以 OpenMontage 仓库中 avatar-video 技能 的 Remotion 集成指南为主体,系统讲解如何用 HeyGen API 生成 AI 数字人(Avatar)视频,并将其嵌入 Remotion 合成(Composition)完成字幕、图形动画、图表等动效叠加与最终渲染。读完本文,你将掌握 HeyGen MP4/WebM 两种输出格式的选型、基于OffthreadVideo的帧精确播放、并行开发工作流、动态时长计算以及一整套可直接运行的生成–合成–渲染代码模板。

一、整体工作流:从 HeyGen 生成到 Remotion 合成

在 OpenMontage 中,HeyGen 数字人视频被用于 talking-head(讲解头)类视频生产。典型的工作流分为四步:

  1. 调用 HeyGen/v2/video/generate接口生成数字人视频;
  2. 轮询视频状态直至完成,拿到可用的视频 URL;
  3. 将视频 URL(或本地下载文件)作为素材喂给 Remotion 合成;
  4. 在 Remotion 中叠加背景、图形动效、字幕、图表等元素并渲染成片。

对应到仓库里的实际实现,remotion-composer 是一个基于 Remotion 4.x(remotion: ^4.0.484)的合成渲染工程,其中的 TalkingHead.tsx 就是"数字人视频 + 动态叠加层 + 字幕"三层结构的典型落地:

  • Layer 1OffthreadVideo播放数字人视频(videoSrc),objectFit: "cover"铺满画面;
  • Layer 2:按时间轴(in_seconds/out_seconds)通过Sequence挂载图表、统计卡片、Callout、对比卡片等 13 类叠加组件;
  • Layer 3:最上层渲染逐词高亮字幕(CaptionOverlay)。

下面按"选型 → 生成 → 合成 → 渲染 → 排障"的顺序完整展开。

二、Quick Start:最小可运行示例

// 1. 获取数字人详情(含默认音色) const avatar = await getAvatarDetails(avatarId); // 2. 生成视频(带背景的 MP4 是最常用形态) const videoId = await generateVideo({ video_inputs: [{ character: { type: "avatar", avatar_id: avatar.id, avatar_style: "normal" }, voice: { type: "text", input_text: script, voice_id: avatar.default_voice_id }, background: { type: "color", value: "#1a1a2e" }, }], dimension: { width: 1920, height: 1080 }, }); // 3. 轮询等待完成(通常耗时 10-15 分钟以上) // 4. 在 Remotion 中使用,并在其上叠加动态图形

三点关键提醒:

  • 所有请求都要携带X-Api-Key请求头,密钥通过环境变量HEYGEN_API_KEY提供(见 avatar-video/SKILL.md);
  • 优先使用数字人自带的default_voice_id,这是 HeyGen 预先匹配好的音色,效果最自然;
  • 视频生成是异步的,不要同步阻塞等待,详见下文"并行开发工作流"。

三、输出格式选型:MP4 还是 WebM?

HeyGen 提供两个端点、两种输出形态,选型直接决定合成层的复杂度:

你的合成场景推荐格式原因
数字人主讲 + 叠加图形MP4 + 背景更简单,叠加层直接放在上层
Loom 风格(数字人浮在屏幕录制上)WebM +closeUp,在 Remotion 里做圆形遮罩需要透明通道,用 CSS 做圆形裁切
数字人叠加在其它视频/内容之上WebM(透明背景)需要看到背后的内容
全屏数字人MP4 + 背景标准做法

核心结论:大多数场景直接用带背景的 MP4;只有需要看到数字人"背后"的内容时才用 WebM。

注意:WebM 只支持normalcloseUp两种avatar_style不支持circle。需要圆形构图时,在 Remotion 中用 CSSborder-radius: 50%实现。

两个端点的结构差异(详见 video-generation.md):

端点格式用途
/v2/video/generateMP4标准——带背景的视频(最常用)
/v1/video.webmWebM透明背景——仅当需要露出背景时使用

WebM 端点使用/v2/video/generate完全不同的请求结构(扁平字段而非video_inputs数组),且要求avatar_pose_id必填,input_text+voice_idinput_audio二者必居其一(不能同时提供)。

四、并行开发工作流:不要干等 15 分钟

HeyGen 视频生成通常需要10-15 分钟甚至更久,最佳实践是让生成与合成开发并行推进:

  1. 先发起 HeyGen 生成——把返回的video_id存到文件,立即退出;
  2. 并行搭建 Remotion 合成——先用占位素材,或直接使用数字人的preview_video_url(一段短视频循环);
  3. 搭建完成后轮询 HeyGen 状态
  4. 把占位素材替换为真实视频 URL

两个实用技巧:

  • 估算时长:按约 150 词/分钟的语速估算,wordCount / 150 * 60 * fps即为近似总帧数;
  • 组件设计:让动效组件在"有无数字人视频"两种情况下都能独立工作,这样动效可以先脱离视频单独测试。

这一思路与仓库中的 TitledVideo.tsx 一致——它把"衬线标题叠加组件"与"底层视频"解耦:视频未就绪时组件仍可单独渲染测试。

五、尺寸对齐:HeyGen 输出必须匹配 Remotion 画布

关键原则:HeyGen 输出分辨率必须与 Remotion 合成画布完全一致,否则会出现拉伸、裁切或黑边。

5.1 通用尺寸预设

// HeyGen 与 Remotion 共用的尺寸常量 const DIMENSIONS = { landscape_1080p: { width: 1920, height: 1080 }, landscape_720p: { width: 1280, height: 720 }, portrait_1080p: { width: 1080, height: 1920 }, portrait_720p: { width: 720, height: 1280 }, square_1080p: { width: 1080, height: 1080 }, square_720p: { width: 720, height: 720 }, } as const; type DimensionPreset = keyof typeof DIMENSIONS;

5.2 按预设生成 HeyGen 视频

async function generateHeyGenVideo( script: string, avatarId: string, voiceId: string, preset: DimensionPreset ): Promise<string> { const dimension = DIMENSIONS[preset]; const response = await fetch("https://api.heygen.com/v2/video/generate", { method: "POST", headers: { "X-Api-Key": process.env.HEYGEN_API_KEY!, "Content-Type": "application/json", }, body: JSON.stringify({ video_inputs: [ { character: { type: "avatar", avatar_id: avatarId, avatar_style: "normal", }, voice: { type: "text", input_text: script, voice_id: voiceId, }, background: { type: "color", value: "#00FF00", // 绿幕,用于后续合成 }, }, ], dimension, }), }); const { data } = await response.json(); return data.video_id; }

5.3 Remotion 合成画布设置

// remotion-composer/src/Root.tsx import { Composition } from "remotion"; import { AvatarComposition } from "./AvatarComposition"; const DIMENSIONS = { landscape_1080p: { width: 1920, height: 1080 }, // ... 与上方一致 }; export const RemotionRoot: React.FC = () => { return ( <> <Composition id="AvatarVideo" component={AvatarComposition} durationInFrames={300} // 后续改为动态计算 fps={30} width={DIMENSIONS.landscape_1080p.width} height={DIMENSIONS.landscape_1080p.height} defaultProps={{ avatarVideoUrl: "", }} /> </> ); };

尺寸约束与建议(来自 dimensions.md):

  • HeyGen 自定义尺寸限制:任一边最小128px、最大4096px,且宽高都必须是偶数
  • 分辨率影响配额消耗:1080p 约为 720p 的 1.5 倍成本,建议草稿用 720p、终稿用 1080p;
  • 平台推荐:YouTube 用 1920×1080(16:9)、TikTok/Reels/Shorts 用 1080×1920(9:16)、Instagram 信息流用 1080×1080(1:1)。

六、为 Remotion 生成数字人视频

6.1 标准方案:带背景的 MP4

大多数 Remotion 合成用 MP4 + 背景最合适,动效与图形叠加在上层即可:

async function generateAvatarForRemotion( script: string, avatarId: string, voiceId: string, options: { style?: "normal" | "closeUp" | "circle"; backgroundColor?: string; } = {} ): Promise<string> { const { style = "normal", backgroundColor = "#1a1a2e" } = options; const response = await fetch("https://api.heygen.com/v2/video/generate", { method: "POST", headers: { "X-Api-Key": process.env.HEYGEN_API_KEY!, "Content-Type": "application/json", }, body: JSON.stringify({ video_inputs: [{ character: { type: "avatar", avatar_id: avatarId, avatar_style: style, }, voice: { type: "text", input_text: script, voice_id: voiceId, }, background: { type: "color", value: backgroundColor, }, }], dimension: { width: 1920, height: 1080 }, }), }); const { data } = await response.json(); return data.video_id; }

6.2 透明背景方案:WebM

仅在需要看到数字人"背后"的内容时使用(例如数字人叠加在屏幕录制上):

// 使用 /v1/video.webm 端点获取透明背景 // 注意:该端点结构与 /v2/video/generate 不同 const response = await fetch("https://api.heygen.com/v1/video.webm", { method: "POST", headers: { "X-Api-Key": process.env.HEYGEN_API_KEY!, "Content-Type": "application/json", }, body: JSON.stringify({ avatar_pose_id: avatarPoseId, // 必填:数字人姿态 ID avatar_style: "normal", // 必填:仅支持 "normal" 或 "closeUp" input_text: script, // 必填(与 voice_id 搭配) voice_id: voiceId, // 必填(与 input_text 搭配) dimension: { width: 1920, height: 1080 }, }), });

请求体字段速查表:

字段必填说明
avatar_pose_id数字人姿态 ID(来自数字人详情)
avatar_style"normal""closeUp"(不支持 circle)
input_text✓*脚本文本(不使用input_audio时必填)
voice_id✓*音色 ID(与input_text搭配)
input_audio✓*音频 URL(不使用input_text时必填)
dimension默认 1280×720

WebM 视频与 MP4 使用同一套状态轮询端点,只是完成后返回的video_url.webm文件。

七、在 Remotion 中使用数字人视频

7.1 关键:渲染必须用 OffthreadVideo

始终使用OffthreadVideo而不是Video来播放 HeyGen 数字人视频。基础Video组件依赖浏览器的视频解码器,帧不精确,渲染时会产生抖动(jitter)。OffthreadVideo通过 FFmpeg 提取帧,保证平滑、精确的播放。

OffthreadVideo属于remotion核心包,无需额外安装

基本用法:

// remotion-composer/src/AvatarComposition.tsx import { OffthreadVideo, useVideoConfig } from "remotion"; interface AvatarCompositionProps { avatarVideoUrl: string; } export const AvatarComposition: React.FC<AvatarCompositionProps> = ({ avatarVideoUrl, }) => { return ( <div style={{ flex: 1, backgroundColor: "#1a1a2e" }}> <OffthreadVideo src={avatarVideoUrl} style={{ width: "100%", height: "100%", objectFit: "contain", }} /> </div> ); };

仓库中的实际印证:无论是 TalkingHead.tsx 的数字人底层视频,还是 TitledVideo.tsx 的全屏背景视频,都统一使用OffthreadVideo,而不是Video

7.2 透明 WebM 叠加(推荐)

使用/v1/video.webm输出的 WebM 时无需绿幕抠像,透明通道直接生效:

import { OffthreadVideo, AbsoluteFill, Sequence } from "remotion"; export const AvatarWithMotionGraphics: React.FC<{ avatarWebmUrl: string }> = ({ avatarWebmUrl }) => { return ( <AbsoluteFill> {/* Layer 1: 背景/内容 */} <AbsoluteFill style={{ backgroundColor: "#1a1a2e" }}> <YourMotionGraphics /> </AbsoluteFill> {/* Layer 2: 透明背景数字人 - 用 OffthreadVideo 保证帧精确 */} <OffthreadVideo src={avatarWebmUrl} transparent style={{ position: "absolute", bottom: 0, right: 0, width: "50%", height: "auto", }} /> {/* Layer 3: 叠加在数字人上层的动效 */} <Sequence from={30}> <AnimatedTitle text="Welcome!" /> </Sequence> </AbsoluteFill> ); };

7.3 Loom 风格:屏幕录制上的圆形数字人

closeUp风格 + WebM,在 Remotion 中用 CSS 圆形遮罩:

import { OffthreadVideo, AbsoluteFill } from "remotion"; export const LoomStyleComposition: React.FC<{ screenRecordingUrl: string; avatarWebmUrl: string; // 通过 /v1/video.webm 以 avatar_style: "closeUp" 生成 }> = ({ screenRecordingUrl, avatarWebmUrl }) => { return ( <AbsoluteFill> {/* 屏幕录制铺满全屏 */} <OffthreadVideo src={screenRecordingUrl} style={{ width: "100%", height: "100%" }} /> {/* 圆形遮罩数字人 - 透明背景让屏幕透出 */} <OffthreadVideo src={avatarWebmUrl} transparent style={{ position: "absolute", bottom: 40, left: 40, width: 180, height: 180, borderRadius: "50%", // CSS 圆形遮罩 overflow: "hidden", objectFit: "cover", }} /> </AbsoluteFill> ); };

再次强调:WebM 不支持circle风格,必须用normalcloseUp,圆形裁切交给 CSS 完成。

7.4 遗留方案:绿幕 + 色度键(不推荐)

若手上只有绿幕背景的 MP4(不推荐,请优先使用 WebM):

// 注意:真正的色度键需要 WebGL 或后期处理 // WebM 透明背景要简单得多 <OffthreadVideo src={avatarVideoUrl} style={{ mixBlendMode: "multiply", // 仅基础合成 }} />

7.5 多层合成模板

import { OffthreadVideo, Sequence, useVideoConfig, Img } from "remotion"; interface LayeredAvatarProps { avatarVideoUrl: string; backgroundUrl: string; logoUrl: string; title: string; } export const LayeredAvatarComposition: React.FC<LayeredAvatarProps> = ({ avatarVideoUrl, backgroundUrl, logoUrl, title, }) => { const { fps } = useVideoConfig(); return ( <div style={{ position: "relative", width: "100%", height: "100%" }}> {/* Layer 1: 背景 */} <Img src={backgroundUrl} style={{ position: "absolute", width: "100%", height: "100%", objectFit: "cover", }} /> {/* Layer 2: 数字人视频 - 用 OffthreadVideo 防止抖动 */} <OffthreadVideo src={avatarVideoUrl} style={{ position: "absolute", bottom: 0, right: 0, width: "40%", height: "auto", }} /> {/* Layer 3: 标题(1 秒后出现) */} <Sequence from={fps}> <div style={{ position: "absolute", top: 50, left: 50, color: "white", fontSize: 48, fontWeight: "bold", }} > {title} </div> </Sequence> {/* Layer 4: Logo */} <Img src={logoUrl} style={{ position: "absolute", top: 20, right: 20, width: 100, height: "auto", }} /> </div> ); };

7.6 素材路径解析

当数字人视频 URL 可能为空或需要本地回退时,可参考仓库的 resolveAsset.ts:它统一处理三类素材来源——远程 URL(http(s)://data:直接放行)、绝对文件路径(Unix 与 Windows 路径均归一化为file://)、public/目录下的相对资源(转staticFile())。合成组件的videoSrc统一经它解析,保证开发预览与命令行渲染行为一致。

八、完整端到端工作流

8.1 生成并合成

import { bundle } from "@remotion/bundler"; import { renderMedia, selectComposition } from "@remotion/renderer"; async function generateAvatarVideoForRemotion( script: string, outputPath: string ) { // 1. 生成 HeyGen 视频 console.log("Generating HeyGen avatar video..."); const videoId = await generateHeyGenVideo( script, "josh_lite3_20230714", "1bd001e7e50f421d891986aad5158bc8", "landscape_1080p" ); // 2. 等待完成 console.log("Waiting for HeyGen video..."); const avatarVideoUrl = await waitForVideo(videoId); console.log(`HeyGen video ready: ${avatarVideoUrl}`); // 3. 读取视频时长换算为 Remotion 帧数 const avatarDuration = await getVideoDuration(avatarVideoUrl); const durationInFrames = Math.ceil(avatarDuration * 30); // 30 fps // 4. 打包 Remotion 工程 console.log("Bundling Remotion project..."); const bundleLocation = await bundle({ entryPoint: "./remotion/src/index.ts", }); // 5. 选中合成 const composition = await selectComposition({ serveUrl: bundleLocation, id: "AvatarVideo", inputProps: { avatarVideoUrl, }, }); // 6. 渲染成片 console.log("Rendering final composition..."); await renderMedia({ composition: { ...composition, durationInFrames, }, serveUrl: bundleLocation, codec: "h264", outputLocation: outputPath, inputProps: { avatarVideoUrl, }, }); console.log(`Final video rendered: ${outputPath}`); return outputPath; }

轮询实现可参考 video-status.md 的规范:状态流转为pending → processing → completed / failedcompleted时返回video_url(同时带thumbnail_urldurationcaptioned_video_urlsubtitle_url等元数据);若 MCP 工具可用(mcp__heygen__get_video),优先使用它替代手写轮询。

8.2 用 calculateMetadata 实现动态时长

数字人视频时长事先未知,正确做法是用calculateMetadata在渲染前探测视频真实时长:

// remotion-composer/src/AvatarComposition.tsx import { CalculateMetadataFunction } from "remotion"; export const calculateAvatarMetadata: CalculateMetadataFunction< AvatarCompositionProps > = async ({ props }) => { // 从 HeyGen 视频读取时长 const duration = await getVideoDurationInSeconds(props.avatarVideoUrl); return { durationInFrames: Math.ceil(duration * 30), fps: 30, width: 1920, height: 1080, }; }; // 在 Root.tsx 中注册 <Composition id="AvatarVideo" component={AvatarComposition} calculateMetadata={calculateAvatarMetadata} defaultProps={{ avatarVideoUrl: "", }} />

仓库的实战对照:在 TitledVideo.tsx 中,calculateTitledVideoMetadata通过@remotion/media-utilsgetVideoMetadata()探测源视频真实时长并换算为帧数(Math.round(meta.durationInSeconds * 30)),探测失败时回退到 60 秒兜底;Root.tsx 中ExplainerCinematicRenderer等合成同样注册了calculateMetadata。这套模式正是数字人类合成动态时长的标准做法。

九、最佳实践清单

9.1 绿幕用于灵活合成

需要后期合成时,让 HeyGen 输出纯绿背景(#00FF00):

background: { type: "color", value: "#00FF00", // 纯绿,供色度键使用 }

优先选 WebM 透明背景,它比绿幕抠像简单可靠得多。

9.2 帧率匹配

HeyGen 默认25 fps,设置 Remotion fps 时要考虑对齐:

// 方案 1:与 HeyGen 的 25 fps 对齐 fps: 25 // 方案 2:使用 30 fps 并微调播放速率 <OffthreadVideo src={avatarVideoUrl} playbackRate={25/30} // 轻微放慢以对齐 />

9.3 URL 直用 vs 本地下载

直接使用 URL 的场景:

  • 在 Remotion Studio 中预览(npm run dev/npx remotion studio);
  • URL 在渲染完成前不会过期;
  • 开发期需要快速迭代。
// 直接使用 URL - 开发期更简单快速 <OffthreadVideo src={avatarVideoUrl} />

先下载再用的场景:

  • URL 会过期(HeyGen 的 URL 大约24 小时后失效);
  • 渲染将在之后进行或需要重复渲染;
  • 网络可靠性是顾虑;
  • 需要离线渲染。
// 带重试的可靠下载 async function downloadVideoWithRetry( url: string, outputPath: string, maxRetries = 5 ): Promise<string> { for (let attempt = 0; attempt < maxRetries; attempt++) { try { const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); const buffer = await response.arrayBuffer(); await fs.promises.writeFile(outputPath, Buffer.from(buffer)); return outputPath; } catch (error) { const delay = 2000 * Math.pow(2, attempt); console.log(`Retry ${attempt + 1}/${maxRetries} in ${delay}ms...`); await new Promise((r) => setTimeout(r, delay)); } } throw new Error("Download failed after retries"); } // 在 Remotion 中使用本地文件 const localPath = await downloadVideoWithRetry(avatarVideoUrl, "./public/avatar.mp4");

混合方案(生产环境推荐):

// 同时保存 URL 与本地路径到元数据 const metadata = { videoUrl: result.video_url, // 快速预览用 localPath: "./public/avatar.mp4", // 可靠渲染用 expiresAt: Date.now() + 24 * 60 * 60 * 1000, // URL 过期时间 }; // 组件内:本地文件存在则优先使用 const videoSrc = fs.existsSync(localPath) ? staticFile("avatar.mp4") : avatarVideoUrl;

9.4 数字人位置预设

const AVATAR_POSITIONS = { fullscreen: { width: "100%", height: "100%", position: "center" }, bottomRight: { width: "40%", bottom: 0, right: 0 }, bottomLeft: { width: "40%", bottom: 0, left: 0 }, pictureInPicture: { width: "25%", bottom: 20, right: 20 }, leftThird: { width: "33%", left: 0, height: "100%" }, };

仓库中的位置预设思路可进一步参考 TalkingHead.tsx 的POSITION_STYLES——它为 9:16(1080×1920)画布定义了lower_thirdupper_thirdleft_panelright_panelfull_overlay五档叠加层位置,并配套 8 帧淡入淡出动画,这正是"数字人 + 动态信息层"合成的生产级细节。

十、输出格式与编码参数

HeyGen 输出:

  • 格式:MP4(H.264)
  • 音频:AAC
  • 分辨率:按请求指定

Remotion 输出:

  • 编码器:H.264(默认)、VP8、VP9、ProRes
  • 质量设置建议匹配或超过 HeyGen 源素材
await renderMedia({ codec: "h264", crf: 18, // 高质量 // ... });

仓库 package.json 提供了可直接复用的渲染命令:npx remotion render src/index.tsx Explainer out/video.mp4(对应npm run build),开发预览用npm run start(即npx remotion studio)。

十一、故障排查

视频在 Remotion 中不播放

  1. 检查 URL 可访问性(CORS 问题);
  2. 确认视频格式兼容性;
  3. 尝试先下载到本地再引用。

尺寸不匹配

确保 HeyGen 与 Remotion 使用完全相同的尺寸:

// 共享配置 const VIDEO_CONFIG = { width: 1920, height: 1080, fps: 30, }; // HeyGen dimension: { width: VIDEO_CONFIG.width, height: VIDEO_CONFIG.height } // Remotion <Composition width={VIDEO_CONFIG.width} height={VIDEO_CONFIG.height} />

渲染出现视频抖动

数字人视频在成片中抖动或卡顿:

  1. OffthreadVideo替换Video——基础Video组件走浏览器解码器,帧不精确;
  2. 更新 import(无需额外安装,属remotion核心包):
    // 之前(导致抖动) import { Video } from "remotion"; // 之后(帧精确) import { OffthreadVideo } from "remotion";
  3. WebM 透明视频加上transparent属性:
    <OffthreadVideo src={avatarWebmUrl} transparent />

音画不同步

  • 核实源视频帧率;
  • 检查编码问题;
  • 考虑用一致的设置重新编码。

十二、深入阅读

  • 数字人技能总览与工具选型:.claude/skills/avatar-video/SKILL.md
  • HeyGen 视频生成完整字段与 WebM 端点:.claude/skills/avatar-video/references/video-generation.md
  • 状态轮询与下载 URL 规范:.claude/skills/avatar-video/references/video-status.md
  • 分辨率、宽高比与配额成本:.claude/skills/avatar-video/references/dimensions.md
  • Remotion 合成入口与注册方式:remotion-composer/src/Root.tsx、remotion-composer/src/index.tsx
  • 数字人叠加合成实现:remotion-composer/src/TalkingHead.tsx
  • 动态时长计算与素材解析:remotion-composer/src/TitledVideo.tsx、remotion-composer/src/lib/resolveAsset.ts

【免费下载链接】OpenMontageWorld's first open-source, agentic video production system. 12 production pipelines, 100+ tools, 700+ agent skill and production-knowledge files. Turn your AI coding assistant into a full video production studio.项目地址: https://gitcode.com/GitHub_Trending/op/OpenMontage

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询