FFmpeg音视频处理解决方案
核心组件:
ffmpeg:主要的命令行工具,用于转码、转换格式等
ffprobe:用于分析多媒体文件信息的工具
ffplay:简单的媒体播放器
主要功能:
✅ 格式转换(转码)
✅ 视频裁剪、合并
✅ 调整分辨率、比特率
✅ 提取音频/视频
✅ 截图/生成缩略图
✅ 添加水印、字幕
✅ 流媒体处理
一、安装FFmpeg
Windows:
- 访问 https://ffmpeg.org/download.html
- 下载Windows版本,解压到指定目录
- 将bin目录添加到系统PATH环境变量
Linux (Ubuntu/Debian):
sudo apt update
sudo apt install ffmpeg
macOS:
brew install ffmpeg
二、项目配置
在application.properties或application.yml中配置FFmpeg路径:
# application.properties
ffmpeg.path=/usr/bin/ffmpeg # Linux/Mac
# 或
ffmpeg.path=C:\\ffmpeg\\bin\\ffmpeg.exe # Windows
三、依赖注入与异步处理
@Service
@Slf4j
public class VideoTranscodingService {@Value("${ffmpeg.path}")private String ffmpegPath; // 注入FFmpeg路径@Async // 异步执行,避免阻塞请求public void transcodeVideo(Long materialId, String inputPath) {// 转码逻辑}
}
核心转码方法
- ProcessBuilder
- Process
private void transcodeToResolution(String inputPath, String outputPath, String resolution) throws Exception {List<String> command = new ArrayList<>();command.add(ffmpegPath);command.add("-i");command.add(inputPath); // 输入文件command.add("-s");command.add(resolution); // 目标分辨率command.add("-c:v");command.add("libx264"); // 视频编码器command.add("-crf");command.add("23"); // 视频质量command.add("-c:a");command.add("aac"); // 音频编码器command.add("-b:a");command.add("128k"); // 音频比特率command.add(outputPath); // 输出文件ProcessBuilder builder = new ProcessBuilder(command);Process process = builder.start();int exitCode = process.waitFor(); // 等待转码完成if (exitCode != 0) {throw new RuntimeException("FFmpeg转码失败,退出码: " + exitCode);}
}
四、控制器中上传文件视频
@RestController
@RequestMapping("/api/video")
public class VideoController {@Autowiredprivate VideoTranscodingService transcodingService;@PostMapping("/upload")public ResponseEntity<String> uploadVideo(@RequestParam("file") MultipartFile file, @RequestParam Long materialId) {try {// 1. 保存上传的文件String uploadDir = "uploads/";String originalFilename = file.getOriginalFilename();String filePath = uploadDir + UUID.randomUUID() + "_" + originalFilename;File dest = new File(filePath);file.transferTo(dest);// 2. 异步启动转码transcodingService.transcodeVideo(materialId, filePath);return ResponseEntity.ok("视频上传成功,转码中...");} catch (Exception e) {return ResponseEntity.status(500).body("上传失败: " + e.getMessage());}}
}
@GetMapping("/status/{materialId}")
public ResponseEntity<Map<String, Object>> getTranscodingStatus(@PathVariable Long materialId) {CourseMaterial material = materialRepository.findById(materialId).orElse(null);if (material == null) {return ResponseEntity.notFound().build();}Map<String, Object> response = new HashMap<>();response.put("status", material.getTranscodingStatus());response.put("filePath", material.getFilePath());response.put("duration", material.getDuration());return ResponseEntity.ok(response);
}