1、FFmpeg是什么

FFmpeg(https://www.ffmpeg.org)是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。它用来干吗呢?视频采集、视频格式转化、视频截图、视频添加水印、视频切片(m3u8、ts)、视频录制、视频推流、更改音视频参数(编码方式、分辨率、码率、比特率等)功能,等等...

下载下来解压完了呢是这个样子:

bin中文件夹有个 ffmpeg.exe,点开,是的,一闪而逝并没有什么用,因为ffmpeg靠命令行来调用:

如上图命令读取 《小星星》.mp4 文件的文件信息,下面的一大片输出就是文件的信息输出了。

2、Java调用FFmpeg

现在我们需要把目标视频在第10秒处截图,怎么做呢,先看看用命令行是这样:

C:\workspace\project\greejoy\picManager\web\tools\ffmpeg\bin\ffmpeg.exe -i C:\Users\Dulk\Desktop\ukulele\01\《小星星》.mp4 -f image2 -ss 10 -t 0.001 -s 320*240 C:\Users\Dulk\Desktop\ukulele\01\littleStar.jpg
1
 
1
C:\workspace\project\greejoy\picManager\web\tools\ffmpeg\bin\ffmpeg.exe -i C:\Users\Dulk\Desktop\ukulele\01\《小星星》.mp4 -f image2 -ss 10 -t 0.001 -s 320*240 C:\Users\Dulk\Desktop\ukulele\01\littleStar.jpg
其中:
  • C:\workspace\project\greejoy\picManager\web\tools\ffmpeg\bin\ffmpeg.exe 指 ffmpeg.exe 的路径
  • C:\Users\Dulk\Desktop\ukulele\01\《小星星》.mp4 指源视频路径
  • C:\Users\Dulk\Desktop\ukulele\01\littleStar.jpg 指截图的输出路径
  • -i 表示输入文件的命令
  • -f 表示输出格式,image2表示输出为图片
  • -ss 表示指定的起始位置,这里设置为10,即10s处
  • -t 表示设置录制/转码时长,既然截图就0.001就足够了
  • -s 表示size,设置帧大小

现在我们看看用Java调用FFmpeg来实现相同的目的,要用到 ProcessBuilder 这个类,该类用于创建操作系统进程,执行本地命令或脚本等工作,其属性command是一个字符串列表(用以输入命令),然后通过start()方法启动执行,但需要注意的是,该方法调用会启动新的进程执行操作,所以并不是和原Java代码同步执行的。

public class FFmpegTest {

    public static void main(String[] args) {

        String ffmpegExePath = "C:\\workspace\\project\\greejoy\\picManager\\web\\tools\\ffmpeg\\bin\\ffmpeg.exe";
String inputFilePath = "C:\\Users\\Dulk\\Desktop\\ukulele\\01\\《小星星》.mp4";
String outputFilePath = "C:\\Users\\Dulk\\Desktop\\ukulele\\01\\littleStarJava.jpg"; List<String> command = new ArrayList<String>();
command.add(ffmpegExePath);
command.add("-i");
command.add(inputFilePath);
command.add("-f");
command.add("image2");
command.add("-ss");
command.add("10");
command.add("-t");
command.add("0.001");
command.add("-s");
command.add("320*240");
command.add(outputFilePath); ProcessBuilder builder = new ProcessBuilder();
builder.command(command);
//正常信息和错误信息合并输出
builder.redirectErrorStream(true);
try {
//开始执行命令
Process process = builder.start(); //如果你想获取到执行完后的信息,那么下面的代码也是需要的
StringBuffer sbf = new StringBuffer();
String line = null;
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((line = br.readLine()) != null) {
sbf.append(line);
sbf.append(" ");
}
String resultInfo = sbf.toString();
System.out.println(resultInfo); } catch (IOException e) {
e.printStackTrace();
}
} }
47
 
1
public class FFmpegTest {
2

3
    public static void main(String[] args) {
4

5
        String ffmpegExePath = "C:\\workspace\\project\\greejoy\\picManager\\web\\tools\\ffmpeg\\bin\\ffmpeg.exe";
6
        String inputFilePath = "C:\\Users\\Dulk\\Desktop\\ukulele\\01\\《小星星》.mp4";
7
        String outputFilePath = "C:\\Users\\Dulk\\Desktop\\ukulele\\01\\littleStarJava.jpg";
8

9
        List<String> command = new ArrayList<String>();
10
        command.add(ffmpegExePath);
11
        command.add("-i");
12
        command.add(inputFilePath);
13
        command.add("-f");
14
        command.add("image2");
15
        command.add("-ss");
16
        command.add("10");
17
        command.add("-t");
18
        command.add("0.001");
19
        command.add("-s");
20
        command.add("320*240");
21
        command.add(outputFilePath);
22

23
        ProcessBuilder builder = new ProcessBuilder();
24
        builder.command(command);
25
        //正常信息和错误信息合并输出
26
        builder.redirectErrorStream(true);
27
        try {
28
            //开始执行命令
29
            Process process = builder.start();
30

31
            //如果你想获取到执行完后的信息,那么下面的代码也是需要的
32
            StringBuffer sbf = new StringBuffer();
33
            String line = null;
34
            BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
35
            while ((line = br.readLine()) != null) {
36
                sbf.append(line);
37
                sbf.append(" ");
38
            }
39
            String resultInfo = sbf.toString();
40
            System.out.println(resultInfo);
41

42
        } catch (IOException e) {
43
            e.printStackTrace();
44
        }
45
    }
46

47
}

可以看到,拼接命令实际上就是把参数和值按序传入一个List,然后将该List传给 ProcessBuilder 类,然后执行 start() 方法即可。可以看到控制台输出的信息如下:

需要注意的是:
  • 操作执行信息的读取是分为正常信息和错误信息,分别需要使用 getInputStream() 和 getErrorStream() 读取,上例使用了 builder.redirectErrorStream(true); 将两者合并输出
  • 如果要使用 Process.waitFor() 则需要小心阻塞问题,此处不展开(FFmpeg在JAVA中的使用以及Process.waitFor()引发的阻塞问题

3、Builder设计模式的应用

如果你每次使用都要如上例中add()一大堆参数,我估计也是头疼:
List<String> command = new ArrayList<String>();
command.add(ffmpegExePath);
command.add("-i");
command.add(inputFilePath);
command.add("-f");
command.add("image2");
command.add("-ss");
command.add("10");
command.add("-t");
command.add("0.001");
command.add("-s");
command.add("320*240");
command.add(outputFilePath);
13
 
1
List<String> command = new ArrayList<String>();
2
command.add(ffmpegExePath);
3
command.add("-i");
4
command.add(inputFilePath);
5
command.add("-f");
6
command.add("image2");
7
command.add("-ss");
8
command.add("10");
9
command.add("-t");
10
command.add("0.001");
11
command.add("-s");
12
command.add("320*240");
13
command.add(outputFilePath);

后来一想,这简直就是绝佳的使用builder模式的示例,先把命令封装成一个类:
/**
* FFmpeg命令的封装类
*/
public class FFmpegCommand { private List<String> command; public FFmpegCommand(List<String> command) {
this.command = command == null ? new ArrayList<String>() : command;
} public List<String> getCommand() {
return command;
} public void setCommand(List<String> command) {
this.command = command;
} /**
* 开始执行命令
*
* @param callback 回调
* @return 命令的信息输出
* @throws FFmpegCommandException
*/
public String start(FFmpegCallback callback) throws FFmpegCommandException {
BufferedReader br = null;
StringBuffer sbf = new StringBuffer();
String resultInfo = null;
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(command);
//正常信息和错误信息合并输出
builder.redirectErrorStream(true);
//开启执行子线程
Process process = builder.start(); String line = null;
br = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((line = br.readLine()) != null) {
sbf.append(line);
sbf.append(" ");
}
resultInfo = sbf.toString(); //等待命令子线程执行完成
int exitValue = process.waitFor();
//完成后执行回调
if (exitValue == 0 && callback != null) {
callback.complete(resultInfo);
}
//销毁子线程
process.destroy(); } catch (IOException e) {
e.printStackTrace();
throw new FFmpegCommandException(e.getMessage());
} catch (InterruptedException e) {
e.printStackTrace();
throw new FFmpegCommandException("线程阻塞异常:" + e.getMessage());
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} return resultInfo;
} }
75
 
1
/**
2
 * FFmpeg命令的封装类
3
 */
4
public class FFmpegCommand {
5

6
    private List<String> command;
7

8
    public FFmpegCommand(List<String> command) {
9
        this.command = command == null ? new ArrayList<String>() : command;
10
    }
11

12
    public List<String> getCommand() {
13
        return command;
14
    }
15

16
    public void setCommand(List<String> command) {
17
        this.command = command;
18
    }
19

20
    /**
21
     * 开始执行命令
22
     *
23
     * @param callback 回调
24
     * @return 命令的信息输出
25
     * @throws FFmpegCommandException
26
     */
27
    public String start(FFmpegCallback callback) throws FFmpegCommandException {
28
        BufferedReader br = null;
29
        StringBuffer sbf = new StringBuffer();
30
        String resultInfo = null;
31
        try {
32
            ProcessBuilder builder = new ProcessBuilder();
33
            builder.command(command);
34
            //正常信息和错误信息合并输出
35
            builder.redirectErrorStream(true);
36
            //开启执行子线程
37
            Process process = builder.start();
38

39
            String line = null;
40
            br = new BufferedReader(new InputStreamReader(process.getInputStream()));
41
            while ((line = br.readLine()) != null) {
42
                sbf.append(line);
43
                sbf.append(" ");
44
            }
45
            resultInfo = sbf.toString();
46

47
            //等待命令子线程执行完成
48
            int exitValue = process.waitFor();
49
            //完成后执行回调
50
            if (exitValue == 0 && callback != null) {
51
                callback.complete(resultInfo);
52
            }
53
            //销毁子线程
54
            process.destroy();
55

56
        } catch (IOException e) {
57
            e.printStackTrace();
58
            throw new FFmpegCommandException(e.getMessage());
59
        } catch (InterruptedException e) {
60
            e.printStackTrace();
61
            throw new FFmpegCommandException("线程阻塞异常:" + e.getMessage());
62
        } finally {
63
            try {
64
                if (br != null) {
65
                    br.close();
66
                }
67
            } catch (IOException e) {
68
                e.printStackTrace();
69
            }
70
        }
71

72
        return resultInfo;
73
    }
74

75
}

自然,要有builder类:
public class FFmpegCommandBuilder {

    List<String> command = new ArrayList<String>();

    public FFmpegCommandBuilder(String exePath) {
if (exePath == null) {
throw new FFmpegCommandRuntimeException("ffmpeg.exe 路径不得为空");
}
//添加命令的exe执行文件位置
command.add(exePath);
} /**
* 添加输入文件的路径
*
* @param inputFilePath
*/
public FFmpegCommandBuilder input(String inputFilePath) {
if (inputFilePath != null) {
command.add("-i");
command.add(inputFilePath);
}
return this;
} /**
* 添加输出文件的路径
*
* @param outputFilePath
*/
public FFmpegCommandBuilder output(String outputFilePath) {
if (outputFilePath != null) {
command.add(outputFilePath);
}
return this;
} /**
* 覆盖输出文件
*/
public FFmpegCommandBuilder override() {
command.add("-y");
return this;
} /**
* 强制输出格式
*
* @param format 输出格式
*/
public FFmpegCommandBuilder format(FFmpegCommandFormatEnum format) {
if (format != null) {
command.add("-f");
command.add(format.getValue());
}
return this;
} /**
* 设置录制/转码的时长
*
* @param duration 形如 0.001 表示0.001秒,hh:mm:ss[.xxx]格式的记录时间也支持
*/
public FFmpegCommandBuilder duration(String duration) {
if (duration != null) {
command.add("-t");
command.add(duration);
}
return this;
} /**
* 搜索到指定的起始时间
*
* @param position 形如 17 表示17秒,[-]hh:mm:ss[.xxx]的格式也支持
*/
public FFmpegCommandBuilder position(String position) {
if (position != null) {
command.add("-ss");
command.add(position);
}
return this;
} /**
* 设置帧大小
*
* @param size 形如 xxx*xxx
* @return
*/
public FFmpegCommandBuilder size(String size) {
if (size != null) {
command.add("-s");
command.add(size);
}
return this;
} /**
* 创建FFmpegCommand命令封装类
*
* @return FFmpegCommand
*/
public FFmpegCommand build() {
return new FFmpegCommand(command);
} }
108
 
1
public class FFmpegCommandBuilder {
2

3
    List<String> command = new ArrayList<String>();
4

5
    public FFmpegCommandBuilder(String exePath) {
6
        if (exePath == null) {
7
            throw new FFmpegCommandRuntimeException("ffmpeg.exe 路径不得为空");
8
        }
9
        //添加命令的exe执行文件位置
10
        command.add(exePath);
11
    }
12

13
    /**
14
     * 添加输入文件的路径
15
     *
16
     * @param inputFilePath
17
     */
18
    public FFmpegCommandBuilder input(String inputFilePath) {
19
        if (inputFilePath != null) {
20
            command.add("-i");
21
            command.add(inputFilePath);
22
        }
23
        return this;
24
    }
25

26
    /**
27
     * 添加输出文件的路径
28
     *
29
     * @param outputFilePath
30
     */
31
    public FFmpegCommandBuilder output(String outputFilePath) {
32
        if (outputFilePath != null) {
33
            command.add(outputFilePath);
34
        }
35
        return this;
36
    }
37

38
    /**
39
     * 覆盖输出文件
40
     */
41
    public FFmpegCommandBuilder override() {
42
        command.add("-y");
43
        return this;
44
    }
45

46
    /**
47
     * 强制输出格式
48
     *
49
     * @param format 输出格式
50
     */
51
    public FFmpegCommandBuilder format(FFmpegCommandFormatEnum format) {
52
        if (format != null) {
53
            command.add("-f");
54
            command.add(format.getValue());
55
        }
56
        return this;
57
    }
58

59
    /**
60
     * 设置录制/转码的时长
61
     *
62
     * @param duration 形如 0.001 表示0.001秒,hh:mm:ss[.xxx]格式的记录时间也支持
63
     */
64
    public FFmpegCommandBuilder duration(String duration) {
65
        if (duration != null) {
66
            command.add("-t");
67
            command.add(duration);
68
        }
69
        return this;
70
    }
71

72
    /**
73
     * 搜索到指定的起始时间
74
     *
75
     * @param position 形如 17 表示17秒,[-]hh:mm:ss[.xxx]的格式也支持
76
     */
77
    public FFmpegCommandBuilder position(String position) {
78
        if (position != null) {
79
            command.add("-ss");
80
            command.add(position);
81
        }
82
        return this;
83
    }
84

85
    /**
86
     * 设置帧大小
87
     *
88
     * @param size 形如 xxx*xxx
89
     * @return
90
     */
91
    public FFmpegCommandBuilder size(String size) {
92
        if (size != null) {
93
            command.add("-s");
94
            command.add(size);
95
        }
96
        return this;
97
    }
98

99
    /**
100
     * 创建FFmpegCommand命令封装类
101
     *
102
     * @return FFmpegCommand
103
     */
104
    public FFmpegCommand build() {
105
        return new FFmpegCommand(command);
106
    }
107

108
}

那么使用builder模式,还是刚才的目的,我们的代码就变成了:
public class FFmpegBuilderTest {

    public static void main(String[] args) {

        String ffmpegExePath = "C:\\workspace\\project\\greejoy\\picManager\\web\\tools\\ffmpeg\\bin\\ffmpeg.exe";
String inputFilePath = "C:\\Users\\Dulk\\Desktop\\ukulele\\01\\《小星星》.mp4";
String outputFilePath = "C:\\Users\\Dulk\\Desktop\\ukulele\\01\\littleStarJavaBuilder.jpg"; FFmpegCommandBuilder builder = new FFmpegCommandBuilder(ffmpegExePath);
builder.input(inputFilePath).format(FFmpegCommandFormatEnum.IMAGE)
.position("10").duration("0.001").size("320*240").output(outputFilePath);
FFmpegCommand command = builder.build();
try {
String result = command.start(null);
System.out.println(result);
} catch (FFmpegCommandException e) {
e.printStackTrace();
}
} }
x
 
1
public class FFmpegBuilderTest {
2

3
    public static void main(String[] args) {
4

5
        String ffmpegExePath = "C:\\workspace\\project\\greejoy\\picManager\\web\\tools\\ffmpeg\\bin\\ffmpeg.exe";
6
        String inputFilePath = "C:\\Users\\Dulk\\Desktop\\ukulele\\01\\《小星星》.mp4";
7
        String outputFilePath = "C:\\Users\\Dulk\\Desktop\\ukulele\\01\\littleStarJavaBuilder.jpg";
8

9
        FFmpegCommandBuilder builder = new FFmpegCommandBuilder(ffmpegExePath);
10
        builder.input(inputFilePath).format(FFmpegCommandFormatEnum.IMAGE)
11
               .position("10").duration("0.001").size("320*240").output(outputFilePath);
12
        FFmpegCommand command = builder.build();
13
        try {
14
            String result = command.start(null);
15
            System.out.println(result);
16
        } catch (FFmpegCommandException e) {
17
            e.printStackTrace();
18
        }
19
    }
20

21
}

Java调用FFmpeg进行视频处理及Builder设计模式的应用的更多相关文章

  1. java调用ffmpeg获取视频文件信息的一些参数

    一.下载ffmpeg http://www.ffmpeg.org/download.html 主要需要bin目录下的ffmpeg可执行文件 二.java代码实现 package com.aw.util ...

  2. Java调用ffmepg+mencoder视频格式转换(*)

    PS: 建议大家在官网下载最新的资源 其他格式转FLV格式,可以用Java调用ffmpeg和memcoder实现 ffmepg: D:\ffmpeg\bin\ffmpeg.exe -i E:\1.mp ...

  3. Java使用FFmpeg处理视频文件指南

    Java使用FFmpeg处理视频文件指南 本文主要讲述如何使用Java + FFmpeg实现对视频文件的信息提取.码率压缩.分辨率转换等功能: 之前在网上浏览了一大圈Java使用FFmpeg处理音视频 ...

  4. Java使用FFmpeg处理视频文件的方法教程

    这篇文章主要给大家介绍了关于Java使用FFmpeg处理视频文件的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧 前言 本文主要 ...

  5. Java+Windows+ffmpeg实现视频转换

    最近由于项目需要,研究了一下如何用Java实现视频转换,“着实”废了点心思,整理整理,写出给自己备忘下. 思路 由于之前没有没法过相关功能的经验,一开始来真不知道从哪里入手.当然,这个解决,googl ...

  6. java调用ffmpeg命令行推流遇到的问题

    1.Java调用命令行,如果没有额外环境变量,不指定工作路径,Runtime有两个方法 public Process exec(String command) public Process exec( ...

  7. NET 2.0(C#)调用ffmpeg处理视频的方法

    另外:ffmpeg的net封装库 http://www.intuitive.sk/fflib/ NET 2.0 调用FFMPEG,并异步读取输出信息的代码...public void ConvertV ...

  8. .Net调用ffmpeg对视频截图

    2019/10/27, .Net c#代码片段 摘要:借助ffmpeg对视频/图片截图.生成缩略图,使用命令行调用ffmpeg工具,支持Linux和Windows 网上很多版本都是需要等待4s的做法, ...

  9. java调用FFmpeg及mencoder转换视频为FLV并截图

    Conver.java package com.ll19.flv; public class Conver { public void run() { try { // 转换并截图 String fi ...

随机推荐

  1. Human Motion Analysis with Wearable Inertial Sensors——阅读2

    Human Motion Analysis with Wearable Inertial Sensors 实时人体运动跟踪已经应用于生物医学领域的许多应用:临床步态分析,运动康复,跌倒检测,关节生物力 ...

  2. T研究:国内云BPM市场规模尚小,预计2018年仅为3.29亿元

    文章摘要:T研究发现,目前国内云BPM市场规模不高,预计今年为3.29亿元,不过其增速稳定,未来发展仍可期. BPM?什么鬼?反正作为“菊外人”的小编是第一次听说. 其实,对于这个词,不光是小编,国内 ...

  3. 简单选择排序算法的C++实现

    简单选择排序采用最简单的选择方法,即在剩余序列中选出最小(或最大)的关键字,和剩余序列的第一个关键字交换位置,依次选择下去,直至使整个序列有序. 算法中两层循环的执行次数和初始序列没有关系,第二层循环 ...

  4. View体系第一篇:基础

    View体系的学习内容为学习刘望舒先生博客总结的内容,大家可到他的博客看到更详细的内容. 一.view之间的继承关系 Viewground用来包裹其他view.在平常的使用中,我们不会直接用到View ...

  5. git 入门教程之基本概念

    基本概念 了解工作区,暂存区和版本库的区别和联系有助于我们更好理解 git 的工作流程,了解命令的操作意图. git 和其他版本控制系统如 svn 的不同之处就是有暂存区的概念. 基本概念 工作区 | ...

  6. [20181130]hash冲突导致查询缓慢.txt

    [20181130]hash冲突导致查询缓慢.txt --//昨天看了链接https://jonathanlewis.wordpress.com/2018/11/26/shrink-space-2/, ...

  7. xxx.jar或者xxx.war中没有主清单属性和spring-boot-maven-plugin的作用

    因为springboot本身集成了tomcat插件,所以我们可以直接使用mvn clean package命令打成jar包或者war包,然后使java -jar xxx.jar 或者 java -ja ...

  8. Hello Flask

    Hello Flask Flask简介 Flask是一个使用Python编写的轻量级Web应用框架.基于Werkzeug WSGI工具箱和Jinja2 模板引擎.Flask使用BSD授权.Flask被 ...

  9. LeetCode算法题-Invert Binary Tree

    这是悦乐书的第194次更新,第199篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第55题(顺位题号是226).反转二叉树.例如: 输入: 4 / \ 2 7 / \ / ...

  10. Linux常用命令-解压缩篇

    前言 Linux常用命令中,有很多用于对文件的压缩或解压,本文将介绍这些解压缩命令中不常见却非常实用的用法. tar tar是linux中最常用的解压缩命令.tar命令可用于处理后缀名为tar,tar ...