当前位置: 首页 > news >正文

【Easylive】transferVideoFile 方法详细解析

【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版

transferVideoFile 方法是一个视频文件转码和处理的核心方法,主要完成视频文件从临时目录转移到正式目录、合并分片、获取视频信息、更新状态等一系列操作。下面我将详细解析这个方法的每个部分。

首先,方法被@Override注解标记,说明这是一个重写的方法。方法接收一个VideoInfoFilePost对象作为参数,这应该是一个包含视频文件相关信息的实体类。方法内部首先创建了一个updateFilePost对象,用于后续更新数据库中的文件信息。接着进入一个try块,开始主要的业务逻辑。

在try块中,首先通过redisComponent.getUploadVideoFile方法从Redis获取上传文件的信息,这里需要传入用户ID和上传ID。这表明系统使用Redis来临时存储上传过程中的文件信息,可能是为了支持断点续传或分片上传。

接下来,构建了临时文件路径tempFilePath和目标文件路径targetFilePath。这里用到了appConfig.getProjectFolder()获取项目根目录,然后拼接上常量FILE_FOLDER、FILE_FOLDER_TEMP和FILE_VIDEO,以及从Redis获取的fileDto.getFilePath()。这说明系统有明确的目录结构来管理临时文件和最终存储的视频文件。

检查目标目录是否存在,如果不存在则创建,确保目录结构正确。然后使用FileUtils.copyDirectory将临时目录下的文件复制到目标目录。这一步可能是将上传完成的文件从临时位置移动到正式存储位置。之后,删除临时目录,释放存储空间,并从Redis中删除对应的文件信息,避免数据冗余。

接下来调用this.union方法合并文件,生成完整的视频文件路径completeVideo。union方法可能负责将分片上传的视频文件合并成一个完整的文件。合并完成后,使用fFmpegUtils.getVideoInfoDuration获取视频时长,并更新updateFilePost的相关属性,包括时长、文件大小、文件路径和转码结果状态为成功。

然后调用this.convertVideo2Ts方法将视频转换为TS格式,可能是为了支持流媒体播放,比如HLS协议。转换完成后删除原始视频文件,保留转换后的TS分片。

如果在上述过程中出现异常,catch块会捕获异常并记录错误日志,同时将转码结果状态设置为失败。无论成功或失败,finally块都会执行,更新数据库中的文件状态。

在finally块中,首先通过videoInfoFilePostMapper.updateByUploadIdAndUserId更新文件信息。接着,构建查询条件,检查是否存在转码失败的文件。如果有失败的文件,更新整个视频的状态为STATUS1(可能表示转码失败)。如果没有失败的文件,再检查是否还有正在转码中的文件。如果所有文件都已处理完成,计算视频总时长并更新视频状态为STATUS2(转码完成),同时记录总时长。

方法概述

@Override
public void transferVideoFile(VideoInfoFilePost videoInfoFilePost) {
    // 方法实现
}

功能:处理视频文件转码和转移流程,包括:

  1. 从临时目录转移视频到正式目录
  2. 合并视频分片(如果有)
  3. 获取视频元信息(时长等)
  4. 更新数据库状态
  5. 处理转码失败情况
  6. 检查并更新视频整体状态

1. 初始化与准备

VideoInfoFilePost updateFilePost = new VideoInfoFilePost();
try {
    // 从Redis获取上传文件信息
    UploadingFileDto fileDto = redisComponent.getUploadVideoFile(videoInfoFilePost.getUserId(), videoInfoFilePost.getUploadId());
    
    // 设置临时文件路径
    String tempFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_FOLDER_TEMP + fileDto.getFilePath();
    File tempFile = new File(tempFilePath);
    
    // 设置目标文件路径
    String targetFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_VIDEO + fileDto.getFilePath();
    File targetFile = new File(targetFilePath);
    if (!targetFile.exists()) {
        targetFile.mkdirs();
    }

解析
• 创建 updateFilePost 对象用于后续更新数据库
• 从Redis获取上传文件信息(包含文件路径等)
• 构造临时文件路径和目标文件路径
• 确保目标目录存在(不存在则创建)

2. 文件转移与处理

    // 将文件从临时目录复制到正式目录
    FileUtils.copyDirectory(tempFile, targetFile);
    
    // 删除临时目录
    FileUtils.forceDelete(tempFile);
    
    // 从Redis删除文件信息
    redisComponent.delVideoFileInfo(videoInfoFilePost.getUserId(), videoInfoFilePost.getUploadId());
    
    // 合并文件(如果是分片上传)
    String completeVideo = targetFilePath + Constants.TEMP_VIDEO_NAME;
    this.union(targetFilePath, completeVideo, true);

解析
• 使用 FileUtils.copyDirectory 将文件从临时目录复制到正式目录
• 删除临时目录(清理空间)
• 从Redis删除已处理的文件信息
• 调用 union 方法合并视频分片(如果有)

3. 获取视频信息

    // 获取视频时长
    Integer duration = fFmpegUtils.getVideoInfoDuration(completeVideo);
    updateFilePost.setDuration(duration);
    updateFilePost.setFileSize(new File(completeVideo).length());
    updateFilePost.setFilePath(Constants.FILE_VIDEO + fileDto.getFilePath());
    updateFilePost.setTransferResult(VideoFileTransferResultEnum.SUCCESS.getStatus());
    
    // 将视频转换为TS格式(用于HLS流媒体)
    this.convertVideo2Ts(completeVideo);

解析
• 使用 fFmpegUtils 获取视频时长
• 设置文件大小、路径和转码结果为成功
• 调用 convertVideo2Ts 将视频转换为TS格式(用于HTTP Live Streaming)

4. 异常处理

} catch (Exception e) {
    log.error("文件转码失败", e);
    updateFilePost.setTransferResult(VideoFileTransferResultEnum.FAIL.getStatus());
}

解析
• 捕获所有异常并记录日志
• 设置转码结果为失败状态

5. 状态更新与检查

finally {
    // 更新文件状态
    videoInfoFilePostMapper.updateByUploadIdAndUserId(updateFilePost, videoInfoFilePost.getUploadId(), videoInfoFilePost.getUserId());
    
    // 检查是否有转码失败的视频文件
    VideoInfoFilePostQuery fileQuery = new VideoInfoFilePostQuery();
    fileQuery.setVideoId(videoInfoFilePost.getVideoId());
    fileQuery.setTransferResult(VideoFileTransferResultEnum.FAIL.getStatus());
    Integer failCount = videoInfoFilePostMapper.selectCount(fileQuery);
    
    if (failCount > 0) {
        // 如果有失败的,设置视频状态为STATUS1(可能是"转码失败"状态)
        VideoInfoPost videoUpdate = new VideoInfoPost();
        videoUpdate.setStatus(VideoStatusEnum.STATUS1.getStatus());
        videoInfoPostMapper.updateByVideoId(videoUpdate, videoInfoFilePost.getVideoId());
        return;
    }
    
    // 检查是否还有转码中的视频文件
    fileQuery.setTransferResult(VideoFileTransferResultEnum.TRANSFER.getStatus());
    Integer transferCount = videoInfoFilePostMapper.selectCount(fileQuery);
    
    if (transferCount == 0) {
        // 如果没有转码中的文件,计算总时长并更新视频状态为STATUS2(可能是"转码完成"状态)
        Integer duration = videoInfoFilePostMapper.sumDuration(videoInfoFilePost.getVideoId());
        VideoInfoPost videoUpdate = new VideoInfoPost();
        videoUpdate.setStatus(VideoStatusEnum.STATUS2.getStatus());
        videoUpdate.setDuration(duration);
        videoInfoPostMapper.updateByVideoId(videoUpdate, videoInfoFilePost.getVideoId());
    }
}

解析

  1. 更新文件状态:无论成功失败,都更新当前文件状态
  2. 检查失败情况
    • 查询该视频下是否有转码失败的文件
    • 如果有,设置视频整体状态为失败(STATUS1)
  3. 检查转码完成情况
    • 查询是否还有转码中的文件
    • 如果没有,计算视频总时长并更新状态为完成(STATUS2)

状态枚举说明

假设的状态枚举含义(根据代码推断):

枚举值可能含义
STATUS0待审核/待处理
STATUS1转码失败
STATUS2转码完成
TRANSFER转码中
SUCCESS转码成功
FAIL转码失败

方法流程图

开始
从Redis获取文件信息
设置临时和目标路径
复制文件到正式目录
删除临时文件
合并分片
获取视频元信息
转换为TS格式
更新状态为成功
异常处理
更新状态为失败
更新文件状态
检查失败文件?
设置视频状态为失败
检查转码中文件?
计算总时长并更新状态为完成
结束

关键点总结

  1. 文件转移流程
    • 从临时目录 → 正式目录
    • 清理临时文件
    • 删除Redis缓存

  2. 视频处理
    • 合并分片(如需要)
    • 获取元信息(时长、大小)
    • 转换为TS格式(用于流媒体)

  3. 状态管理
    • 单个文件状态更新
    • 整体视频状态检查
    • 失败处理和成功状态更新

  4. 事务完整性
    • 使用try-catch确保异常被捕获
    • finally块确保状态一定会更新
    • 全面的状态检查机制

可能的优化建议

  1. 异步处理:视频转码是耗时操作,可以考虑使用消息队列异步处理
  2. 进度反馈:可以增加转码进度反馈机制
  3. 重试机制:对于失败的转码任务可以加入重试逻辑
  4. 资源清理:确保在任何异常情况下都能正确清理临时资源
  5. 状态枚举:建议使用更具描述性的枚举名称,如"TRANSCODE_COMPLETED"而非"STATUS2"

相关文章:

  • 边缘计算的崛起:当计算从“云端漫步“变成“街头快闪“
  • ZLG嵌入式笔记 | 文件系统异步写入引发的问题
  • 魔改chromium——基础环境搭建
  • Go语言深度解析:从Java到Go的范式革命与实践指南
  • linux发布程序常用脚本
  • Skl-Videolingo-v2.0(VideoLingo):打破语言壁垒的下一代视频本地化工具
  • 云安全入门
  • spring-ai-alibaba第二章ollama集成EmbeddingModel
  • pyexcelerate在写入Excel时为何效率高?
  • Kotlin 协程官方文档知识汇总(二)
  • 详解隔离级别(4种),分别用表格展示问题出现的过程及解决办法
  • Geotools结合SLD实现矢量中文标注下的乱码和可用字体解析
  • 基于JavaWeb的二手图书交易系统(源码+lw+部署文档+讲解),源码可白嫖!
  • 【云原生】Kubernetes CEL 速查表
  • 【Git “fetch“ 命令详解】
  • Spring Boot中事务状态(TransactionStatus)的核心信息及常见应用场景
  • Android Photo Picker 深入解析与实战指南
  • 【视觉与语言模型参数解耦】为什么?方案?
  • Qt之共享内存类QSharedMemory的使用及实现原理(全)
  • 3.第二阶段x64游戏实战-分析人物移动实现人物加速
  • 即日起,“应急使命·2025”演习公开征集新质救援能力
  • 向左繁华都市,向右和美乡村,嘉兴如何打造城乡融合发展样本
  • 中国代表:美“对等关税”和歧视性补贴政策严重破坏世贸规则
  • 杭州挂牌临平区两宗住宅用地,起始总价约11.02亿元
  • 国务院安委办、应急管理部进一步调度部署“五一”假期安全防范工作
  • 中央网信办:重点整治违规AI产品、利用AI制作发布谣言等突出问题