【剪映小助手源码精讲】第29章 视频生成服务

第29章 视频生成服务

29.1 概述

视频生成服务是剪映小助手的核心功能之一,负责将编辑完成的草稿导出为最终的视频文件。该服务采用异步处理模式,通过任务管理系统实现视频渲染的提交、处理和状态查询。服务支持云端渲染,能够处理复杂的视频合成任务,并提供实时的任务状态反馈。

29.2 核心实现

29.2.1 视频生成任务提交

视频生成服务的核心实现位于 src/service/gen_video.py 文件中,包含两个主要功能:

python 复制代码
async def gen_video(request: GenVideoRequest) -> GenVideoResponse:
    """提交视频生成任务"""
    logger.info(f"提交视频生成任务: {request.draft_url}")
    
    # 参数验证
    if not request.draft_url:
        raise ValueError("草稿URL不能为空")
    
    # 获取草稿缓存
    draft = await get_draft_cache(request.draft_url)
    if not draft:
        raise INVALID_DRAFT_URL
    
    # 生成任务ID
    task_id = f"video_gen_{uuid.uuid4().hex}"
    
    # 创建任务数据
    task_data = {
        'draft_url': request.draft_url,
        'draft_data': draft.to_dict(),
        'create_time': time.time(),
        'status': 'pending',
        'progress': 0
    }
    
    # 提交任务到任务管理器
    try:
        task_manager.submit_task(task_id, 'video_generation', task_data)
        logger.info(f"视频生成任务提交成功: {task_id}")
        
        return GenVideoResponse(
            message=f"视频生成任务已提交,任务ID: {task_id}"
        )
        
    except Exception as e:
        logger.error(f"视频生成任务提交失败: {str(e)}")
        raise VIDEO_GENERATION_SUBMIT_FAILED

29.2.2 任务状态查询

查询视频生成任务的状态:

python 复制代码
async def gen_video_status(task_id: str) -> dict:
    """查询视频生成任务状态"""
    logger.info(f"查询视频生成任务状态: {task_id}")
    
    # 参数验证
    if not task_id:
        raise ValueError("任务ID不能为空")
    
    # 从任务管理器获取任务状态
    try:
        task_info = task_manager.get_task_status(task_id)
        if not task_info:
            raise VIDEO_TASK_NOT_FOUND
        
        logger.info(f"任务状态查询成功: {task_info.get('status')}")
        
        return {
            'task_id': task_id,
            'status': task_info.get('status'),
            'progress': task_info.get('progress', 0),
            'message': task_info.get('message', ''),
            'result_url': task_info.get('result_url'),
            'create_time': task_info.get('create_time'),
            'update_time': task_info.get('update_time'),
            'error': task_info.get('error')
        }
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"任务状态查询失败: {str(e)}")
        raise VIDEO_STATUS_QUERY_FAILED

29.3 云端渲染与异步处理

29.3.1 任务管理系统

视频生成采用任务管理系统进行异步处理:

python 复制代码
class TaskManager:
    """任务管理器"""
    
    def __init__(self):
        self.tasks = {}                    # 任务存储
        self.workers = {}                  # 工作进程
        self.task_queue = asyncio.Queue()  # 任务队列
    
    def submit_task(self, task_id: str, task_type: str, task_data: dict):
        """提交任务"""
        task_info = {
            'task_id': task_id,
            'task_type': task_type,
            'task_data': task_data,
            'status': 'pending',
            'progress': 0,
            'create_time': time.time(),
            'update_time': time.time()
        }
        
        # 存储任务信息
        self.tasks[task_id] = task_info
        
        # 添加到处理队列
        asyncio.create_task(self.process_task(task_id))
        
        return task_id
    
    async def process_task(self, task_id: str):
        """处理任务"""
        task_info = self.tasks.get(task_id)
        if not task_info:
            return
        
        try:
            # 更新状态为处理中
            task_info['status'] = 'processing'
            task_info['progress'] = 0
            task_info['update_time'] = time.time()
            
            # 执行视频生成
            if task_info['task_type'] == 'video_generation':
                await self.generate_video(task_id, task_info)
            
        except Exception as e:
            # 处理失败
            task_info['status'] = 'failed'
            task_info['error'] = str(e)
            task_info['update_time'] = time.time()
            logger.error(f"任务处理失败 {task_id}: {str(e)}")

29.3.2 视频渲染流程

视频渲染的具体实现流程:

python 复制代码
async def generate_video(self, task_id: str, task_info: dict):
    """生成视频"""
    task_data = task_info['task_data']
    draft_data = task_data['draft_data']
    
    try:
        # 步骤1: 准备渲染环境
        logger.info(f"开始准备渲染环境: {task_id}")
        task_info['progress'] = 10
        task_info['message'] = '准备渲染环境'
        
        # 创建临时工作目录
        work_dir = f"/tmp/video_gen_{task_id}"
        os.makedirs(work_dir, exist_ok=True)
        
        # 步骤2: 解析草稿数据
        logger.info(f"解析草稿数据: {task_id}")
        task_info['progress'] = 20
        task_info['message'] = '解析草稿数据'
        
        draft = Draft.from_dict(draft_data)
        
        # 步骤3: 准备素材文件
        logger.info(f"准备素材文件: {task_id}")
        task_info['progress'] = 30
        task_info['message'] = '准备素材文件'
        
        await self.prepare_materials(draft, work_dir)
        
        # 步骤4: 视频合成
        logger.info(f"开始视频合成: {task_id}")
        task_info['progress'] = 50
        task_info['message'] = '视频合成中'
        
        output_file = await self.compose_video(draft, work_dir)
        
        # 步骤5: 上传结果文件
        logger.info(f"上传结果文件: {task_id}")
        task_info['progress'] = 80
        task_info['message'] = '上传结果文件'
        
        result_url = await self.upload_result(output_file)
        
        # 步骤6: 完成任务
        logger.info(f"视频生成完成: {task_id}")
        task_info['status'] = 'completed'
        task_info['progress'] = 100
        task_info['message'] = '视频生成完成'
        task_info['result_url'] = result_url
        task_info['update_time'] = time.time()
        
        # 清理临时文件
        shutil.rmtree(work_dir, ignore_errors=True)
        
    except Exception as e:
        logger.error(f"视频生成失败 {task_id}: {str(e)}")
        task_info['status'] = 'failed'
        task_info['error'] = str(e)
        task_info['update_time'] = time.time()
        
        # 清理临时文件
        if os.path.exists(work_dir):
            shutil.rmtree(work_dir, ignore_errors=True)

29.3.3 视频合成实现

视频合成的具体实现:

python 复制代码
async def compose_video(self, draft: Draft, work_dir: str) -> str:
    """合成视频"""
    # 输出文件路径
    output_file = os.path.join(work_dir, 'output.mp4')
    
    # 构建FFmpeg命令
    cmd = [
        'ffmpeg',
        '-y',  # 覆盖输出文件
        '-f', 'rawvideo',
        '-vcodec', 'rawvideo',
        '-s', f'{draft.width}x{draft.height}',
        '-pix_fmt', 'rgb24',
        '-r', str(draft.fps),
        '-i', '-',  # 从标准输入读取
        '-f', 'mp4',
        '-vcodec', 'libx264',
        '-pix_fmt', 'yuv420p',
        output_file
    ]
    
    # 执行视频合成
    process = await asyncio.create_subprocess_exec(
        *cmd,
        stdin=asyncio.subprocess.PIPE,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE
    )
    
    # 生成视频帧数据
    frame_data = await self.generate_frames(draft)
    
    # 写入帧数据
    stdout, stderr = await process.communicate(input=frame_data)
    
    if process.returncode != 0:
        raise Exception(f"视频合成失败: {stderr.decode()}")
    
    return output_file

29.4 任务状态管理

29.4.1 任务状态定义

视频生成任务的状态定义:

python 复制代码
class TaskStatus:
    PENDING = "pending"                # 待处理
    PROCESSING = "processing"          # 处理中
    COMPLETED = "completed"            # 已完成
    FAILED = "failed"                  # 已失败
    CANCELLED = "cancelled"            # 已取消
    TIMEOUT = "timeout"                # 超时

29.4.2 任务进度更新

实时更新任务进度:

python 复制代码
def update_task_progress(self, task_id: str, progress: int, message: str):
    """更新任务进度"""
    task_info = self.tasks.get(task_id)
    if task_info:
        task_info['progress'] = progress
        task_info['message'] = message
        task_info['update_time'] = time.time()
        
        # 发送进度更新事件
        self.send_progress_event(task_id, progress, message)

29.4.3 任务超时处理

处理任务超时情况:

python 复制代码
async def check_task_timeout(self):
    """检查任务超时"""
    current_time = time.time()
    timeout_seconds = 3600  # 1小时超时
    
    for task_id, task_info in self.tasks.items():
        if task_info['status'] == 'processing':
            if current_time - task_info['update_time'] > timeout_seconds:
                # 任务超时
                task_info['status'] = 'timeout'
                task_info['error'] = '任务处理超时'
                task_info['update_time'] = current_time
                
                logger.warning(f"任务超时: {task_id}")

29.5 数据结构定义

29.5.1 请求参数模型

视频生成服务的请求参数定义在 src/schemas/gen_video.py 中:

python 复制代码
class GenVideoRequest(BaseModel):
    """根据草稿导出视频"""
    draft_url: str = Field(default="", description="草稿URL")

29.5.2 响应参数模型

python 复制代码
class GenVideoResponse(BaseModel):
    """生成视频响应参数"""
    message: str = Field(..., description="响应消息")

29.6 异常处理

视频生成服务定义了完善的异常处理机制:

python 复制代码
# 无效的草稿URL
INVALID_DRAFT_URL = HTTPException(
    status_code=400,
    detail="无效的草稿URL"
)

# 视频生成任务提交失败
VIDEO_GENERATION_SUBMIT_FAILED = HTTPException(
    status_code=500,
    detail="视频生成任务提交失败"
)

# 视频任务未找到
VIDEO_TASK_NOT_FOUND = HTTPException(
    status_code=404,
    detail="视频生成任务未找到"
)

# 视频状态查询失败
VIDEO_STATUS_QUERY_FAILED = HTTPException(
    status_code=500,
    detail="视频生成任务状态查询失败"
)

29.7 API接口定义

29.7.1 视频生成接口

python 复制代码
@router.post("/genVideo", response_model=GenVideoResponse)
async def gen_video_endpoint(request: GenVideoRequest):
    """生成视频"""
    try:
        return await gen_video(request)
    except Exception as e:
        logger.error(f"生成视频失败: {str(e)}")
        raise VIDEO_GENERATION_SUBMIT_FAILED

29.7.2 任务状态查询接口

python 复制代码
@router.get("/genVideoStatus/{task_id}")
async def gen_video_status_endpoint(task_id: str):
    """查询视频生成任务状态"""
    try:
        return await gen_video_status(task_id)
    except Exception as e:
        logger.error(f"查询视频状态失败: {str(e)}")
        raise VIDEO_STATUS_QUERY_FAILED

29.8 使用示例

29.8.1 视频生成请求示例

json 复制代码
{
    "draft_url": "capcut://draft/123456789"
}

29.8.2 视频生成响应示例

json 复制代码
{
    "message": "视频生成任务已提交,任务ID: video_gen_abc123def456"
}

29.8.3 任务状态查询响应示例

json 复制代码
{
    "task_id": "video_gen_abc123def456",
    "status": "processing",
    "progress": 65,
    "message": "视频合成中",
    "result_url": null,
    "create_time": 1640995200,
    "update_time": 1640995265,
    "error": null
}

29.9 性能优化

视频生成服务采用了多种性能优化策略:

29.9.1 异步处理优化

python 复制代码
# 使用连接池管理HTTP连接
connector = aiohttp.TCPConnector(
    limit=100,                         # 连接池大小
    limit_per_host=30,                 # 每个主机的连接数
    ttl_dns_cache=300,               # DNS缓存时间
    use_dns_cache=True,                # 启用DNS缓存
)

# 创建会话
session = aiohttp.ClientSession(
    connector=connector,
    timeout=aiohttp.ClientTimeout(total=30)
)

29.9.2 内存管理优化

python 复制代码
# 使用内存映射文件处理大文件
import mmap

def process_large_file(file_path):
    with open(file_path, 'rb') as f:
        # 创建内存映射
        with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
            # 处理文件内容
            return process_data(mm)

29.9.3 缓存优化

python 复制代码
# 使用LRU缓存
from functools import lru_cache

@lru_cache(maxsize=128)
def get_video_template(template_id):
    """获取视频模板(带缓存)"""
    return load_template_from_db(template_id)

29.10 扩展性设计

视频生成服务具有良好的扩展性:

  • 渲染引擎扩展:支持接入不同的视频渲染引擎
  • 输出格式扩展:支持多种视频输出格式
  • 云服务商扩展:支持多个云渲染服务商
  • 任务类型扩展:支持添加新的任务类型

附录

代码仓库地址:

  • GitHub: https://github.com/Hommy-master/capcut-mate
  • Gitee: https://gitee.com/taohongmin-gitee/capcut-mate

接口文档地址:

  • API文档地址: https://docs.jcaigc.cn
相关推荐
副露のmagic2 小时前
更弱智的算法学习 day9
python·学习·算法
weixin_421585012 小时前
SpatialTransformer库函数分析二
python·keras
Pyeako2 小时前
python中pandas库的使用(超详细)
开发语言·python·pandas
Data_agent2 小时前
京东获得京东商品详情API,python请求示例
java·前端·爬虫·python
Cherry的跨界思维2 小时前
27、Python压缩备份安全指南:从zipfile到AES-256加密,生产级自动化备份全方案
人工智能·python·安全·自动化·办公自动化·python自动化·python办公自动化
Raink老师2 小时前
第 8 章 Python 中的 I/O
python
micro_cloud_fly2 小时前
langchain langgraph历史会话的 json序列化
python·langchain·json
Mintopia2 小时前
🚀 技术并购视角:AIGC领域的 Web 生态整合与资源重组
人工智能·llm·aigc
般若Neo2 小时前
AI视频生成技术原理与行业应用 - AI视频概览
人工智能·aigc·ai视频