日期:2026-05-09
公开说明:本文是下一阶段产品化架构草案,包含历史实验背景和当时的 Web MVP 设想。当前开源仓库的可运行入口以根目录 README.md 和 docs/live-photo-generation.md 为准;模板媒体资产需要用户自行提供。
本文档面向下一阶段生产级前后端开发,目标是把当前已验证的 .livp 生成能力,升级为可商业化运营的 Web 平台。
如果目标是先做可安装在用户 Mac 上运行的本地应用,见 Mac 本机可安装版技术方案。
平台提供 AI 实况壁纸生成能力:
- 用户输入提示词、上传参考图或选择模板。
- 平台生成封面图、动态视频和 iOS 可识别的
.livp文件。 - 生成结果可以通过百度网盘、Safari 下载、文件 App、AirDrop 等方式导入 iPhone。
- 导入相册后显示为 Live Photo,并且可以设置为锁屏动态壁纸。
第一版核心不是做最丰富的编辑器,而是保证交付文件稳定可用。
当前验证结论决定生产方案:
- 只写基础 Live Photo 元数据不够,iOS Photos 可识别,但锁屏动态效果不可用。
- iPhone 原生
mebx不能作为通用模板迁移到任意视频。 - 已验证的中性
mebx轨道可以迁移到自定义视频,并可作为锁屏动态壁纸。 .livp不只是普通 ZIP 改后缀,必须包含兼容的内部文件名和 ZIP 注释。- 当前稳定规格为
1080x1920 / 1s / 60fps / HEVC hvc1 / 0.5s cover / silent AAC。
因此生产 MVP 固定输出规格,不开放任意比例、任意时长、任意帧率。
推荐:
Next.js
React
TypeScript
Tailwind CSS 或 shadcn/ui
TanStack Query
原因:
- 适合快速做商业 Web 产品。
- 支持服务端渲染、静态页面、API 路由和后续 SEO。
- TypeScript 可以降低前后端接口漂移。
- TanStack Query 适合轮询异步生成任务状态。
- 第一版页面状态简单,暂不需要 Zustand/Jotai 这类全局状态库。
第一版页面:
- 首页即生成工作台,不做营销落地页。
- 生成任务页。
- 结果下载页。
- 简单模板选择可以先内置在工作台里。
- 订单、历史作品和管理后台可以后置。
推荐:
Node.js
PostgreSQL
S3 兼容对象存储
FFmpeg
Swift LivePhotoPackager CLI
说明:
- Node.js 负责 Web API、任务编排和业务状态;鉴权、支付可以后置。
- PostgreSQL 存储任务、作品、素材、模型调用日志和任务状态;用户、订单、支付表可以后置。
- 第一版不引入 BullMQ/Redis,后台生成进程直接从 PostgreSQL 任务表领取任务。
- 对象存储保存上传素材、AI 生成资产、
.livp、预览图和日志文件。 - Swift CLI 继续负责 Live Photo 元数据写入和
mebx轨道注入。 - NestJS 不是第一版必须项。如果开发速度优先,可以先用 Next.js API routes 或轻量 Node API;等业务模块增多后再迁移到 NestJS。
生产代码不要硬编码某个具体模型名,而是做模型适配层:
ImageProvider
VideoProvider
PromptProvider
ModerationProvider
模型选择通过配置控制:
IMAGE_PROVIDER=openai
IMAGE_MODEL=gpt-image-1.5
VIDEO_PROVIDER=openai
VIDEO_MODEL=sora-2
如果后续 gpt-image-2 或其他视频模型可用,只需要替换配置和 provider 适配器,不改业务主流程。
官方文档当前可参考:
为了尽快做出可付费验证的版本,以下技术或模块先不进入第一版:
Redis / BullMQ
SSE / WebSocket
Zustand / Jotai
独立管理后台
完整模板市场
订单系统和支付系统
CDN
Load Balancer
PostgreSQL 主从
复杂日志平台
复杂监控告警系统
Kubernetes
多模型调度系统
Linux 化打包链路
第一版保留最小闭环:
用户上传视频或输入提示词
-> 创建生成任务
-> 后台 Worker 生成 `.livp`
-> 用户下载
-> iPhone 真机验证
暂缓这些技术不是否定它们,而是避免在商业验证前背上过多工程成本。
flowchart LR
U["用户浏览器"] --> FE["Next.js 前端"]
FE --> API["业务 API 服务"]
API --> DB["PostgreSQL"]
API --> Store["对象存储"]
Worker["生成 Worker"] --> DB
Worker --> AIImg["图像生成 Provider"]
Worker --> AIVid["视频生成 Provider"]
Worker --> FF["FFmpeg 归一化"]
Worker --> Pack["Swift LivePhotoPackager"]
Pack --> Livp[".livp 打包器"]
Worker --> QA["结构与兼容性校验"]
Worker --> Store
FE --> Store
核心原则:
- HTTP API 只创建任务,不直接执行视频生成。
- 生成任务先写入 PostgreSQL,由后台 Worker 轮询领取并执行。
- 所有中间产物都落对象存储,方便排障和复跑。
.livp输出必须经过结构校验后才能给用户下载。
sequenceDiagram
participant User as 用户
participant Web as 前端
participant API as API 服务
participant DB as PostgreSQL
participant Worker as 生成 Worker
participant AI as AI Provider
participant Packager as 打包链路
participant Store as 对象存储
User->>Web: 输入提示词 / 上传参考素材
Web->>API: POST /api/jobs
API->>Store: 保存用户素材
API->>DB: 写入 queued 任务
API-->>Web: 返回 jobId
Web->>API: 轮询任务状态
Worker->>DB: 领取 queued 任务
Worker->>AI: 生成封面图 / 视频
AI-->>Worker: 返回素材
Worker->>Packager: 归一化 + 写元数据 + 打包 .livp
Packager-->>Worker: 输出 .livp
Worker->>Store: 上传结果文件
Worker->>DB: 更新任务成功
API-->>Web: 返回下载地址
输入视频
-> FFmpeg 归一化到 1080x1920 / 1s / 60fps / HEVC hvc1
-> 生成静音 AAC
-> 0.5s 抽封面
-> Swift CLI 写图片 MakerApple[17]
-> Swift CLI 写 MOV content.identifier
-> Swift CLI 注入中性 mebx 轨道
-> 生成 HEIC + MOV
-> 使用 IMB_xxxxxxxx.HEIC.heic / IMB_xxxxxxxx.HEIC.mov 作为内部文件名
-> zip -0 -X 打包
-> 写入 ZIP 注释
-> 自动校验
-> 上传对象存储
/ 生成工作台
/jobs/[jobId] 生成进度和结果
/works 我的作品
/works/[workId] 作品详情和下载
后置页面:
/templates 模板市场
/orders 订单记录
/admin 管理后台入口
/admin/jobs 任务排障
/admin/templates 模板管理
/admin/assets 资产管理
第一版工作台包含:
- 少量内置模板选择。
- 提示词输入。
- 参考图上传。
- 画面比例固定为竖屏。
- 生成按钮。
- 任务进度。
- 视频预览。
.livp下载。- iPhone 导入说明。
不要让用户选择帧率、时长、编码格式。生产初期这些属于内部稳定性参数。
前端使用轮询即可,后续再升级为 SSE 或 WebSocket。
queued 排队中
generating AI 生成中
packaging 打包中
validating 校验中
succeeded 成功
failed 失败
cancelled 已取消
结果页展示:
.livp下载按钮。- 预览视频。
- 文件大小。
- 生成参数。
- 推荐导入路径。
- 百度网盘导入提示。
- 常见失败原因。
MVP 可以是一个后端仓库,内部模块清晰拆分:
api-service
jobs
assets
works
templates
worker-service
image-generation
video-generation
media-normalization
livephoto-packaging
validation
第一版可以暂不做完整账号、订单、支付和管理后台。用户身份可以先用匿名 sessionId 或简单登录,等下载付费链路确定后再补完整用户体系。
第一版可以先把 API 和 Worker 放在同一个代码仓库里,但用两个进程启动:
api 处理 HTTP 请求
worker 轮询 PostgreSQL 任务表并执行生成
业务量上来后,再考虑引入 Redis/BullMQ 或云队列。
POST /api/jobs
GET /api/jobs/:jobId
POST /api/jobs/:jobId/cancel
GET /api/works
GET /api/works/:workId
GET /api/works/:workId/download
GET /api/templatesuploads/presign、支付接口和管理后台接口可以第二阶段再加。
创建任务请求:
{
"templateId": "tpl_float_bag",
"prompt": "透明塑料袋在水中缓慢漂浮,柔和光线,竖屏壁纸",
"referenceAssetId": "asset_123",
"outputSpec": "ios-live-wallpaper-v1"
}任务状态响应:
{
"jobId": "job_123",
"status": "packaging",
"progress": 72,
"stage": "livephoto_packaging",
"previewUrl": "https://cdn.example.com/preview.mp4",
"downloadUrl": null,
"error": null
}每个生成任务必须有:
jobIdidempotencyKeyinputHashmodelConfigSnapshotpackagingSpecVersionattempt
同一用户重复提交相同请求时,可以复用结果或防止重复扣费。
第一版不单独引入队列中间件,Worker 直接从 jobs 表领取任务。
推荐领取逻辑:
UPDATE jobs
SET
status = 'generating',
locked_by = $1,
locked_at = now(),
attempt = attempt + 1,
updated_at = now()
WHERE id = (
SELECT id
FROM jobs
WHERE status = 'queued'
ORDER BY created_at ASC
FOR UPDATE SKIP LOCKED
LIMIT 1
)
RETURNING *;需要增加字段:
locked_by
locked_at
attempt
last_error_at
Worker 每次只领取一个任务,执行完成后更新状态。进程异常退出时,可以通过 locked_at 超时把任务重新置为 queued 或 failed。
users
id
email
phone
auth_provider
created_at
templates
id
name
cover_asset_id
prompt_preset
status
created_at
assets
id
owner_user_id
type
storage_key
mime_type
size_bytes
width
height
duration_ms
checksum
created_at
jobs
id
user_id
template_id
status
progress
stage
prompt
input_asset_id
output_work_id
locked_by
locked_at
attempt
error_code
error_message
last_error_at
packaging_spec_version
model_config_json
created_at
updated_at
works
id
user_id
job_id
preview_asset_id
livp_asset_id
source_video_asset_id
status
created_at
orders
id
user_id
work_id
amount
currency
payment_provider
payment_status
created_at
generation_logs
id
job_id
stage
provider
request_id
latency_ms
cost_estimate
status
created_at
第一版最小表可以只有:
jobs
assets
works
templates
generation_logs
users 可以用匿名 sessionId 过渡;orders 等支付链路跑通后再加。
works 或 jobs 中必须记录:
packaging_spec_version = ios-live-wallpaper-v1
template_mebx_version = vendor-neutral-v1
zip_comment_version = livp-comment-v1
ffmpeg_profile = 1080x1920-1s-60fps-hvc1-v1
未来如果调整 2 秒、30fps 或自研中性 mebx,可以按版本灰度。
users/{userId}/uploads/{assetId}/source
jobs/{jobId}/inputs/source
jobs/{jobId}/ai/image.png
jobs/{jobId}/ai/video.mp4
jobs/{jobId}/normalized/video.mov
jobs/{jobId}/packaging/live-photo.heic
jobs/{jobId}/packaging/live-photo.mov
jobs/{jobId}/outputs/output.livp
jobs/{jobId}/logs/ffmpeg.log
jobs/{jobId}/logs/packager.log
jobs/{jobId}/validation/report.json
生产环境不要只保留最终 .livp。中间产物是排障、复跑和质量分析的基础。
第一版如果不做登录,可以把 users/{userId} 改成:
sessions/{sessionId}/uploads/{assetId}/source
每次生成后必须自动校验。
分辨率 = 1080x1920
时长 = 1.0s
帧率 = 60fps
编码 = HEVC hvc1
音频 = AAC
封面时间 = 0.5s
图片存在 MakerApple[17]
MOV 存在 content.identifier
图片 UUID 与视频 UUID 一致
MOV 存在 live-photo-info mebx 轨道
MOV 存在 still-image-time 轨道
live-photo-info 样本数量符合当前模板
ZIP 内部只有 HEIC/JPG + MOV
内部文件名符合 IMB_xxxxxxxx.HEIC.heic/mov
ZIP 使用 store 模式或与样本兼容的模式
ZIP 注释存在
ZIP 注释 offset/size 与真实文件一致
末尾包含 1000LIVP 标记
校验失败的文件不能进入下载态。
Vercel / Node Web
-> API 服务
云服务器 / GPU 或 CPU Worker
-> FFmpeg
-> Swift CLI
-> 打包器
托管 PostgreSQL
S3 / R2 / OSS / COS
如果 AI 生成使用外部 API,Worker 不一定需要 GPU,但需要稳定 CPU、磁盘和较好的网络。
CDN 可以后置。第一版下载量不大时,对象存储签名 URL 或 API 透传下载已经够用。
Load Balancer
-> API Service x N
-> Worker Service x N
-> Admin Service
PostgreSQL 主从
对象存储 + CDN
日志系统
监控告警
后续如果任务量变大,再升级为队列架构:
PostgreSQL 任务表
-> Redis / BullMQ
-> image-generation queue
-> video-generation queue
-> packaging queue
-> validation queue
- 上传文件大小限制。
- 上传 MIME 和真实格式校验。
- 提示词内容审核。
- 生成结果审核。
- 禁止违法、侵权、成人、公众人物滥用等内容。
- API 鉴权。
- 下载链接短期签名。
- 对象存储私有桶。
- 生成接口限流。
- 每用户并发任务限制。
管理后台权限和支付 webhook 验签等到对应模块上线时再做。
- 用户提交任务前预估成本。
- AI 调用失败重试次数有限。
- 包装失败只重跑包装,不重跑 AI。
- 免费用户低并发。
- 高成本模型仅付费用户可用。
商业化可以按阶段推进。
验证期先不急着接支付系统,建议先做:
免费生成少量样本
人工收款或邀请码发放下载权限
记录用户是否成功设置锁屏
验证用户是否愿意为结果付费
确认需求后再接正式支付:
免费预览
付费下载 .livp
单张购买
套餐点数
模板专题包
订单状态:
created
pending
paid
fulfilled
refunded
failed
扣费建议发生在生成成功后下载前,减少“用户付费但生成失败”的客服成本。
必须记录:
- 每个 job 的阶段耗时。
- AI provider request id。
- FFmpeg 日志。
- Swift packager 日志。
.livp校验报告。- 下载次数。
- 用户反馈的导入路径和失败原因。
关键指标:
生成成功率
打包成功率
校验通过率
平均生成耗时
P95 生成耗时
单任务平均成本
用户反馈的锁屏成功率
支付转化率、退款率等指标等正式支付上线后再加入。
所有会影响兼容性的参数都必须版本化:
ios-live-wallpaper-v1
1080x1920
1s
60fps
HEVC hvc1
vendor-neutral mebx v1
livp ZIP comment v1
新规格不能直接替换:
ios-live-wallpaper-v2
2s
60fps
new neutral mebx
每个版本都要有真实 iPhone 验证矩阵。
- Next.js 前端工程。
- Node API 工程。
- PostgreSQL schema。
- 基于 PostgreSQL 的任务表和后台 Worker。
- 对象存储接入。
- 当前
make-livp.sh封装为 Worker 任务。
- 接入图像生成 provider。
- 接入视频生成 provider。
- 完成任务状态页。
- 完成
.livp下载。 - 增加结构校验。
- 用户系统。
- 订单系统。
- 支付 webhook。
- 作品库。
- 下载鉴权。
- 简单管理后台。
- 生成失败重试。
- Worker 横向扩容。
- 日志和基础告警。
- 多 iOS 版本验证矩阵。
- 导入渠道验证。
当前文件:
scripts/make-livp.sh
Sources/LivePhotoPackager/main.swift
tools/set-livp-zip-comment.js
web/server.mjs
web/static/*
迁移方式:
保留 Swift CLI 和打包工具
-> 抽成 packages/livephoto-packager
保留 make-livp.sh 的逻辑
-> 改成 worker 内部 MediaPipeline
废弃当前 web/server.mjs
-> 替换为 Node API + PostgreSQL 任务表
保留 web/static 的交互验证经验
-> 替换为 Next.js 工作台
.livpZIP 注释单元测试。.livp内部文件名测试。- FFmpeg 输出规格测试。
- MOV
mebx轨道存在性测试。 - 图片和视频 UUID 一致性测试。
- 失败任务重试测试。
- 大文件上传测试。
- 并发生成测试。
- iOS 后续版本改变锁屏动态壁纸校验规则。
- 第三方导入渠道改变
.livp识别逻辑。 - 不同 AI 视频模型输出导致压缩后观感不稳定。
- 当前中性
mebx来源需要进一步合法性评估。
- 用户不知道如何导入相册和设置锁屏。
- 百度网盘等渠道体验不稳定。
- 生成内容质量不稳定影响付费转化。
- 使用第三方
.livp作为逆向参考需要法律评估。 - 模板、提示词、生成结果可能涉及版权或肖像权。
- 付费下载前后需要明确退款规则。
立即进入生产开发时,建议按这个顺序:
- 建立
apps/web和apps/api。 - 建立
packages/livephoto,把现有打包链路包进去。 - 建立 PostgreSQL schema 和 job 任务表。
- 先支持“上传视频 -> 生成
.livp”的生产级异步任务。 - 再接 AI 图像和视频生成。
- 用真实用户验证导入、锁屏和付费意愿。
- 最后接支付、模板市场和管理后台。
这样可以先把最关键的 .livp 交付链路工程化,再把 AI 和商业化能力接上去。
当前 Swift LivePhotoPackager CLI 不是纯 Swift 跨平台程序,它依赖 Apple 平台媒体框架,因此生产部署需要特别处理。
Sources/LivePhotoPackager/main.swift 使用了:
AVFoundation
CoreGraphics
ImageIO
UniformTypeIdentifiers
Foundation
关键能力:
ImageIO读取图片、写入 HEIC/JPG、写入MakerApple[17]。AVFoundation读取 MOV、复制 video/audio/metadata track、写入 QuickTime metadata。AVAssetReader/AVAssetWriter负责保留和迁移mebxmetadata track。UniformTypeIdentifiers负责输出图片格式标识。
这些能力依赖 macOS/iOS 系统框架,普通 Linux 服务器没有这些框架。
当前 Package.swift 声明:
platforms: macOS v13+
swift-tools-version: 5.9
构建机器需要:
macOS 13 或更高
Xcode 或 Command Line Tools
Swift 5.9 或更高
本机已验证环境:
Xcode 26.4.1
生产环境可以在 CI 中编译出二进制,再分发到 macOS Worker。运行时仍然需要 macOS,因为二进制链接 Apple 系统框架。
scripts/make-livp.sh 除了 Swift CLI,还依赖:
ffmpeg
hevc_videotoolbox
swift run livephoto-packager
node
python3
zip
uuidgen
vendor-livp/IMB_ZyUbrU.HEIC.heic
vendor-livp/IMB_ZyUbrU.HEIC.mov
其中 hevc_videotoolbox 是 Apple VideoToolbox 硬件/系统编码器,也要求 macOS。若要在 Linux 上运行,需要把 FFmpeg 编码参数改成 libx265,但这会改变编码链路,需要重新做 iPhone 锁屏兼容性验证。
MVP 阶段推荐:
API 服务:Linux 容器 / 普通云服务器
数据库:托管 PostgreSQL
生成 Worker:macOS 机器
可选 macOS Worker 来源:
Mac mini 自托管
MacStadium
AWS EC2 Mac
其他支持 macOS 的 CI/服务器供应商
不推荐第一版直接把打包链路迁移到 Linux。原因是当前通过真实 iPhone 验证成功的是 macOS AVFoundation + VideoToolbox + compatible .livp 这条链路,换成 Linux 原生 MP4/MOV/HEIC 库后需要重新验证所有锁屏兼容性。
Web/API 服务
-> 创建 job
-> 写入 PostgreSQL 任务表
macOS Worker
-> 轮询并领取 job
-> 下载输入素材
-> 调用 FFmpeg + Swift CLI + ZIP comment 工具
-> 校验 `.livp`
-> 上传对象存储
-> 回写 job 状态
macOS Worker 应该做成无状态服务:
- 不保存长期用户数据。
- 本地只使用临时目录。
- 每个 job 结束后清理中间文件。
- 所有输入、输出、日志都上传对象存储。
未来可以评估重写打包器,以降低 macOS Worker 成本:
图片元数据:libheif / exiftool / ImageMagick
MOV metadata:Bento4 / GPAC / FFmpeg / 自研 MOV box 写入
mebx track:自研 QuickTime box 复制和时间轴重写
ZIP comment:当前 Node 工具可跨平台保留
HEVC 编码:libx265 或云转码服务
但这条路线需要重新验证:
- iOS Photos 是否仍识别 Live Photo。
- iPhone 锁屏动态效果是否可用。
- 百度网盘等第三方导入路径是否仍能保存。
- 不同 iOS 版本是否一致。
所以短期商业化优先选择 macOS Worker,长期再做 Linux 化降本。