提取视频中的图片

本文档介绍如何使用 JAVE 库内置的 FFmpeg 功能来从视频中提取图片,并通过控制器将其整合到 Web 应用中。

使用 FFmpeg 命令提取图片

获取 FFmpeg 命令路径

JAVE 库内置了一个 DefaultFFMPEGLocator 类,该类在实例化时会将 FFmpeg 可执行文件复制到系统的临时文件夹中。通过该类可以获取 FFmpeg 可执行文件的路径,从而在程序中调用 FFmpeg。

import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;

public class FfmpegPathExample {

  public static void main(String[] args) {
    // 创建一个locator对象
    DefaultFFMPEGLocator locator = new DefaultFFMPEGLocator();
    // 获取路径
    String executablePath = locator.getExecutablePath();
    // 示例输出路径:C:\Users\Administrator\AppData\Local\Temp\jave\ffmpeg-amd64-3.5.0.exe
    System.out.println(executablePath);
  }
}

调用 FFmpeg 命令提取图片

使用 FFmpeg 命令可以从视频中提取帧并保存为图片。以下代码展示了如何使用 FFmpeg 从视频中每秒提取一帧,并将这些帧保存到指定的输出目录中。

import java.io.File;
import java.io.IOException;

import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;

public class FrameExtractor {

  public static void main(String[] args) {
    File source = new File("input.mp4");

    File outputDir = new File("output/");
    outputDir.mkdirs(); // 创建输出目录

    // 创建一个locator对象
    DefaultFFMPEGLocator locator = new DefaultFFMPEGLocator();

    try {
      // 构建 FFmpeg 命令
      String[] command = {
          locator.getExecutablePath(),
          "-i", source.getAbsolutePath(),  // 输入视频文件
          "-vf", "fps=1",  // 每秒提取一帧
          outputDir.getAbsolutePath() + "/frame%d.jpg"  // 输出文件路径
      };

      // 执行 FFmpeg 命令
      ProcessBuilder processBuilder = new ProcessBuilder(command);
      Process process = processBuilder.start();
      process.waitFor(); // 等待命令执行完成

      System.out.println("Frames extracted successfully.");
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("finish");
  }
}

添加图片水印

在提取图片的同时,你可以通过使用 FFmpeg 的 drawtext 过滤器在图片上添加水印。以下代码展示了如何将帧编号作为水印添加到图片上。

import java.io.File;
import java.io.IOException;

import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;

public class FrameExtractorWithWatermark {

  public static void main(String[] args) {
    String filePath = "video.mp4";
    File source = new File(filePath);

    File outputDir = new File("output/");
    outputDir.mkdirs(); // 创建输出目录

    // 创建一个locator对象
    DefaultFFMPEGLocator locator = new DefaultFFMPEGLocator();

    try {
      // 构建 FFmpeg 命令,添加水印
      String[] command = {
          locator.getExecutablePath(),
          "-i", source.getAbsolutePath(),  // 输入视频文件
          "-vf", "fps=1,drawtext=text='%{n}':x=10:y=H-th-10:fontsize=50:fontcolor=red",  // 添加帧号水印
          outputDir.getAbsolutePath() + "/frame%d.jpg"  // 输出文件路径
      };

      // 执行 FFmpeg 命令
      ProcessBuilder processBuilder = new ProcessBuilder(command);
      Process process = processBuilder.start();
      process.waitFor(); // 等待命令执行完成

      System.out.println("Frames extracted successfully with watermark.");
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("finish");
  }
}
  1. FFmpeg drawtext 过滤器:
  • drawtext=text='%{n}': 在图片上添加文本水印,%{n} 表示当前帧的编号。

  • x=10:y=H-th-10: 指定文本在图像上的位置,x=10 和 y=H-th-10 分别表示文本的左上角坐标。

  • fontsize=50: 指定字体大小为 50。

  • fontcolor=red: 指定字体颜色为白色。

    2.ProcessBuilder:

  • 使用 ProcessBuilder 来执行 FFmpeg 命令。

    3.输出路径:

  • 图片文件将保存在 output 目录下,文件名格式为 frame%d.jpg,其中 %d 表示帧号。

整合到 Controller 中

接下来,我们将上述功能整合到 Web 应用中,使用户可以通过上传视频文件来提取图片。

配置文件

首先,我们需要设置静态文件的访问路径。将静态文件的根目录设置为 pages 目录:

server.resources.static-locations = pages

可以通过以下代码获取页面的根路径:

String pageRoot = TioBootServer.me().getHttpConfig().getPageRoot();

编写 Controller

编写一个控制器来处理用户上传的文件,并调用 VideoService 来提取视频中的帧,最后返回生成的图片文件路径。

public HttpResponse frames(UploadFile file) {
  HttpResponse response = TioRequestContext.getResponse();
  String pageRoot = TioBootServer.me().getHttpConfig().getPageRoot();

  if (file == null) {
    response.setStatus(400);
    response.setJson(RespVo.fail("upload file is empty"));
    return response;
  }

  String inputFolderName = "input";
  String outputFolderName = pageRoot + File.separator + "output";

  byte[] fileData = file.getData();
  File inputFolder = new File(inputFolderName);
  if (!inputFolder.exists()) {
    inputFolder.mkdirs(); // 创建输入目录
  }
  File outputFolder = new File(outputFolderName);
  if (!outputFolder.exists()) {
    outputFolder.mkdirs(); // 创建输出目录
  }

  File source = new File(inputFolderName + "/" + file.getName());
  FileUtil.writeBytes(fileData, source);

  VideoService videoService = Aop.get(VideoService.class);
  videoService.frames(outputFolder, source);

  String[] filenames = outputFolder.list();
  List<String> filePaths = new ArrayList<>(filenames.length);
  for (String string : filenames) {
    filePaths.add("/output/" + string);
  }

  return response.setJson(RespVo.ok(filePaths));
}

编写 Service

VideoService 类用于封装帧提取的逻辑:

package com.litongjava.jave.server.service;

import java.io.File;
import java.io.IOException;

import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;

public class VideoService {

  public void frames(File outputFolder, File source) {
    // 创建一个locator对象
    DefaultFFMPEGLocator locator = new DefaultFFMPEGLocator();

    try {
      // 构建 FFmpeg 命令,提取帧并添加水印
      String[] command = {
          locator.getExecutablePath(),
          "-i", source.getAbsolutePath(),  // 输入视频文件
          "-vf", "fps=1,drawtext=text='%{n}':x=10:y=H-th-10:fontsize=50:fontcolor=red",  // 添加帧号水印
          outputFolder.getAbsolutePath() + "/frame_%d.jpg"  // 输出文件路径
      };

      // 执行 FFmpeg 命令
      ProcessBuilder processBuilder = new ProcessBuilder(command);
      Process process = processBuilder.start();
      process.waitFor(); // 等待命令执行完成
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
  }

}

调用 Controller

调用上面定义的控制器后,将返回提取的图片路径,格式如下:

{
  "data": ["/output/frame1.jpg", "/output/frame10.jpg", "/output/frame11.jpg"],
  "ok": true,
  "code": 1,
  "msg": null
}

访问生成的图片

通过 Tio-boot 的静态资源处理器,用户可以访问生成的图片。访问地址格式如下:

http://localhost/output/frame1.jpg

总结

本文档详细介绍了如何使用 JAVE 内置的 FFmpeg 提取视频中的帧,并添加水印。我们还展示了如何将这一功能集成到 Web 应用中,通过控制器和服务类实现视频帧的提取和图片文件的访问。希望通过此文档,您可以更好地理解和实现类似的功能。