AI智能体零基础制作【无限时长口播视频】首尾帧操作教程
从一张照片到20秒口播视频:AI 首尾帧链式生成全流程拆解

手把手教你用 RunningHub API 实现多段视频的无缝衔接,让 AI 生成的口播视频像一条拍完的。
前言
做短视频口播内容,最头疼的不是写文案,而是「人」。请演员成本高,自己出镜又没时间,能不能让 AI 替你出镜?
今天这篇文章,我把完整的 「照片 → 改装 → 口播视频 → 多段衔接」 工作流拆开来讲。重点讲一个很多人忽略的技巧:首尾帧链式生成——用上一段的尾帧作为下一段的参考图,让多段视频之间画面连贯、不再「跳切」。
整个流程用到的核心工具就一个:RunningHub 的 ComfyUI 云端工作流 API,图生图用 Kera2Edit,图生视频用 AnimateDiff + MiniMax H3。
整体流程一览

看起来很清晰对吧?接下来每一步都详细讲。
第一步:图生图编辑 —— 给照片「换装」
场景

手头有一张带迷彩妆的照片,但我们要的是普通休闲装风格。直接用这张图去生成视频,出来的还是迷彩妆,不符合口播场景。所以第一步先把图「改」了。
用的工作流
Kera2Edit(RunningHub App ID: 2088926295186034689),一个专门做图生图局部重绘和风格迁移的 ComfyUI 工作流。
API 调用
1) 上传本地图片到 RunningHub
curl -X POST 'https://www.runninghub.cn/openapi/v2/media/upload/binary' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-F 'file=@./original_photo.jpg'
返回的 data.fileName(形如 openapi/xxxxx.jpg)就是后续提交任务时用的图片标识。
2) 提交图生图任务
KREA-2-EDIT-One-image-V2 单图编辑工作流【短剧必备】
下载地址:https://www.runninghub.cn/post/2088923554007048194/?inviteCode=oga1ahgc
curl -X POST 'https://www.runninghub.cn/openapi/v2/run/ai-app/2088926295186034689' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{
"nodeInfoList": [
{
"nodeId": "160",
"fieldName": "text",
"fieldValue": "Remove all camouflage face paint, replace with clean natural face..."
},
{
"nodeId": "104",
"fieldName": "image",
"fieldValue": "openapi/xxxxx.jpg"
}
],
"instanceType": "default",
"usePersonalQueue": "false"
}'
关键节点说明:
nodeId: 160, fieldName: text→ 编辑提示词,告诉模型要改什么nodeId: 104, fieldName: image→ 参考图,用上传后返回的 fileName
3) 轮询等待结果
curl -X POST 'https://www.runninghub.cn/openapi/v2/query' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{"taskId": "你的taskId"}'
状态会经历 QUEUED → RUNNING → SUCCESS,图生图一般 60-90秒 出结果,消耗约 14-17 RH 币。
提示词技巧
写图生图提示词时,要同时说清楚三件事:
| 要素 | 示例 |
|---|---|
| 改什么 | Remove camouflage face paint, change military outfit |
| 改成什么 | Clean natural face, t-shirt and jeans |
| 保留什么 | Keep facial features, pose, background unchanged |
漏掉第三条,AI 可能会连人脸一起「重绘」掉,那就不是同一个人了。
第二步:图生视频 —— 让照片「开口说话」
用的工作流
AnimateDiff + MiniMax H3(App ID: 2088844222551121921),图生视频,支持角色对话、表情变化、自然动作,单条生成约 10 秒视频。
提示词格式:6 段式结构
这个工作流对提示词格式有要求,推荐使用 6 段式 Full-Reference 格式:
subject_definitions:
<Subject 1> is 人物描述 in <Picture 1>, 外貌特征.
summary:
[reference generation] 概括视频内容和参考关系.
retention_analysis:
<Subject 1> (appears in [Shot 1]): fully_preserved - 要保留的特征.
summarized_description:
[Shot 1] 镜头描述. <Subject 1> (S1) 动作描述.
S1 says, <d>[Chinese] 台词内容</d>
overall_soundscape:
环境音描述.
non_diegetic_music:
背景音乐描述.
⚠️ 重要避坑:不要在提示词中引用 <Audio 1>。该工作流的音频节点未连接,引用会直接报错 prompt media tag validation failed。如需配音,后期用 edge-tts + ffmpeg 叠加。
实际调用
MiniMax H3稳定加速版(全能参考4步加速版本)
工作流下载地址:https://www.runninghub.cn/post/2088836712364601345/?inviteCode=oga1ahgc
curl -X POST 'https://www.runninghub.cn/openapi/v2/run/ai-app/2088844222551121921' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d @task1.json
关键节点说明:
nodeId: 138, fieldName: value→ 6 段式提示词nodeId: 137, fieldName: image→ 参考图(可以是图生图的输出,也可以是尾帧)
图生视频一般 4-5 分钟出结果,消耗约 49-52 RH 币。
第三步(核心):首尾帧链式生成
为什么需要这一步?
如果你直接用同一张静态参考图分别生成两段视频,虽然人物一样,但每段视频的起始姿态、光线、背景位置都会不同。拼接在一起时会出现明显的「跳切」——上一个镜头人往左看,下一个镜头人突然往右看,观感非常割裂。
解决思路
第1段视频最后一帧的画面状态
↓
作为第2段视频的输入参考图
↓
第2段从这个姿态「接着演」
这样两段视频之间的人物姿态、表情、光线角度都是连续的,拼接后观感就像一镜到底。
具体操作
1) 用 ffmpeg 提取第1段视频的最后一帧
# 方法一:用 -sseof 参数从末尾往前seek
ffmpeg -sseof -1 -i scene1.mp4 \
-vframes 1 -q:v 2 \
last_frame.jpg -y
# 方法二:已知总帧数,直接定位(更精确)
ffmpeg -i scene1.mp4 \
-vf "select=eq(n\,242)" \
-vframes 1 last_frame.jpg -y
-sseof -1 表示从文件末尾往前偏移 1 秒,-vframes 1 只取 1 帧。-q:v 2 保证输出质量。
2) 上传尾帧到 RunningHub
curl -X POST 'https://www.runninghub.cn/openapi/v2/media/upload/binary' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-F 'file=@./last_frame.jpg'
3) 用尾帧作为参考图提交第2段视频
和第2步完全一样的 API 调用,唯一区别是把 nodeId: 137 的 image 字段值换成尾帧的 fileName:
{
"nodeId": "137",
"fieldName": "image",
"fieldValue": "openapi/尾帧的fileName.jpg"
}
提示词中可以加一句 seamlessly continuing from the previous shot 强调连贯性。
效果对比
| 方式 | 衔接效果 | 说明 |
|---|---|---|
| 同一张参考图 | ❌ 跳切明显 | 两段起始姿态不同,拼接后有断裂感 |
| 尾帧链式传递 | ✅ 自然过渡 | 第2段从第1段结束的画面状态开始,连贯 |
第四步:视频拼接(可选)
两段视频都生成后,可以用 ffmpeg 无损拼接:
# 创建文件列表
echo "file 'scene1.mp4'" > filelist.txt
echo "file 'scene2.mp4'" >> filelist.txt
# concat 拼接
ffmpeg -f concat -safe 0 -i filelist.txt \
-c copy output_final.mp4 -y
-c copy 表示不重新编码,直接拷贝流,速度极快且无画质损失。前提是两段视频的编码参数(分辨率、帧率、编码格式)一致——使用同一个工作流生成的视频天然满足这个条件。
第五步:TTS 配音叠加(可选)
RunningHub 的图生视频工作流自带的音频效果有限。如果你想要清晰的中文人声配音,建议用 edge-tts 单独生成音频,再叠加到视频上:
# 生成 TTS 音频
edge-tts --voice zh-CN-YunxiNeural \
--text "以前我以为长大能叱咤风云,长大之后才搞明白,我只能风吹雨打" \
--write-media audio1.mp3
# 叠加到视频(替换原始音频)
ffmpeg -i scene1.mp4 -i audio1.mp3 \
-c:v copy -c:a aac -map 0:v:0 -map 1:a:0 \
scene1_with_voice.mp4 -y
如果音频和视频时长不完全匹配,可以用 -shortest 参数自动截取:
ffmpeg -i scene1.mp4 -i audio1.mp3 \
-c:v copy -c:a aac -map 0:v:0 -map 1:a:0 \
-shortest scene1_final.mp4 -y
完整流程代码(Python 脚本)
把整个流程串起来,核心代码长这样:
import requests
import subprocess
import time
API_KEY = "你的RunningHub API Key"
BASE_URL = "https://www.runninghub.cn/openapi/v2"
HEADERS = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}
# ---- 工具函数 ----
def upload_image(path):
"""上传图片到 RunningHub"""
resp = requests.post(
f"{BASE_URL}/media/upload/binary",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": open(path, "rb")}
)
return resp.json()["data"]["fileName"]
def submit_task(app_id, node_info):
"""提交 ComfyUI 工作流任务"""
resp = requests.post(
f"{BASE_URL}/run/ai-app/{app_id}",
headers=HEADERS,
json={
"nodeInfoList": node_info,
"instanceType": "default",
"usePersonalQueue": "false"
}
)
return resp.json()["taskId"]
def poll_result(task_id, interval=30, timeout=600):
"""轮询任务结果,返回下载 URL"""
start = time.time()
while time.time() - start < timeout:
resp = requests.post(
f"{BASE_URL}/query",
headers=HEADERS,
json={"taskId": task_id}
)
data = resp.json()
if data["status"] == "SUCCESS":
return data["results"][0]["url"]
elif data["status"] == "FAILED":
raise Exception(f"Task failed: {data}")
time.sleep(interval)
raise TimeoutError("Task polling timeout")
def extract_last_frame(video_path, output_path):
"""用 ffmpeg 提取视频最后一帧"""
subprocess.run([
"ffmpeg", "-sseof", "-1", "-i", video_path,
"-vframes", "1", "-q:v", "2", output_path, "-y"
], check=True, capture_output=True)
# ---- 主流程 ----
def main():
# 1. 图生图:换装
print("📷 Step 1: 图生图编辑...")
original_fname = upload_image("./original.jpg")
edit_task = submit_task(
"2088926295186034689", # Kera2Edit
[
{"nodeId": "160", "fieldName": "text",
"fieldValue": "Remove camouflage...keep face..."},
{"nodeId": "104", "fieldName": "image",
"fieldValue": original_fname}
]
)
edit_url = poll_result(edit_task)
# 下载编辑后的图片...
# 2. 生成第1段视频
print("🎬 Step 2: 生成第1段视频...")
edited_fname = upload_image("./edited_photo.jpg")
v1_task = submit_task(
"2088844222551121921", # AnimateDiff + MiniMax H3
[
{"nodeId": "138", "fieldName": "value",
"fieldValue": "subject_definitions:..."},
{"nodeId": "137", "fieldName": "image",
"fieldValue": edited_fname}
]
)
v1_url = poll_result(v1_task)
# 下载第1段视频...
# 3. 提取尾帧 → 生成第2段视频(核心!)
print("🔗 Step 3: 提取尾帧,链式生成第2段...")
extract_last_frame("./scene1.mp4", "./last_frame.jpg")
lf_fname = upload_image("./last_frame.jpg")
v2_task = submit_task(
"2088844222551121921",
[
{"nodeId": "138", "fieldName": "value",
"fieldValue": "subject_definitions:...(续)"},
{"nodeId": "137", "fieldName": "image",
"fieldValue": lf_fname} # 用尾帧!
]
)
v2_url = poll_result(v2_task)
# 4. 拼接
print("✂️ Step 4: 拼接视频...")
with open("filelist.txt", "w") as f:
f.write("file 'scene1.mp4'\n")
f.write("file 'scene2.mp4'\n")
subprocess.run([
"ffmpeg", "-f", "concat", "-safe", "0",
"-i", "filelist.txt", "-c", "copy",
"output.mp4", "-y"
], check=True)
print("🎉 Done!")
if __name__ == "__main__":
main()
费用一览
| 环节 | 工作流 | 耗时 | RH 币消耗 |
|---|---|---|---|
| 图片上传 | - | ~2秒 | 免费 |
| 图生图 | Kera2Edit | 60-90秒 | 14-17 币 |
| 图生视频 ×2 | AnimateDiff + MiniMax H3 | 4-5分钟/段 | 49-52 币/段 |
| 总计 | 约12分钟 | 约120 币 |
常见问题
Q: 两段视频的分辨率不一样怎么办?
同一个工作流生成的视频分辨率是固定的(本案例为 1152×640),不会有这个问题。如果混用不同工作流,需要在拼接前用 ffmpeg 统一分辨率:
ffmpeg -i input.mp4 -vf scale=1152:640 -c:a copy output.mp4 -y
Q: 尾帧提取出来是黑帧怎么办?
可能是因为视频末尾有黑场。用 -sseof -0.5 从末尾 0.5 秒处提取,避开黑场:
ffmpeg -sseof -0.5 -i scene1.mp4 -vframes 1 last_frame.jpg -y
Q: 并发提交被限流怎么办?
RunningHub 对同一 API Key 有并发限制(错误码 421)。解决方案:串行提交,等上一段完成后再提交下一段。正好我们的链式流程天然是串行的——必须等第1段完成才能提取尾帧。
Q: 提示词中能用英文吗?
可以。MiniMax H3 引擎对中英文都有不错的理解力。6 段式结构中的画面描述用英文、台词用中文(放在 <d>[Chinese]...</d> 标签中)是推荐的混合用法。
总结
整条链路的核心就三句话:
- 图生图解决「人不对」的问题——先把照片改成你想要的风格
- 图生视频解决「不动」的问题——让静态照片变成有表情有动作的视频
- 尾帧传递解决「不连贯」的问题——用上段结尾做下段开头,实现无缝衔接
这套流程不局限于口播视频。任何需要多段连续 AI 视频的场景——剧情短片、产品展示、教程录制——都可以复用这个「首尾帧链式生成」的思路。
关键就在那一帧:提取、上传、作为下一段的起点。一个小操作,换来的是观感上的质的飞跃。
本文基于 RunningHub ComfyUI 云端工作流实战总结,工作流 App ID 均为当前可用版本。API 文档详见 RunningHub 开放平台。

更多推荐


所有评论(0)