这里写目录标题
-
- 一、整体实现架构
- 二、前置准备(零额外环境,复用原有配置)
- 三、完整实战实现(以小学数学《9的乘法口诀》为例)
-
- [3.1 第一步:配置化定义互动式视频片段(核心,仅需改此文件)](#3.1 第一步:配置化定义互动式视频片段(核心,仅需改此文件))
- [3.2 第二步:核心代码实现(复用原有模块+新增互动功能)](#3.2 第二步:核心代码实现(复用原有模块+新增互动功能))
- [3.3 第三步:一键实操运行步骤](#3.3 第三步:一键实操运行步骤)
- 四、交互视频播放与网课平台适配
-
- [4.1 本地播放测试(验证跳转功能)](#4.1 本地播放测试(验证跳转功能))
- [4.2 网课平台适配(学习通/腾讯课堂/钉钉)](#4.2 网课平台适配(学习通/腾讯课堂/钉钉))
- [4.3 效果演示(文字版)](#4.3 效果演示(文字版))
- 五、优化技巧(提升互动体验与视频质量)
-
- [5.1 跳转精准性优化](#5.1 跳转精准性优化)
- [5.2 互动体验优化](#5.2 互动体验优化)
- [5.3 算力与生成速度优化](#5.3 算力与生成速度优化)
- 六、常见问题排错(解决实操痛点)
互动式教育视频是网课/课堂教学的核心进阶需求,相比单向讲解视频,通过知识点讲解→提问引导→点击跳转解答的交互形式,能显著提升学生参与度和知识点吸收效率。
本文基于Wan2.2-T2V-A5B核心模型,结合moviepy实现配置化生成多段视频片段+互动拼接+可点击跳转的交互MP4生成 ,生成的视频兼容学习通/腾讯课堂/钉钉等主流网课平台,支持播放器内点击跳转,无需额外插件。
核心价值
- 适配K12/成人教育全学段,支持数学/语文/科普等所有学科;
- 完全配置化操作,新增互动视频仅需修改JSON配置,无需改核心代码;
- 复用原有Wan2.2-T2V-A5B模型/多模态模块(语音/公式),无额外技术门槛;
- 生成的交互MP4兼容主流播放器(PotPlayer/MPV)和网课平台,点击即可跳转。
关键技术点
- 多段片段生成 :基于Wan2.2-T2V-A5B批量生成讲解/提问/解答三类视频片段;
- 交互实现原理 :利用MP4的章节点(Chapter)+ 超链接特性实现跳转,兼容网课平台;
- 可视化提示:在视频画面添加互动引导文字(如「点击此处查看解答」),提升操作体验;
- 无缝拼接 :通过
moviepy实现多段片段的帧间无缝衔接,保证播放流畅性。
一、整体实现架构
互动式教育视频的实现全程复用原有Wan2.2-T2V-A5B核心模块,仅新增批量片段生成 和互动拼接两个模块,整体架构如下:
配置化定义互动片段(讲解/提问/解答)
↓
Wan2.2-T2V-A5B批量生成多段视频片段(复用核心模型/语音/公式模块)
↓
moviepy片段预处理(统一分辨率/帧率/时长,保证无缝衔接)
↓
【新增】互动标记模块:添加跳转节点+超链接+可视化互动提示
↓
【新增】交互MP4生成:拼接片段+写入章节跳转信息+导出成品
↓
网课平台适配测试(学习通/腾讯课堂)
二、前置准备(零额外环境,复用原有配置)
- 环境依赖:完全复用原有Wan2.2-T2V-A5B的Python环境,仅需新增1个轻量依赖(用于MP4章节写入):
bash
# 用于写入MP4章节/超链接,实现点击跳转(CSDN可直接复制运行)
pip install pymediainfo mp4box -i https://pypi.tuna.tsinghua.edu.cn/simple
- 模块复用 :直接复用原有核心模块,无需修改:
Wan22T2VA5BInfer:Wan2.2-T2V-A5B视频生成;EduPromptEngine:教育专属提示词生成;EduTTS:语音合成;EduFormulaHighlight:公式/文字叠加;EduVideoPost:视频后处理。
三、完整实战实现(以小学数学《9的乘法口诀》为例)
以小学数学三年级《9的乘法口诀》 为实战案例,实现:
- 讲解片段(20s):讲解9的乘法口诀核心知识点+公式;
- 提问片段(10s):提出互动问题「7×9=?,点击查看解答」;
- 解答片段(15s):解答问题+口诀强化;
- 成品效果:播放到提问片段时,点击画面提示区域,直接跳转到解答片段。
3.1 第一步:配置化定义互动式视频片段(核心,仅需改此文件)
在原有edu_config.json基础上,新增互动片段配置 ,按讲解/提问/解答分类,定义每个片段的类型/内容/时长/公式/互动跳转目标,无需改任何代码。
互动式教育视频专属配置文件:interactive_edu_config.json
json
{
"global_config": {
"video_id": "math_9_multi_001",
"subject": "小学数学",
"grade": "三年级",
"knowledge_point": "表内乘法-9的乘法口诀",
"resolution": [720, 1280],
"fps": 30,
"style": "极简卡通",
"use_formula_highlight": true,
"use_bgm": true,
"bgm_path": "./bgm/edu_light.mp3",
"bgm_volume": 0.1
},
"interactive_clips": [
{
"clip_id": "clip1_explain",
"clip_type": "explain",
"duration": 20,
"explain_text": "同学们,今天学习9的乘法口诀,一九得九,二九十八,三九二十七,四九三十六,五九四十五,六九五十四,七九六十三,八九七十二,九九八十一。",
"formula_list": [
{
"content": "1\\times9=9\\quad2\\times9=18\\quad3\\times9=27",
"show_time": [3, 18],
"position": [300, 100],
"size": 40
}
],
"tts_config": {"voice_type": "小学老师", "speed": 0.9, "volume": 5}
},
{
"clip_id": "clip2_question",
"clip_type": "question",
"duration": 10,
"explain_text": "现在来考考大家,7乘以9等于多少呢?点击屏幕中间的提示区域,查看正确答案吧!",
"formula_list": [
{
"content": "7\\times9=?",
"show_time": [0, 10],
"position": [500, 300],
"size": 60
}
],
"tts_config": {"voice_type": "小学老师", "speed": 0.8, "volume": 5},
"interactive": {
"prompt_text": "点击此处查看解答",
"prompt_position": [400, 450],
"prompt_size": 36,
"jump_to_clip": "clip3_answer"
}
},
{
"clip_id": "clip3_answer",
"clip_type": "answer",
"duration": 15,
"explain_text": "正确答案是63,七九六十三,大家记住了吗?9的乘法口诀要多背多练哦!",
"formula_list": [
{
"content": "7\\times9=63\\quad\\text{七九六十三}",
"show_time": [2, 13],
"position": [400, 300],
"size": 56
}
],
"tts_config": {"voice_type": "小学老师", "speed": 0.9, "volume": 5}
}
],
"api_config": {
"xfyun": {
"APPID": "【替换为你的】",
"API_KEY": "【替换为你的】",
"API_SECRET": "【替换为你的】"
}
}
}
关键配置项说明
| 配置项 | 作用 | 互动核心点 |
|---|---|---|
interactive_clips |
定义所有互动片段,按讲解→提问→解答顺序排列 | 片段ID唯一,用于跳转标记 |
clip_type |
片段类型:explain(讲解)/question(提问)/answer(解答) |
仅question片段需配置interactive |
interactive |
互动配置(提问片段专属) | 定义提示文字/位置/跳转目标片段ID |
jump_to_clip |
跳转目标片段ID | 实现「提问→解答」的点击跳转核心 |
3.2 第二步:核心代码实现(复用原有模块+新增互动功能)
所有代码基于原有Wan2.2-T2V-A5B代码体系增量开发 ,保留原有模块的复用性,新增批量片段生成 和互动拼接+跳转实现两个核心函数,代码注释详细,可直接复制到CSDN运行。
完整可运行代码:interactive_edu_video.py
python
import json
import os
import torch
import moviepy.editor as mp
import matplotlib.pyplot as plt
from moviepy.video.VideoClip import TextClip
from moviepy.video.compositing.CompositeVideoClip import CompositeVideoClip
from moviepy.audio.fx.all import volumex
import subprocess
# -------------------------- 全局配置与原有模块复用(无需修改) --------------------------
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
MODEL_PATH = "./wan22_model" # 原有模型权重路径
OUTPUT_DIR = "./interactive_edu_video"
CLIP_DIR = f"{OUTPUT_DIR}/clips" # 多段片段保存目录
TEMP_DIR = f"{OUTPUT_DIR}/temp"
os.makedirs(OUTPUT_DIR, exist_ok=True)
os.makedirs(CLIP_DIR, exist_ok=True)
os.makedirs(TEMP_DIR, exist_ok=True)
# 复用原有核心模块(Wan2.2-T2V-A5B/TTs/公式高亮)
from wan_edu_core import (
Wan22T2VA5BInfer, EduPromptEngine, EduTTS,
EduFormulaHighlight, EduBGM, EduVideoPost
)
# -------------------------- 【新增】1. 批量生成互动视频片段 --------------------------
def batch_generate_clips(config):
"""
基于Wan2.2-T2V-A5B批量生成讲解/提问/解答片段
:param config: 互动配置文件
:return: 片段路径字典 {clip_id: clip_path}
"""
global_config = config["global_config"]
interactive_clips = config["interactive_clips"]
api_config = config["api_config"]
# 初始化核心模块(仅初始化1次,提升效率)
wan_infer = Wan22T2VA5BInfer()
prompt_engine = EduPromptEngine()
formula_highlight = EduFormulaHighlight()
bgm_merger = EduBGM()
post_process = EduVideoPost({"font": "黑体", "font_size":24, "color":"#FFF", "bg_color":"#000", "position":"bottom"})
clip_path_dict = {}
for clip in interactive_clips:
clip_id = clip["clip_id"]
clip_type = clip["clip_type"]
duration = clip["duration"]
explain_text = clip["explain_text"]
formula_list = clip["formula_list"]
tts_config = clip["tts_config"]
print(f"\n开始生成片段:{clip_id} - {clip_type}")
# 步骤1:生成教育专属提示词(区分片段类型,适配提问/解答场景)
prompt = prompt_engine.generate_prompt(
subject=global_config["subject"],
knowledge_point=f"{global_config['knowledge_point']}-{clip_type}",
style=global_config["style"],
duration=duration
)
# 提问片段额外添加「互动提示画面」关键词
if clip_type == "question":
prompt += ",画面中间显示问题,无多余元素,互动感强"
print(f"片段提示词:{prompt}")
# 步骤2:Wan2.2-T2V-A5B生成视频画面
video_path = wan_infer.generate_edu_video(
prompt=prompt,
style=global_config["style"],
duration=duration,
resolution=global_config["resolution"],
fps=global_config["fps"]
)
# 步骤3:多模态融合(语音+公式)
tts = EduTTS(api_config["xfyun"], tts_config)
audio_path = tts.generate_audio(explain_text, use_api=True, video_id=clip_id)
audio_video_path = post_process.merge_audio_video(video_path, audio_path)
formula_video_path = formula_highlight.add_formula_to_video(
audio_video_path, formula_list, clip_id, global_config["resolution"]
)
# 步骤4:融合背景音乐
bgm_video_path = bgm_merger.merge_bgm(formula_video_path, clip_id)
# 步骤5:保存片段到专属目录
final_clip_path = f"{CLIP_DIR}/{clip_id}.mp4"
import shutil
shutil.copy(bgm_video_path, final_clip_path)
clip_path_dict[clip_id] = final_clip_path
print(f"片段生成完成:{final_clip_path}")
return clip_path_dict
# -------------------------- 【新增】2. 互动拼接+跳转实现(核心) --------------------------
def generate_interactive_video(config, clip_path_dict):
"""
拼接多段片段,添加互动提示文字+跳转节点,生成可点击跳转的交互MP4
:param config: 互动配置文件
:param clip_path_dict: 片段路径字典
:return: 交互MP4路径
"""
global_config = config["global_config"]
interactive_clips = config["interactive_clips"]
video_id = global_config["video_id"]
resolution = global_config["resolution"]
fps = global_config["fps"]
# 步骤1:加载所有片段,统一帧率/分辨率(保证无缝拼接)
clip_list = []
jump_mark_dict = {} # 跳转标记 {clip_id: (start_time, end_time)}
total_duration = 0.0
for clip in interactive_clips:
clip_id = clip["clip_id"]
clip_path = clip_path_dict[clip_id]
clip_mp = mp.VideoFileClip(clip_path).set_fps(fps)
# 统一分辨率
clip_mp = clip_mp.resize(size=(resolution[1], resolution[0]))
# 记录片段时间轴(用于跳转标记)
jump_mark_dict[clip_id] = (total_duration, total_duration + clip_mp.duration)
clip_list.append(clip_mp)
total_duration += clip_mp.duration
# 步骤2:拼接所有片段
merged_clip = mp.concatenate_videoclips(clip_list, method="compose")
merged_path = f"{TEMP_DIR}/{video_id}_merged.mp4"
merged_clip.write_videofile(merged_path, fps=fps, codec="libx264", audio_codec="aac")
print(f"\n片段拼接完成:{merged_path}")
# 步骤3:为提问片段添加「可视化互动提示文字」(如"点击此处查看解答")
interactive_merged_path = add_interactive_prompt(config, merged_path, jump_mark_dict)
# 步骤4:写入MP4跳转节点(章节点+超链接),实现点击跳转
final_interactive_path = f"{OUTPUT_DIR}/{video_id}_interactive_final.mp4"
write_mp4_jump_node(config, interactive_merged_path, final_interactive_path, jump_mark_dict)
# 清理临时文件
for f in os.listdir(TEMP_DIR):
os.remove(f"{TEMP_DIR}/{f}")
return final_interactive_path
# -------------------------- 【新增】2.1 添加可视化互动提示文字 --------------------------
def add_interactive_prompt(config, merged_path, jump_mark_dict):
"""
为提问片段添加互动提示文字(如"点击此处查看解答"),画面居中醒目
"""
global_config = config["global_config"]
interactive_clips = config["interactive_clips"]
merged_clip = mp.VideoFileClip(merged_path)
video_clips = [merged_clip]
for clip in interactive_clips:
clip_id = clip["clip_id"]
clip_type = clip["clip_type"]
# 仅为提问片段添加互动提示
if clip_type != "question":
continue
# 互动提示配置
interactive = clip["interactive"]
prompt_text = interactive["prompt_text"]
prompt_position = interactive["prompt_position"]
prompt_size = interactive["prompt_size"]
# 片段时间轴
start_time, end_time = jump_mark_dict[clip_id]
# 生成互动提示文字剪辑(红底白字,醒目,全程显示在提问片段)
prompt_clip = TextClip(
prompt_text, font="黑体", fontsize=prompt_size,
color="#FFFFFF", bg_color="#FF0000", stroke_width=2
).set_position(prompt_position)\
.set_start(start_time)\
.set_end(end_time)\
.set_duration(merged_clip.duration)
video_clips.append(prompt_clip)
print(f"为提问片段{clip_id}添加互动提示:{prompt_text}")
# 合成带互动提示的视频
interactive_merged_clip = CompositeVideoClip(video_clips)
interactive_merged_path = f"{TEMP_DIR}/{global_config['video_id']}_with_prompt.mp4"
interactive_merged_clip.write_videofile(interactive_merged_path, fps=global_config["fps"], codec="libx264")
return interactive_merged_path
# -------------------------- 【新增】2.2 写入MP4跳转节点(实现点击跳转核心) --------------------------
def write_mp4_jump_node(config, input_mp4, output_mp4, jump_mark_dict):
"""
利用mp4box写入MP4章节点+超链接,实现点击跳转
兼容学习通/腾讯课堂/PotPlayer等平台/播放器
:param input_mp4: 带互动提示的拼接视频
:param output_mp4: 最终交互MP4
:param jump_mark_dict: 片段时间轴
"""
interactive_clips = config["interactive_clips"]
# 生成mp4box的章节/跳转命令
mp4box_cmds = ["mp4box", "-add", input_mp4, "-new", output_mp4]
chapter_idx = 1
# 步骤1:写入章节点(每个片段为一个章节,用于定位)
for clip in interactive_clips:
clip_id = clip["clip_id"]
start_time, end_time = jump_mark_dict[clip_id]
# 章节格式:-chap start=时间(ms),end=时间(ms),name=章节名
start_ms = int(start_time * 1000)
end_ms = int(end_time * 1000)
mp4box_cmds.extend([
"-chap",
f"start={start_ms},end={end_ms},name={clip_id}"
])
chapter_idx += 1
# 步骤2:写入超链接(提问片段→解答片段,实现点击跳转)
for clip in interactive_clips:
if clip["clip_type"] != "question":
continue
interactive = clip["interactive"]
jump_to_clip = interactive["jump_to_clip"]
# 提问片段的时间范围(点击此区域触发跳转)
q_start, q_end = jump_mark_dict[clip["clip_id"]]
# 解答片段的开始时间(跳转目标)
a_start = jump_mark_dict[jump_to_clip][0]
# 超链接格式:-link start=ms,end=ms,dest=目标时间(ms)
q_start_ms = int(q_start * 1000)
q_end_ms = int(q_end * 1000)
a_start_ms = int(a_start * 1000)
mp4box_cmds.extend([
"-link",
f"start={q_start_ms},end={q_end_ms},dest={a_start_ms}"
])
print(f"写入跳转链接:{clip['clip_id']} → {jump_to_clip}({q_start}s → {a_start}s)")
# 执行mp4box命令,生成交互MP4
subprocess.run(mp4box_cmds, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(f"交互MP4生成完成,跳转节点已写入:{output_mp4}")
# -------------------------- 【新增】3. 主函数:端到端生成互动式教育视频 --------------------------
def generate_interactive_edu_video(config_path):
"""
端到端生成可点击跳转的互动式教育视频
:param config_path: 互动配置文件路径
"""
# 步骤1:加载配置文件
with open(config_path, "r", encoding="utf-8") as f:
config = json.load(f)
print("配置文件加载完成,开始生成互动式教育视频...")
# 步骤2:批量生成互动片段
clip_path_dict = batch_generate_clips(config)
print(f"\n所有片段生成完成:{clip_path_dict}")
# 步骤3:拼接片段+添加互动+写入跳转,生成最终交互MP4
final_video_path = generate_interactive_video(config, clip_path_dict)
print(f"\n✅ 互动式教育视频生成完成!")
print(f"成品路径:{final_video_path}")
print(f"播放说明:在学习通/腾讯课堂/PotPlayer中播放,点击提问片段的红色提示文字区域,可直接跳转到解答片段!")
# -------------------------- 程序入口:一键运行 --------------------------
if __name__ == "__main__":
# 运行核心函数(传入互动配置文件路径)
generate_interactive_edu_video("interactive_edu_config.json")
3.3 第三步:一键实操运行步骤
步骤1:目录结构校验
确保项目目录新增interactive_edu_video和bgm文件夹,结构如下:
WanEduVideo/
├─ wan_edu_core.py # 原有核心模块文件
├─ interactive_edu_config.json # 互动配置文件
├─ interactive_edu_video.py # 新增互动视频代码
├─ wan22_model/ # 原有模型权重
├─ bgm/ # 背景音乐文件夹
│ └─ edu_light.mp3 # 网课轻音乐
└─ interactive_edu_video/ # 自动生成,保存片段和成品
├─ clips/ # 多段互动片段
└─ temp/ # 临时文件
步骤2:激活虚拟环境
bash
cd E:\WanEduVideo
venv\Scripts\activate
步骤3:一键生成互动式教育视频
bash
python interactive_edu_video.py
步骤4:运行结果
- 自动在
interactive_edu_video/clips/生成讲解/提问/解答3个片段; - 最终在
interactive_edu_video/生成可点击跳转的交互MP4:math_9_multi_001_interactive_final.mp4; - 控制台打印跳转链接信息,确认提问→解答的跳转节点已写入。
四、交互视频播放与网课平台适配
4.1 本地播放测试(验证跳转功能)
使用支持MP4超链接的播放器打开成品视频,推荐:
- PotPlayer(Windows):播放到提问片段时,点击画面中间的红色提示文字区域,直接跳转到解答片段;
- MPV播放器 (跨平台):支持点击跳转,可通过
Ctrl+O打开视频测试。
4.2 网课平台适配(学习通/腾讯课堂/钉钉)
生成的交互MP4原生兼容国内主流网课平台,无需额外转换,直接上传即可:
- 学习通/超星:上传视频到课程章节,学生在学习通APP/网页端播放时,点击提问区域即可跳转解答;
- 腾讯课堂:上传到「课程视频」模块,支持播放器内点击跳转,适配直播回放/点播;
- 钉钉课堂:直接上传到钉盘,分享到课堂,兼容钉钉内置播放器。
4.3 效果演示(文字版)
【0-20s 讲解片段】:9的乘法口诀讲解,画面显示口诀公式;
【20-30s 提问片段】:画面中间显示"7×9=?",下方红色提示"点击此处查看解答",点击该区域→直接跳转到30s;
【30-45s 解答片段】:画面显示"7×9=63 七九六十三",讲解答案。
五、优化技巧(提升互动体验与视频质量)
5.1 跳转精准性优化
- 提问片段时长控制 :建议提问片段时长8-15s,给学生足够的思考和点击时间;
- 跳转区域全覆盖 :将提问片段的整个画面 设为跳转区域(配置中
prompt_position居中,提示文字醒目); - 时间轴校准 :批量生成片段时,确保每个片段的实际时长与配置时长一致,避免跳转偏移。
5.2 互动体验优化
- 提示文字样式 :使用红底白字+描边 (
stroke_width=2),保证在任何画面下都醒目; - 背景音乐适配 :提问片段的背景音乐音量降低10% (修改
bgm_volume为0.08),突出问题引导; - 画面简洁化 :提问/解答片段的提示词添加「无多余元素,画面干净」,避免干扰学生注意力。
5.3 算力与生成速度优化
- 片段切片大小调整 :低显卡(3060 12G)将
wan_edu_core.py中的slice_size改为4,降低显存占用; - 批量生成复用模型:核心模型仅初始化1次,避免重复加载,提升多片段生成速度;
- 降低去噪步数 :将
Wan22T2VA5BInfer中的self.scheduler.set_timesteps(50)改为30,生成速度提升40%。
六、常见问题排错(解决实操痛点)
问题1:运行mp4box时报错「找不到命令」
- 原因:mp4box未加入系统环境变量;
- 解决方案 :
-
下载FFmpeg/mp4box工具包,解压到项目根目录;
-
在代码中指定mp4box绝对路径,如:
pythonmp4box_path = "./mp4box/mp4box.exe" mp4box_cmds = [mp4box_path, "-add", input_mp4, "-new", output_mp4]
-
问题2:点击提示区域无法跳转
- 原因1:播放器不支持MP4超链接(如Windows自带播放器)→ 更换PotPlayer/学习通播放;
- 原因2 :跳转节点写入失败→ 检查控制台是否有
mp4box执行报错,重新运行代码; - 原因3:片段时间轴偏移→ 确保每个片段的实际时长与配置时长一致。
问题3:片段拼接处卡顿/跳帧
-
原因:片段的帧率/分辨率不一致;
-
解决方案 :在
generate_interactive_video函数中,添加强制重编码 :pythonclip_mp = clip_mp.resize(size=(resolution[1], resolution[0])).set_fps(fps).write_videofile(f"{TEMP_DIR}/{clip_id}_tmp.mp4", codec="libx264") clip_mp = mp.VideoFileClip(f"{TEMP_DIR}/{clip_id}_tmp.mp4")
问题4:低显卡显存不足
- 解决方案 :
- 将片段时长拆分更短(如讲解片段20s→15s);
- 降低分辨率为[480, 854];
- 关闭公式高亮(
use_formula_highlight=false)。