JavaEE:ffmpeg使用
程序员文章站
2022-07-10 18:32:47
说明:ffmpeg用于视频编码、视频解码。一、ffmpeg安装/使用(windows系统):1.ffmpeg安装:(1)下载ffmpeg-20200807-fab00b0-win64-static.zip,解压到C:\ffmpeg:https://ffmpeg.zeranoe.com/builds/(2)配置ffmpeg环境变量,在系统变量"Path"末尾加入路径:...;C:\ffmpeg\bin;2.ffmpeg一些命令使用(打开CMD):(1)查看版本号:...
说明:
ffmpeg用于视频编码、视频解码。
一、ffmpeg安装/使用(windows系统):
1.ffmpeg安装:
(1)下载ffmpeg-20200807-fab00b0-win64-static.zip,解压到C:\ffmpeg:
https://ffmpeg.zeranoe.com/builds/
(2)配置ffmpeg环境变量,在系统变量"Path"末尾加入路径:
...;C:\ffmpeg\bin;
2.ffmpeg一些命令使用(打开CMD):
(1)查看版本号:
ffmpeg.exe -version
(2)转化视频文件格式(这里为avi转mp4,也可转为mp3、gif等):
<1>说明:
命令说明:
-c:v >-c:v设置编码格式为libx264(h264的一种)
-s >设置显示分辨率为1280x720
-pix_fmt yuv420p >设置每个像素点采样方式(分三种:yuv4:4:4、yuv4:2:2、yuv4:2:0),用于还原每个像素点的yuv值(亮度与色彩)。
-b:a >设置音频码率为63k
-b:v >设置视频码率为753k
-r >设置帧率为18
Kbps码率计算公式:
10MB视频:10 * 1024 * 1024 * 8 /10 / 1024 = 8192Kbps
分辨率要求:
高清>=960x400、超清>=1280x544、1080p>=1920x800
时长要求:
高清/超清/1080p>=15秒
平均码率要求:
H.265/HEVC(mp4格式):高清>0.56Mbps、超清>0.9Mbps、1080p>2.1Mbps
H.264/AVC/AVCHD/X264(mp4、mkv格式)、RV40/RealVideo 9(rmvb格式)、WMV3/WVC1/WMVA/VC-1/Windows Meadia Video 9(wmv格式):高清>0.86Mbps、超清>1.5Mbps、1080p>3.5Mbps
MPEG-4 Visual/Xvid/Divx(avi、mp4格式):高清>1.6Mbps、超清>3Mbps、1080p>5Mbps
MPEG-2(mpeg/mpg/vob格式)、MPEG-1(mpeg/mpg格式):高清>4Mbps、超清>7.5Mbps、1080p>8Mbps
<2>命令(cd到C:\video目录下,old.avi在此目录):
ffmpeg.exe -i old.avi -c:v libx264 -s 1280x720 -pix_fmt yuv420p -b:a 63k -b:v 753k -r 18 old1.mp4
(3)将mp4分段,生成一个m3u8索引文件+n个ts文件:
说明:
-hls_time >设置每片长度为10秒
-hls_list_size >设置保存分段数量,0保存所有
-hls_segment_filename >设置分段文件名,%06d动态生成6位数字,如new_123456.ts
命令(cd到C:\video目录下,old1.mp4在此目录,生成的m3u8和ts列表放在C:\hls目录下):
ffmpeg -i old1.mp4 -hls_time 10 -hls_list_size 0 -hls_segment_filename ../hls/new_%06d.ts ../hls/new.m3u8
生成的文件如下:
new.m3u8
new_000000.ts
new_000001.ts
new_000002.ts
...
new.m3u8内容如下:
#EXTM3U //开始标志
#EXT-X-VERSION:3 //版本号
#EXT-X-TARGETDURATION:14
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:13.888889,
new_000000.ts //每个片段的名称
#EXTINF:13.888889,
new_000001.ts
...
#EXT-X-ENDLIST //结束标志
二、使用java调用ffmpeg转换视频编码:
1.在工程application.yml中配置ffmpeg.exe路径和源视频与生成的视频目录:
#hls:
hls: #自定义属性,配置ffmpeg与视频加载与输出的地址
ffmpeg‐path: C:/ffmpeg/bin/ffmpeg.exe
avi-dir: C:/video/avi/
mp4-dir: C:/video/mp4/
hls-dir: C:/video/hls/
2.调用ffmpeg进行视频转换的java类:
(1)处理基类,VideoConvertBase.java:
//视频处理基类
public abstract class VideoConvertBase {
protected String ffmpegPath; //ffmpeg.exe路径
public VideoConvertBase(String ffmpegPath) {
this.ffmpegPath = ffmpegPath;
}
//删除已存在的文件
protected void delete(String dirPath, String fileName) {
if (dirPath == null || fileName == null) {
return;
}
File file = new File(dirPath, fileName);
if (file.exists() && file.isFile()) {
file.delete();
}
}
//校验视频长度是否一样
protected Boolean check(String oldVideo, String newVideo) {
if (oldVideo == null || newVideo == null) {
return false;
}
String oldLen = getLenth(oldVideo);
if (oldLen == null) {
return false;
}
if (oldLen.lastIndexOf(".") != -1 && oldLen.length() > 1) {
oldLen = oldLen.substring(0, oldLen.lastIndexOf(".")); //视频分秒
}
String newLen = getLenth(newVideo);
if (newLen == null) {
return false;
}
if (newLen.lastIndexOf(".") != -1 && newLen.length() > 1) {
newLen = newLen.substring(0, newLen.lastIndexOf(".")); //视频分秒
}
if (!oldLen.equals(newLen)) {
return false;
}
return true;
}
//获取视频长度
public String getLenth(String path) {
ArrayList<String> cmd = new ArrayList<>(); //等同于cmd中输入命令:ffmpeg -i old.avi
cmd.add(ffmpegPath);
cmd.add("-i");
cmd.add(path);
try {
Process process = new ProcessBuilder().command(cmd).redirectErrorStream(true).start(); //执行命令
String info = readInfo(process);
int start = info.trim().indexOf("Duration: ");
int end = info.trim().indexOf(", start:");
if (start == -1 || end == -1) {
return null;
}
String time = info.substring(start + 10, end);
if (time != null && !time.equals("")) {
return time.trim();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//读取信息
public String readInfo(Process process) {
InputStream in = null;
try {
StringBuffer sb = new StringBuffer();
in = process.getInputStream();
InputStream errorIn = process.getErrorStream();
boolean done = false;
int maxSleep = 10 * 60;
int sleep = 0;
while (!done) {
if (sleep > maxSleep) {
return "读取错误";
}
try {
while (in.available() > 0) {
sb.append(new Character((char) in.read()));
}
while (errorIn.available() > 0) {
sb.append(new Character((char) in.read()));
}
process.exitValue();
done = true;
} catch (IllegalThreadStateException e) {
e.printStackTrace();
Thread.currentThread().sleep(1000);
sleep++;
}
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
return "读取错误";
} finally {
if (in != null) {
try {
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
(2)avi转mp4的类:
public class AviToMp4Biz extends VideoConvertBase {
private static AviToMp4Biz I;
private AviToMp4Biz(String ffmpegPath) {
super(ffmpegPath);
}
public static AviToMp4Biz I(String ffmpegPath) {
if (I == null) {
I = new AviToMp4Biz(ffmpegPath);
}
return I;
}
private String createMp4Name(String mp4Name) {
return mp4Name.substring(0, mp4Name.lastIndexOf(".")) + ".mp4";
}
//avi转mp4
public String aviToMp4(String aviDir, String aviName, String mp4Dir) {
String mp4Name = createMp4Name(aviName);
String aviPath = aviDir + aviName;
String mp4Path = mp4Dir + mp4Name;
delete(mp4Dir, mp4Name); //删已存在的
File file = new File(mp4Path);
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
//等同在cmd中输入:ffmpeg.exe -i C:/video/avi/old.avi -c:v libx264 -y -s 1280x720 -pix_fmt yuv420p -b:a 63k -b:v 753k -r 18 C:/video/mp4/old.mp4
ArrayList<String> cmd = new ArrayList<>();
cmd.add(ffmpegPath); //配置ffmpeg.exe
cmd.add("-i");
cmd.add(aviPath); //avi文件路径
cmd.add("-c:v");
cmd.add("libx264");
cmd.add("-y");
cmd.add("-s");
cmd.add("1280x720");
cmd.add("-pix_fmt");
cmd.add("yuv420p");
cmd.add("-b:a");
cmd.add("63k");
cmd.add("-b:v");
cmd.add("753k");
cmd.add("-r");
cmd.add("18");
cmd.add(mp4Path); //mp4文件路径
try {
new ProcessBuilder().command(cmd).redirectErrorStream(true).start(); //执行avi转mp4命令
if (check(aviPath, mp4Path)) {
return "avi转mp4成功";
} else {
return "avi转mp4失败,视频文件校验不通过";
}
} catch (IOException e) {
e.printStackTrace();
return "avi转mp4异常: " + e.getMessage();
}
}
}
(3)mp4转hls的类:
public class MP4ToHlsBiz extends VideoConvertBase {
private static MP4ToHlsBiz I;
private MP4ToHlsBiz(String ffmpegPath) {
super(ffmpegPath);
}
public static MP4ToHlsBiz I(String ffmpegPath) {
if (I == null) {
I = new MP4ToHlsBiz(ffmpegPath);
}
return I;
}
private String createM3u8Name(String mp4Name) {
return mp4Name.substring(0, mp4Name.lastIndexOf(".")) + ".m3u8";
}
//mp4转hls
public String mp4ToHls(String mp4Dir, String mp4Name, String hlsDir) {
String m3u8Name = createM3u8Name(mp4Name);
String mp4Path = mp4Dir + mp4Name;
String hlsPath = hlsDir + m3u8Name;
delete(hlsDir, m3u8Name); //删已存在的
//等同在cmd中输入:ffmpeg -i C:/video/mp4/old.mp4 -hls_time 10 -hls_list_size 0 -hls_segment_filename C:/video/hls/old_%06d.ts C:/video/hls/old.m3u8
List<String> cmd = new ArrayList<String>();
cmd.add(ffmpegPath);
cmd.add("-i");
cmd.add(mp4Path);
cmd.add("-hls_time");
cmd.add("10");
cmd.add("-hls_list_size");
cmd.add("0");
cmd.add("-hls_segment_filename");
cmd.add(hlsDir + m3u8Name.substring(0, m3u8Name.lastIndexOf(".")) + "_%06.ts"); //等同:../hls/new_%06d.ts
cmd.add(hlsPath);
try {
new ProcessBuilder().command(cmd).redirectErrorStream(true).start(); //执行mp4转hls命令
if (!check(mp4Path, hlsPath)) {
return "mp4转hls失败,视频文件校验不通过";
}
ArrayList<String> list = getTsList(hlsPath);
if (list == null || list.isEmpty()) {
return "mp4转hls失败,ts文件为空";
}
return "mp4转hls成功";
} catch (IOException e) {
e.printStackTrace();
return "mp4转hls异常: " + e.getMessage();
}
}
private ArrayList<String> getTsList(String hlsPath) {
ArrayList<String> list = new ArrayList<>();
BufferedReader read = null;
try {
read = new BufferedReader(new FileReader(hlsPath));
String line;
String lastLine = "";
while ((line = read.readLine()) != null) {
if (line.endsWith(".ts")) {
list.add(line);
}
lastLine = line;
}
if (lastLine.contains("#EXT-X-ENDLIST")) {
return list;
}
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (read != null) {
try {
read.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.视频转换业务类:
@Service
public class VideoBiz {//视频转换业务类
@Value("${hls.ffmpeg‐path}") //加载application.yml中配置的ffmpeg路径
String ffmpegPath;
@Value("${hls.avi-dir}") //加载视频路径
String aviDir;
@Value("${hls.mp4-dir}")
String mp4Dir;
@Value("${hls.hls-dir}")
String hlsDir;
public String aviToMp4(String aviName) {//avi转mp4
return AviToMp4Biz.I(ffmpegPath).aviToMp4(aviDir, aviName, mp4Dir);
}
public String mp4ToHls(String mp4Name) {//mp4转hls
return MP4ToHlsBiz.I(ffmpegPath).mp4ToHls(mp4Dir, mp4Name, hlsDir);
}
}
本文地址:https://blog.csdn.net/a526001650a/article/details/107893093