目地:写一个java制作视频的网站。

大致流程:用户上传n张图片和背景音乐,网站返回一个视频。

大家都知道,java是通过命令行来调用ffmpeg的,java没有现成的比较好的封装ffmpeg的jar包。

所以,我们需要自己写调用ffmpeg的接口。

我采用的是spring boot架构。

首先配置pom文件

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--configuration-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!--jpa-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--start:视频获取某一帧的图片-->
        <!--<dependency>-->
            <!--<groupId>org.bytedeco</groupId>-->
            <!--<artifactId>javacv</artifactId>-->
            <!--<version>1.4.3</version>-->
        <!--</dependency>-->


        <!--<dependency>-->
            <!--<groupId>org.bytedeco.javacpp-presets</groupId>-->
            <!--<artifactId>ffmpeg-platform</artifactId>-->
            <!--<version>4.0.2-1.4.3</version>-->
        <!--</dependency>-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>
        <!--end:视频获取某一帧的图片-->

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!--导入jsp相关包-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <!-- jsp 依赖的jar包start-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>

	</dependencies>

注意:整个项目全部依赖此pom文件,后面不再更改。

我们还需要一个命令行处理器,创建一个CmdExecuter

package com.example.demo.util;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;
public class CmdExecuter {
    public static String exec(List<String> cmd) {
        String converted_time = null;
        Process proc =null;
        BufferedReader stdout = null;
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(cmd);
            builder.redirectErrorStream(true);
            proc = builder.start();
            stdout = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            String line;
            int lineNumber=1;
            List<String> returnStringList = new LinkedList<String>();
//            命令行信息
            for(String attribute : cmd) {
                System.out.println(attribute);
            }
//            命令行调试信息
            while ((line = stdout.readLine()) != null) {
                System.out.println("第"+lineNumber+"行:"+line);
                lineNumber=lineNumber+1;
                returnStringList.add(FFMPEG.dealString(line));
            }
            String info = "";
            for (int i = returnStringList.size() - 1; i >= 0; i--) {
                if (null != returnStringList.get(i) && returnStringList.get(i).startsWith("frame=")) {
                    info = returnStringList.get(i);
                    break;
                }
            }
            if (null != info) {
                converted_time = info.split("time=")[1].split("bitrate=")[0].trim();
            }
        } catch (IndexOutOfBoundsException ex) {
            converted_time = null;
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                proc.waitFor();
                stdout.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return converted_time;
    }


}

随后我们需要一个配置地址的类PropertyPlaceholder

package com.example.demo.entity;

import org.springframework.stereotype.Component;

/**
 * @program: javavc
 * @author: wlr
 * @create: 2019-07-26 10:20
 * @description:
 **/
@Component
//@ConfigurationProperties(prefix = "person")
public class PropertyPlaceholder {
//    根目录
    static String rootdir=System.getProperty("user.dir");
//    ffmpeg路径
    private String ffmpegpath="\\src\\main\\java\\com\\example\\demo\\tool\\bin\\ffmpeg.exe";
//    图片输入目录
    private String pictureInput="\\src\\main\\resources\\picture\\input";
//    图片输出目录
    private String pictureOut="\\src\\main\\resources\\picture\\out";
//    视频输入目录
    private String mp4Input="\\src\\main\\resources\\mp4\\input";
//    视频输出目录
    private String mp4Out="\\src\\main\\resources\\mp4\\out";

//    private String videoPath=mp4Input;
//    private String videoFramesPath=mp4Out;

    public static String getRootdir() {
        return rootdir;
    }

    public String getFfmpegpath() {
        return rootdir+ffmpegpath;
    }

    public void setFfmpegpath(String ffmpegpath) {
        this.ffmpegpath = ffmpegpath;
    }

    public String getPictureInput() {
        return rootdir+pictureInput;
    }

    public void setPictureInput(String pictureInput) {
        this.pictureInput = pictureInput;
    }

    public String getPictureOut() {
        return rootdir+pictureOut;
    }

    public void setPictureOut(String pictureOut) {
        this.pictureOut = pictureOut;
    }

    public String getMp4Input() {
        return rootdir+mp4Input;
    }

    public void setMp4Input(String mp4Input) {
        this.mp4Input = mp4Input;
    }

    public String getMp4Out() {
        return rootdir+mp4Out;
    }

    public void setMp4Out(String mp4Out) {
        this.mp4Out = mp4Out;
    }

//    public String getVideoPath() {
//        return rootdir+videoPath;
//    }
//
//    public void setVideoPath(String videoPath) {
//        this.videoPath = videoPath;
//    }
//
//    public String getVideoFramesPath() {
//        return rootdir+videoFramesPath;
//    }
//
//    public void setVideoFramesPath(String videoFramesPath) {
//        this.videoFramesPath = videoFramesPath;
//    }

    public String getPictureOut(String img_path) {
        return rootdir+pictureOut+"\\"+img_path;
    }
}

接下来我们需要在PropertyPlaceholder类指向ffmpeg的地址放放置ffmpeg的解压版

下载地址:http://www.ffmpeg.org/download.html

新建一个FFMPEG,通过命令行调用ffmpeg.exe。

package com.example.demo.util;


import com.example.demo.entity.PropertyPlaceholder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@Component
public class FFMPEG {
    @Autowired
    PropertyPlaceholder propertyPlaceholder;
    public static String dealString(String str) {
        Matcher m = java.util.regex.Pattern.compile("^frame=.*").matcher(str);
        String msg = "";
        while (m.find()) {
            msg = m.group();
        }
        return msg;
    }

    /**
     * 如果是数字就是成功的时间(秒数)
     *
     * @param str
     * @return
     */
    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(str);
        if (!isNum.matches()) {
            return false;
        }
        return true;
    }

    /**
     *   如果返回不是null的值就是成功(值为转换用时单位:秒)
     *
     * @param instr
     * @return
     */
    public static String returnSecond(String instr) {
        String returnValue = null;
        if (null != instr) {
            String[] a = instr.split("\\.");
            String[] b = a[0].split(":");
            // 返回秒
            int returnNumber = 0;
            if (null != instr && b[0].length() != 0) {
                returnNumber = Integer.valueOf(b[0]) * 60 * 60 + Integer.valueOf(b[1]) * 60 + Integer.valueOf(b[2]);
                returnValue = String.valueOf(returnNumber);
            } else {
                returnValue = null;
            }
        }
        return returnValue;
    }

    /**
     * 获取视频格式(转码前的格式和转码后的格式都可以调用)
     *
     * @param outputPath
     * @return
     */
    public static String returnVideoFormat(String outputPath) {
        return outputPath.substring(outputPath.lastIndexOf(".") + 1);
    }
    public String jpgTomp4(HashMap<String, String> dto){
        List<String> cmd = new ArrayList<String>();
        cmd.add(dto.get("ffmpeg_path"));
        cmd.add("-y");
        cmd.add("-i");
        cmd.add(dto.get("input_path"));
        if (null != dto.get("screen_size")) {
            cmd.add("-s");
            cmd.add(dto.get("screen_size"));
        }
        if (null != dto.get("logo")) {
            String logo = dto.get("logo");
            cmd.add("-vf");
            String xaxis = dto.get("xaxis");
            String yaxis = dto.get("yaxis");
            xaxis = xaxis != null && !xaxis.equals("") ? xaxis : "0";
            yaxis = yaxis != null && !yaxis.equals("") ? yaxis : "0";


            String logoString = "movie=" + logo + "[logo],[in][logo]overlay=x=" + xaxis + ":y=" + yaxis + "[out]";
            cmd.add(logoString);
        }
        cmd.add("-strict");
        cmd.add("-2");
        if (null != dto.get("vb") && !dto.get("vb").equals("")) {
            cmd.add("-vb");
            cmd.add(dto.get("vb") + "k");
        }
        if (null != dto.get("ab") && !dto.get("ab").equals("")) {
            cmd.add("-ab");
            cmd.add(dto.get("ab") + "k");
        }
        cmd.add("-q:v");
        cmd.add("4");
        cmd.add(dto.get("video_converted_path"));
        String converted_time = CmdExecuter.exec(cmd);
        return returnSecond(converted_time);//获取转换时间
    }

    /**
     * @ HashMap<String,String> dto 参数传递对象<br>
     * dto中包含的参数<br>
     * (必填)1.ffmpeg_path:ffmpeg执行文件地址,如 d:\\ffmpeg\\ffmpeg.exe Linux下直接调用ffmpeg命令(当然你事先已经有这个程序了)<br>
     * (必填)2.input_path:原视频路径<br>
     * (必填)3.video_converted_path:转换后视频输出路径<br>
     * (可选)4.screen_size:视频尺寸 长度乘宽度 乘号用英文小写"x"如 512x480<br>
     * (可选)5.logo:水印地址(其实在ffmpeg中有一个专门的watermark参数,logo跟它有何不同,我还没看,不过对我来说效果一样 貌似需要png图片才行)<br>
     * (可选,如果填写必须有logo才行,默认为0)6.xaxis:水印logo的横坐标(只有logo参数为一个正确路径才行) 比如0<br>
     * (可选,如果填写必须有logo才行,默认为0)6.yaxis:水印logo的纵坐标(只有logo参数为一个正确路径才行) 比如0<br>
     * (可选)vb:视频比特率,传入一个数值,单位在程序里面拼接了k
     * (可选)ab:音频比特率,传入一个数值,单位在程序里面拼接了k
     */
    public String videoTransfer(HashMap<String, String> dto) {
//  String ffmpeg_path,String input_path,String video_converted_path,String logo,String screen_size,String xaxis,String yaxis,String vb,String ab
        List<String> cmd = new ArrayList<String>();
        cmd.add(dto.get("ffmpeg_path"));
        cmd.add("-y");
        cmd.add("-i");
        cmd.add(dto.get("input_path"));
        if (null != dto.get("screen_size")) {
            cmd.add("-s");
            cmd.add(dto.get("screen_size"));
        }
        if (null != dto.get("logo")) {
            String logo = dto.get("logo");
            cmd.add("-vf");
            String xaxis = dto.get("xaxis");
            String yaxis = dto.get("yaxis");
            xaxis = xaxis != null && !xaxis.equals("") ? xaxis : "0";
            yaxis = yaxis != null && !yaxis.equals("") ? yaxis : "0";


            String logoString = "movie=" + logo + "[logo],[in][logo]overlay=x=" + xaxis + ":y=" + yaxis + "[out]";
            cmd.add(logoString);
        }
        cmd.add("-strict");
        cmd.add("-2");
        if (null != dto.get("vb") && !dto.get("vb").equals("")) {
            cmd.add("-vb");
            cmd.add(dto.get("vb") + "k");
        }
        if (null != dto.get("ab") && !dto.get("ab").equals("")) {
            cmd.add("-ab");
            cmd.add(dto.get("ab") + "k");
        }
        cmd.add("-q:v");
        cmd.add("4");
        cmd.add(dto.get("video_converted_path"));
        String converted_time = CmdExecuter.exec(cmd);
        return returnSecond(converted_time);//获取转换时间
    }
    /*
    * 给一张图片添加图片水印
    * */
    public void makeWatermask(String inputImage0,String inputImage1,int width,int height,int x,int y,String outputPath){
        List<String> cmd = new ArrayList<String>();
        cmd.add(propertyPlaceholder.getFfmpegpath());
        cmd.add("-i");
        cmd.add(inputImage0);
        cmd.add("-vf");
        cmd.add("movie='"+inputImage1.replace("\\","\\\\").replace(":","\\:")+"',"+"scale="+width+":"+height+"[watermask];[in][watermask]"+"overlay="+x+":"+y+"[out]");
        cmd.add("-y");
        cmd.add(outputPath);
        CmdExecuter.exec(cmd);
    }
    /*
    * 给1张图片添加2张图片水印
    * */
    public void makeWatermask(String inputImage0,String inputImage1,int width,int height,int x,int y,String inputImage2,int width1,int height1,int x1,int y1,String outputPath){
        List<String> cmd = new ArrayList<String>();
        cmd.add(propertyPlaceholder.getFfmpegpath());
        cmd.add("-i");
        cmd.add(inputImage0);
        cmd.add("-vf");
        cmd.add("movie='"+inputImage1.replace("\\","\\\\").replace(":","\\:")+"',"+"scale="+width+":"+height+"[1watermask];movie='"+inputImage2.replace("\\","\\\\").replace(":","\\:")+"',"+"scale="+width+":"+height+"[2watermask];[in][1watermask]"+"overlay="+x+":"+y+"[int];[int][2watermask]overlay="+x1+":"+y1+"[out]");
        cmd.add("-y");
        cmd.add(outputPath);
        CmdExecuter.exec(cmd);
    }

    /**
     * @param input_path 图片输入正则路径
     * @param video_converted_path 视频输出路径
     */
    /*
    在input_path文件夹下,将1.png~n.png的图片转化为视频,注意,这里的图片必须是1.png起始
    * */
    public void imageToMp4(String input_path,String video_converted_path){
        List<String> cmd = new ArrayList<String>();
        cmd.add(propertyPlaceholder.getFfmpegpath());
        cmd.add("-f");
        cmd.add("image2");
        cmd.add("-i");
        cmd.add(input_path);
        cmd.add("-vcodec");
        cmd.add("libx264");
        cmd.add("-r");
        cmd.add("25");
        cmd.add("-b");
        cmd.add("382k");
        cmd.add("-y");
        cmd.add(video_converted_path);
        CmdExecuter.exec(cmd);
    }
    /*
    给视频添加水印
    input_path:视频输入路径
    video_converted_path:视频转换路径
    logo:logo图片转换路径
    xaxis:logo图片相对于视频的横坐标
    yaxis:logo图片相对于视频的纵坐标
    * */
    public void addWatermark(String input_path,String video_converted_path,String logo,String xaxis,String yaxis){
        HashMap<String, String> dto = new HashMap<String, String>();
        dto.put("ffmpeg_path", propertyPlaceholder.getFfmpegpath());//必填
        dto.put("input_path", input_path);//必填
        dto.put("video_converted_path", video_converted_path);//必填
        dto.put("logo", "C\\\\:/Users/WLR/Desktop/project/video_demo/src/oyuemeili.png");//可选(注意windows下面的logo地址前面要写4个反斜杠,如果用浏览器里面调用servlet并传参只用两个,如 d:\\:/ffmpeg/input/logo.png)
        dto.put("xaxis",xaxis);
        dto.put("yaxis",yaxis);
        String secondsString = new FFMPEG().videoTransfer(dto);
        System.out.println("转换共用:" + secondsString + "秒");
    }

    /**
     * 将.MP4转化为.ts
     * @param mp4Address    mp4地址
     * @param tsAddress     ts地址
     */
    public void mp4ToTs(String mp4Address, String tsAddress) {
        List<String> cmd = new ArrayList<String>();
        cmd.add(propertyPlaceholder.getFfmpegpath());
        cmd.add("-i");
        cmd.add(mp4Address);
        cmd.add("-vcodec");
        cmd.add("copy");
        cmd.add("-vbsf");
        cmd.add("h264_mp4toannexb ");
        cmd.add("-y");
        cmd.add(tsAddress);
        CmdExecuter.exec(cmd);
    }

    /**
     * 将多个.ts转换为.mp4
     * @param tsName
     * @param outTsAddress
     */
    public void addTsToMP4(ArrayList<String> tsName, String outTsAddress) {
        List<String> cmd = new ArrayList<String>();
        cmd.add(propertyPlaceholder.getFfmpegpath());
        cmd.add("-i");
        int size=tsName.size();
        String concat="\"concat:";
        for (int i=0;i<size-1;i++){
            concat=concat+(String) tsName.remove(0)+"|";
        }
        concat=concat+(String) tsName.remove(0)+"\"";
        cmd.add(concat);
        cmd.add("-c");
        cmd.add("copy");
        cmd.add("-bsf:a");
        cmd.add("aac_adtstoasc");
        cmd.add("-movflags");
        cmd.add("faststart");
        cmd.add("-y");
        cmd.add(outTsAddress);
        cmd.add("-y");
        CmdExecuter.exec(cmd);
    }

    /**
     * 给MP4视频添加背景音乐
     * @param mp3Address
     * @param mp4Address
     */
    public void addMp3(String mp3Address,String mp4Address,String output){
        List<String> cmd=new ArrayList<>();
        cmd.add(propertyPlaceholder.getFfmpegpath());
        cmd.add("-i");
        cmd.add(mp3Address);
        cmd.add("-i");
        cmd.add(mp4Address);
        cmd.add("-y");
        cmd.add(output);
        CmdExecuter.exec(cmd);
    }
//    public static void main(String[] arg) {
//        HashMap<String, String> dto = new HashMap<String, String>();
//        dto.put("ffmpeg_path", "C:\\Users\\WLR\\Desktop\\project\\ffmpeg-20190628-098ab93-win64-static\\ffmpeg-20190628-098ab93-win64-static\\bin\\ffmpeg.exe");//必填
//        dto.put("input_path", "C:\\Users\\WLR\\Desktop\\project\\video_demo\\src\\瓯越文化.mp4");//必填
//        dto.put("video_converted_path", "C:\\Users\\WLR\\Desktop\\project\\video_demo\\src\\aaa.mp4");//必填
//        dto.put("logo", "C\\\\:/Users/WLR/Desktop/project/video_demo/src/oyuemeili.png");//可选(注意windows下面的logo地址前面要写4个反斜杠,如果用浏览器里面调用servlet并传参只用两个,如 d:\\:/ffmpeg/input/logo.png)
//        dto.put("xaxis","100");
//        dto.put("yaxis","100");
//        String secondsString = new FFMPEG().videoTransfer(dto);
//        System.out.println("转换共用:" + secondsString + "秒");
//    }


}

 创建一个 接口MarkText,用来调用FFMPEG工具

package com.example.demo.service;

import java.awt.*;
import java.util.ArrayList;
import java.util.Queue;


public interface MarkText {
    //加文字水印
    void mark(String imgPath, String outImgPath, String text, Font font, Color color, int x, int y);
    //加图片水印
    void mark(String inputImg, String markImg, String outputImg, int width, int height, int x, int y);
    //通过gotravale视频模版在文件夹下生成视频帧的图片
    void makePictureInDirByGotravale(String username, ArrayList filename);
    //制作视频
    void makeMp4(String imgDir,String mp3);
}

用类MarkText4J实现 接口MarkText

package com.example.demo.service.impl;

import com.example.demo.entity.PropertyPlaceholder;
import com.example.demo.service.MarkText;
import com.example.demo.util.FFMPEG;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

/**
 * @program: demo
 * @author: wlr
 * @create: 2019-06-29 21:28
 * @description:水印图片的四种方式
 **/
@Service
public class MarkText4J implements MarkText {
    @Autowired
    PropertyPlaceholder propertyPlaceholder;
    @Autowired
    FFMPEG ffmpeg;
    //静态资源文件本地路径(非target)
    String localAddress=propertyPlaceholder.getRootdir()+"\\src\\main\\resources";
    // 加文字水印
    public void mark(BufferedImage bufImg, Image img, String text, Font font, Color color, int x, int y) {
        Graphics2D g = bufImg.createGraphics();
        g.drawImage(img, 0, 0, bufImg.getWidth(), bufImg.getHeight(), null);
        g.setColor(color);
        g.setFont(font);
        g.drawString(text, x, y);
        g.dispose();
    }
    // 加图片水印
    public void mark(BufferedImage bufImg, Image img, Image markImg, int width, int height, int x, int y) {
        Graphics2D g = bufImg.createGraphics();
        g.drawImage(img, 0, 0, bufImg.getWidth(), bufImg.getHeight(), null);
        g.drawImage(markImg, x, y, width, height, null);
        g.dispose();
    }

    /**
     * 给图片增加文字水印
     *
     * @param imgPath
     *            -要添加水印的图片路径
     * @param outImgPath
     *            -输出路径
     * @param text-文字
     * @param font
     *            -字体
     * @param color
     *            -颜色
     * @param x
     *            -文字位于当前图片的横坐标
     * @param y
     *            -文字位于当前图片的竖坐标
     */
    public void mark(String imgPath, String outImgPath, String text, Font font, Color color, int x, int y) {
        try {
            // 读取原图片信息
            File imgFile = null;
            Image img = null;
            if (imgPath != null) {
                imgFile = new File(imgPath);
            }
            if (imgFile != null && imgFile.exists() && imgFile.isFile() && imgFile.canRead()) {
                img = ImageIO.read(imgFile);
            }
            int imgWidth = img.getWidth(null);
            int imgHeight = img.getHeight(null);
            // 加水印
            BufferedImage bufImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
            mark(bufImg, img, text, font, color, x, y);
            // 输出图片
            FileOutputStream outImgStream = new FileOutputStream(outImgPath);
            ImageIO.write(bufImg, "jpg", outImgStream);
            outImgStream.flush();
            outImgStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 给图片增加图片水印
     *
     * @param inputImg
     *            -源图片,要添加水印的图片
     * @param markImg
     *            - 水印图片
     * @param outputImg
     *            -输出图片(可以是源图片)
     * @param width
     *            - 水印图片宽度
     * @param height
     *            -水印图片高度
     * @param x
     *            -图片相对于原图片横坐标
     * @param y
     *            -图片相对于原图片纵坐标
     */
    public void mark(String inputImg, String markImg, String outputImg, int width, int height, int x, int y) {
        // 读取原图片信息
        File inputImgFile = null;
        File markImgFile = null;
        Image img = null;
        Image mark = null;
        try {
            if (inputImg != null && markImg != null) {
                inputImgFile = new File(inputImg);
                markImgFile = new File(markImg);
            }
            if (inputImgFile != null && inputImgFile.exists() && inputImgFile.isFile() && inputImgFile.canRead()) {

                img = ImageIO.read(inputImgFile);

            }
            if (markImgFile != null && markImgFile.exists() && markImgFile.isFile() && markImgFile.canRead()) {

                mark = ImageIO.read(markImgFile);

            }
            int imgWidth = img.getWidth(null);
            int imgHeight = img.getHeight(null);
            BufferedImage bufImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
            mark(bufImg, img, mark, width, height, x, y);
            FileOutputStream outImgStream = new FileOutputStream(outputImg);
            ImageIO.write(bufImg, "png", outImgStream);
            outImgStream.flush();
            outImgStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void  makeVideoframe1Aboutgotravale(String username,String inputImg,String sep,int pictureNum){
        ffmpeg.makeWatermask(localAddress+"\\moban\\gotravale\\blank.png",localAddress+"\\user\\"+username+"\\picture\\input\\"+inputImg,725,418,221,76,localAddress+"\\user\\"+username+"\\picture\\out\\"+inputImg);
//        mark( localAddress+"\\moban\\gotravale\\blank.png",localAddress+"\\user\\"+username+"\\picture\\input\\"+inputImg, localAddress+"\\user\\"+username+"\\picture\\out\\"+inputImg, 725, 418, 221, 76);
        makeVideoframeAboutgotravale(username,inputImg,sep,pictureNum);
    }
    public void  makeVideoframe2Aboutgotravale(String username,String inputImg,String inputImg1,String sep,int pictureNum){
        ffmpeg.makeWatermask(localAddress+"\\moban\\gotravale\\blank.png",localAddress+"\\user\\"+username+"\\picture\\input\\"+inputImg,411,399,58,38,localAddress+"\\user\\"+username+"\\picture\\input\\"+inputImg1,343,301,499,165,localAddress+"\\user\\"+username+"\\picture\\out\\"+inputImg);
//        mark( localAddress+"\\moban\\gotravale\\blank.png",localAddress+"\\user\\"+username+"\\picture\\input\\"+inputImg, localAddress+"\\user\\"+username+"\\picture\\out\\"+inputImg, 411, 399, 58, 38);
//        mark( localAddress+"\\user\\"+username+"\\picture\\out\\"+inputImg,localAddress+"\\user\\"+username+"\\picture\\input\\"+inputImg1, localAddress+"\\user\\"+username+"\\picture\\out\\"+inputImg, 343, 301, 499, 165);
        makeVideoframeAboutgotravale(username,inputImg,sep,pictureNum);
    }
    public void makeVideoframeAboutgotravale(String username,String inputImg,String sep,int pictureNum){
        for (int num=1;num<=pictureNum;){
            ffmpeg.makeWatermask(localAddress+"\\user\\"+username+"\\picture\\out\\"+inputImg,localAddress+"\\moban\\gotravale\\"+sep+"\\"+num+".png",998,560,0,0,localAddress+"\\user\\"+username+"\\picture\\"+sep+"\\"+num+".jpg");
//            mark(localAddress+"\\user\\"+username+"\\picture\\out\\"+inputImg, localAddress+"\\moban\\gotravale\\"+sep+"\\"+num+".png", localAddress+"\\user\\"+username+"\\picture\\"+sep+"\\"+num+".png", 998, 560, 0, 0);
            num++;
        }
    }
    public void  makeVideoframe3Aboutgotravale(String username,String inputImg,String sep,int pictureNum){
        mark( localAddress+"\\moban\\gotravale\\blank.png",localAddress+"\\user\\"+username+"\\picture\\input\\"+inputImg, localAddress+"\\user\\"+username+"\\picture\\out\\"+inputImg, 754, 442, 214, 53);
        makeVideoframeAboutgotravale(username,inputImg,sep,pictureNum);
    }
    /**
     * @param username 用户名

     * @param filename   输入图片名的集合
     */
    //給特定一个文件夹下的图片添加水印
    @Override
    public void makePictureInDirByGotravale(String username, ArrayList filename) {
        /*
        * 生成sep1文件夹关键帧
        * */
        //将用户上传的图片水印到空白图片上
        String inputImg=(String) filename.remove(0);
        makeVideoframe1Aboutgotravale(username,inputImg,"sep1",85);
        /*
         * 生成sep2文件夹关键帧
         * */
        inputImg=(String) filename.remove(0);
        String inputImg1=(String) filename.remove(0);
        makeVideoframe2Aboutgotravale(username,inputImg,inputImg1,"sep2",166);
        /*
         * 生成sep3文件夹关键帧
         * */
        inputImg=(String) filename.remove(0);
        makeVideoframe3Aboutgotravale(username,inputImg,"sep3",82);
        /*
        将图片生成对应的MP4文件
        * */
        ffmpeg.imageToMp4(localAddress+"\\user\\"+username+"\\picture\\sep1\\%d.jpg",localAddress+"\\user\\"+username+"\\mp4\\input\\sep1.mp4");
        ffmpeg.imageToMp4(localAddress+"\\user\\"+username+"\\picture\\sep2\\%d.jpg",localAddress+"\\user\\"+username+"\\mp4\\input\\sep2.mp4");
        ffmpeg.imageToMp4(localAddress+"\\user\\"+username+"\\picture\\sep3\\%d.jpg",localAddress+"\\user\\"+username+"\\mp4\\input\\sep3.mp4");
        /*
        * 将mp4转化为ts文件
        * */
        ffmpeg.mp4ToTs(localAddress+"\\user\\"+username+"\\mp4\\input\\sep1.mp4",localAddress+"\\user\\"+username+"\\ts\\sep1.ts");
        ffmpeg.mp4ToTs(localAddress+"\\user\\"+username+"\\mp4\\input\\sep2.mp4",localAddress+"\\user\\"+username+"\\ts\\sep2.ts");
        ffmpeg.mp4ToTs(localAddress+"\\user\\"+username+"\\mp4\\input\\sep3.mp4",localAddress+"\\user\\"+username+"\\ts\\sep3.ts");
        /*
        * 将ts文件黏连在一起,转化为mp4
        * */
        ArrayList<String> tsName = new ArrayList<>();
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\start.ts");
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\start_font.ts");
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\start_end.ts");
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\sep1start.ts");
        tsName.add(localAddress+"\\user\\"+username+"\\ts\\sep1.ts");
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\sep1end.ts");
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\sep2start.ts");
        tsName.add(localAddress+"\\user\\"+username+"\\ts\\sep2.ts");
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\sep2end.ts");
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\sep3start.ts");
        tsName.add(localAddress+"\\user\\"+username+"\\ts\\sep3.ts");
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\sep3end.ts");
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\end.ts");
        tsName.add(localAddress+"\\moban\\gotravale\\ts\\end_font.ts");
        ffmpeg.addTsToMP4(tsName,localAddress+"\\user\\"+username+"\\mp4\\input\\input.mp4");
        /*
        * 给MP4视频添加背景音乐
        * */
        ffmpeg.addMp3(localAddress+"\\user\\"+username+"\\mp3\\"+filename.remove(0),localAddress+"\\user\\"+username+"\\mp4\\input\\input.mp4",localAddress+"\\user\\"+username+"\\mp4\\out\\output.mp4");
    }

    @Override
    public void makeMp4(String imgDir, String mp3) {
//        ffmpeg.imageToMp4(imgDir,);
    }
}

最后,通过VideoController调用MarkText4J

package com.example.demo.controller;


import com.example.demo.service.impl.MarkText4J;
//import com.example.demo.util.VideoProcessing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Queue;

@RestController
public class VideoController {

    @Autowired
    MarkText4J mark;
//    将视频用图片的形式保存
//    @RequestMapping(value = "/grabberVideoFrame")
//    public void grabberVideoFrame(String videoFileName){
//        videoProcessing.grabberVideoFramer(videoFileName);
//    }
//      将图片生成视频
    @RequestMapping(value = "/makeVideo")
    public void makeVideo(){
        ArrayList<String> filename = new ArrayList<>();
        filename.add("1.png");
        filename.add("2.png");
        filename.add("3.png");
        filename.add("4.png");
        filename.add("culture.mp3");
        mark.makePictureInDirByGotravale("www",filename);
    }
}

以下是我的文件放置方式,方便大家实现demo 

java 视频 处理 java制作视频_java


application.properties

#环境为springboot2.0.6
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
spring.servlet.multipart.resolve-lazily=false
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
spring.jpa.open-in-view=false

application.yml

spring:
  #配置属性文件
  profiles:
    active: dev
#  配置默认访问路径
  resources:
    static-locations: classpath:/
  #配置MySQL
  datasource:
    #添加驱动器名称
    driver-class-name: com.mysql.cj.jdbc.Driver
    #添加数据库连接的url
    url: jdbc:mysql://127.0.0.1:3306/pyt?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    #配置数据库用户名
    username: 
    #配置数据库密码
    password: 
  #配置jpa
  jpa:
    hibernate:
      #ddl-auto属性:
      #   create 启动时删数据库中的表,然后创建,退出时不删除数据表
      #   create-drop 启动时删数据库中的表,然后创建,退出时删除数据表 如果表不存在报错
      #   update 如果启动时表格式不一致则更新表,原有数据保留
      #   validate 项目启动表结构进行校验 如果不一致则报错
      ddl-auto: update
    #改属性值若为true,操作数据库时,可打印SQL语句到控制台
    show-sql: true

由于图片,视频等资源过大,我将其上传到我的博客中大家可以自行下载。目前在intel i7 7700的cpu下需要40秒左右生成一个4.76MB的视频,项目算法正在优化中。

在下一篇文章中,将会阐述视频制作逻辑