1.文档
官网:FFmpeg
官方使用文档:ffmpeg Documentation
中文简介:https://www.cnblogs.com/leisure_chn/p/10297002.html
函数及时间:ffmpeg日记1011-过滤器-语法高阶,逻辑,函数使用_ffmpeg gte(t,2)-CSDN博客
java集成ffmpeg:SpringBoot集成ffmpeg实现视频转码播放_jave-all-deps-CSDN博客
2.命令
快速复制视频片段
ffmpeg -ss 00:00:40 -i test.mp4 -to 00:01:00 -c:v copy -c:a copy testOutput40.mp4
复制视频片段,并调整视频质量,crf一般为23,crf越大压缩越厉害,质量下降越多
ffmpeg -i test.mp4 -ss 00:30 -to 00:50 -c:v libx264 -crf 23 newTest1.mp4
调整视频分辨率(调整的分辨率超过原来的,不能提升画质)
ffmpeg -i test.mp4 -vf scale=432:240 video_240p.mp4 -hide_banner
ffmpeg -i test.mp4 -vf scale=-1:240 video_240p.mp4 -hide_banner
加水印(10:10水印距离左上角的像素距离)
ffmpeg -i test.mp4 -i used100.png -filter_complex "overlay=10:10" testOutputWater.mp4
--保真加水印,不怎么改变视频编码及质量
ffmpeg -i test.mp4 -i used100.png -filter_complex "overlay=10:10" -c:v libx264 -c:a copy testOutputWater2.mp4
--视频2秒后开始显示水印,gte(t,2)
ffmpeg -i test.mp4 -vf "movie=used100.png[logo];[in][logo]overlay=x='if(gte(t,2),0,NAN)'" testOutputWater6.mp4
--在指定时间范围(20-30秒)显示水印,between(t,20,30)
ffmpeg -i test.mp4 -vf "movie=used100.png[logo];[in][logo]overlay=x='if(between(t,20,30),0,NAN)'" testOutputWater9.mp4
加硬字幕
ffmpeg -i test.mp4 -vf subtitles=test.srt mp4_add_captions1.mp4
3.java代码
maven依赖,使用集成的ffmpeg程序,缺点是打成的jar包很大,优点是不需要手动安装ffmpeg
ws.schild
jave-all-deps
3.0.1
ws.schild
jave-nativebin-win32
ws.schild
jave-nativebin-linux32
ws.schild
jave-nativebin-osx64
第二种方式,不引入集成的ffmpeg,手动在程序运行的服务器上安,优点是打成的jar包较小,关于如何手动在服务器上配置ffmpeg,请见附录2
ws.schild
jave-core
3.0.1
工具类
package mis.shared.file;
import lombok.extern.slf4j.Slf4j;
import mis.shared.date.DateUnitl;
import ws.schild.jave.Encoder;
import ws.schild.jave.EncoderException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;
import ws.schild.jave.encode.VideoAttributes;
import ws.schild.jave.info.MultimediaInfo;
import ws.schild.jave.process.ProcessWrapper;
import ws.schild.jave.process.ffmpeg.DefaultFFMPEGLocator;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.Calendar;
/**
* 音视频操作
*
* @since 2024/1/22
*/
@Slf4j
public class FfmpegUtil {
/**
* 通过本地路径获取多媒体文件信息(宽,高,时长,编码等)
*
* @param filePath 文件路径
* @return MultimediaInfo 媒体对象,包含 (宽,高,时长,编码等)
*/
public static MultimediaInfo GetMediaInfo(String filePath) {
MultimediaInfo multimediaInfo = null;
try {
multimediaInfo = new MultimediaObject(new File(filePath)).getInfo();
} catch (EncoderException e) {
log.error("获取媒体信息异常!", e);
}
return multimediaInfo;
}
/**
* 修改视频分辨率,修改分辨率超过原视频,不能提高画质
* 标清SD(Standard Definition) 宽 x 高
* 480p 640x480 704x480 720x480 848x480
* 高清 HD(High Definition)
* 720p 960x720 1280x720
* 1080p 1440x1080 1920x1080
* 超高清UHD(Ultra High Definition)
* 4k 40963112 4096*2160
*
* @param inputPath 视频来源地址
* @param outputPath 输出视频地址
* @param width 宽度
* @param height 高度
*/
public static boolean ChangeScale(String inputPath, String outputPath, int width, int height) {
ProcessWrapper ffmpeg = null;
try {
if (new File(outputPath).exists()) {
log.error("目标文件已存在,outputPath:{}", outputPath);
return false;
}
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-i");
ffmpeg.addArgument(inputPath);
ffmpeg.addArgument("-vf");
ffmpeg.addArgument("scale=" + width + ":" + height);
ffmpeg.addArgument("-c:a");
ffmpeg.addArgument("copy");
ffmpeg.addArgument(outputPath);
long start = System.currentTimeMillis();
ffmpeg.execute();
//等待完成
WaitFfmpegFinish(ffmpeg);
log.debug("ffmpeg压缩{}花费时间:{}秒", inputPath, (System.currentTimeMillis() - start) / 1000);
return true;
} catch (Exception e) {
log.error("修改视频画质异常", e);
} finally {
if (ffmpeg != null) {
ffmpeg.destroy();
}
}
return false;
}
/**
* 复制视频片段
*
* @param inputPath 视频来源地址
* @param outputPath 输出视频地址
* @param startTime 开始时间,01:02:03在视频的第1小时第2分钟第3秒处
* @param endTime 结束时间,格式同startTime
*/
public static boolean CopyVideo(String inputPath, String outputPath, String startTime, String endTime) {
ProcessWrapper ffmpeg = null;
try {
if (new File(outputPath).exists()) {
log.error("目标文件已存在,outputPath:{}", outputPath);
return false;
}
log.debug("start copy,inputPath:{},outputPath:{}", inputPath, outputPath);
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-ss");
ffmpeg.addArgument(startTime);
ffmpeg.addArgument("-i");
ffmpeg.addArgument(inputPath);
ffmpeg.addArgument("-to");
ffmpeg.addArgument(endTime);
ffmpeg.addArgument("-c:v");
ffmpeg.addArgument("copy");
ffmpeg.addArgument("-c:a");
ffmpeg.addArgument("copy");
ffmpeg.addArgument(outputPath);
long start = System.currentTimeMillis();
//异步执行
ffmpeg.execute();
//等待完成
WaitFfmpegFinish(ffmpeg);
log.info("{}花费时间:{}毫秒", outputPath, System.currentTimeMillis() - start);
return true;
} catch (Exception e) {
log.error("复制视频片段异常", e);
} finally {
if (ffmpeg != null) {
ffmpeg.destroy();
}
}
return false;
}
/**
* 视频格式转换为mp4
*/
public static boolean FormatToMp4(String inputPath, String outputPath) {
try {
File target = new File(outputPath);
if (target.exists()) {
log.error("目标文件已存在,outputPath:{}", outputPath);
return false;
}
MultimediaObject multimediaObject = new MultimediaObject(new File(inputPath));
EncodingAttributes attributes = new EncodingAttributes();
// 设置视频的音频参数
AudioAttributes audioAttributes = new AudioAttributes();
attributes.setAudioAttributes(audioAttributes);
// 设置视频的视频参数
VideoAttributes videoAttributes = new VideoAttributes();
// 设置帧率
videoAttributes.setFrameRate(25);
attributes.setVideoAttributes(videoAttributes);
// 设置输出格式
attributes.setOutputFormat("mp4");
Encoder encoder = new Encoder();
encoder.encode(multimediaObject, target, attributes);
return true;
} catch (Exception e) {
log.error("视频转换异常!", e);
return false;
}
}
/**
* 获取视频第多少秒的缩略图
*/
public static boolean GetThumbnail(String inputPath, int second, String outputPath) {
ProcessWrapper ffmpeg = null;
try {
File target = new File(outputPath);
if (target.exists()) {
log.error("目标文件已存在,outputPath:{}", outputPath);
return false;
}
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-i");
ffmpeg.addArgument(inputPath);
ffmpeg.addArgument("-ss");//第多少秒的视频画面
ffm服务器托管网peg.addArgument("" + second);
ffmpeg.addArgument("-s");
ffmpeg.addArgument("600*360");//图片宽*高
ffmpeg.addArgument(outputPath);
ffmpeg.execute();
//等待执行完成
WaitFfmpegFinish(ffmpeg);
return true;
} catch (Exception e) {
log.error("获取视频缩略图异常", e);
} finally {
if (ffmpeg != null) {
ffmpeg.destroy();
}
}
return false;
}
/**
* 添加水印
*
* @param inputPath 输入视频路径
* @param waterMarkPath 水印文件路径
* @param x 距离左上角水平距离
* @param y 距离左上角垂直距离
* @param outputPath 输出视频路径
*/
public static boolean AddWatermark(String inputPath, String waterMarkPath,
int x, int y, String outputPath) {
ProcessWrapper ffmpeg = null;
try {
File target = new File(outputPath);
if (target.exists()) {
log.error("目标文件已存在,outputPath:{}", outputPath);
return false;
}
ffmpeg = new DefaultFFMPEGLocator().createExecutor();
ffmpeg.addArgument("-i");
ffmpeg.addArgument(inputPath);
ffmpeg.addArgument("-i");
ffmpeg.addArgument(waterMarkPath);
ffmpeg.addArgument("-filter_complex");
ffmpeg.addArgument("overlay=" + x + ":" + y);
ffmpeg.addArgument(outputPath);
ffmpeg.execute();
//等待执行完成
WaitFfmpegFinish(ffmpeg);
} catch (Exception e) {
log.error("添加水印异常", e);
return false;
} finally {
if (ffmpeg != null) {
ffmpeg.destroy();
}
}
return true;
}
/**
* 获取ffmpeg默认路径
*/
public static String GetDefaultFFMPEGPath() {
return new File(System.getProperty("java.io.tmpdir"), "jave/").getAbsolutePath();
}
/**
* 将第多少秒转换为视频时间(HH:mm:ss)
* 例: 第70秒为00:01:10
*
* @param second 视频中的第多少秒
*/
public static String TransferVideoTime(int second) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.add(Calendar.SECOND, second);
return DateUnitl.To服务器托管网String(calendar.getTime(), "HH:mm:ss");
}
/**
* 等待命令执行完成
*/
private static void WaitFfmpegFinish(ProcessWrapper processWrapper) throws Exception {
//输出执行情况不是通过inputStream,而是errorStream
try (BufferedReader br = new BufferedReader(new InputStreamReader(processWrapper.getErrorStream()))) {
String processInfo;
while ((processInfo = br.readLine()) != null) {
//打印执行过程
log.trace(processInfo);
}
} catch (Exception e) {
log.error("等待执行完成异常!", e);
}
}
public static void main(String[] args) {
String inputFile = "e://develop//tmp//test.mp4";
String outputFile = "e://develop//tmp//test685m_10_42min.mp4";
String waterMarKFile = "e://develop//tmp//watermark100x75.png";
String outputPng = "e://develop//tmp//mainTest3.png";
//获取媒体信息
// MultimediaInfo multimediaInfo = GetMediaInfo(inputFile);
// VideoSize videoSize = multimediaInfo.getVideo().getSize();
// log.info("宽:{},高:{},时长:{}秒", videoSize.getWidth(), videoSize.getHeight(),
// multimediaInfo.getDuration() / 1000);
//修改视频分辨率
//ChangeScale(inputFile, outputFile, 640, 480);
//复制视频指定片段
//CopyVideo(inputFile, outputFile, "00:00:10", "00:42:00");
//添加水印
//AddWatermark(inputFile, waterMarKFile, 50, 50, outputFile);
//获取视频第多少秒的缩略图
System.out.println(GetThumbnail(inputFile, 1, outputPng));
}
}
附录1
字幕test.srt
1
00:00:20,000 --> 00:00:30,000
这是视频第20秒到30秒将显示的字幕
2
00:00:50,000 --> 00:00:60,000
这是视频第50秒到60秒将显示的字幕
附录2
关于ffmpeg在服务器上的位置,参考如下代码
public DefaultFFMPEGLocator() {
String os = System.getProperty("os.name").toLowerCase();
boolean isWindows = os.contains("windows");
boolean isMac = os.contains("mac");
LOG.debug("Os name is isWindows: {} isMac: {}", os, isWindows, isMac);
// Dir Folder
File dirFolder = new File(System.getProperty("java.io.tmpdir"), "jave/");
if (!dirFolder.exists()) {
LOG.debug(
"Creating jave temp folder to place executables in ", dirFolder.getAbsolutePath());
dirFolder.mkdirs();
} else {
LOG.debug("Jave temp folder exists in ", dirFolder.getAbsolutePath());
}
// -----------------ffmpeg executable export on disk.-----------------------------
String suffix = isWindows ? ".exe" : (isMac ? "-osx" : "");
String arch = System.getProperty("os.arch");
// File
File ffmpegFile = new File(dirFolder, "ffmpeg-" + arch + "-" + Version.getVersion() + suffix);
LOG.debug("Executable path: {}", ffmpegFile.getAbsolutePath());
// Check the version of existing .exe file
if (ffmpegFile.exists()) {
// OK, already present
LOG.debug("Executable exists in ", ffmpegFile.getAbsolutePath());
} else {
LOG.debug("Need to copy executable to ", ffmpegFile.getAbsolutePath());
copyFile("ffmpeg-" + arch + suffix, ffmpegFile);
}
// Need a chmod?
if (!isWindows) {
try {
Runtime.getRuntime().exec(new String[] {"/bin/chmod", "755", ffmpegFile.getAbsolutePath()});
} catch (IOException e) {
LOG.error("Error setting executable via chmod", e);
}
}
// Everything seems okay
path = ffmpegFile.getAbsolutePath();
if (ffmpegFile.exists())
{
LOG.debug("ffmpeg executable found: {}", path);
}
else
{
LOG.error("ffmpeg executable NOT found: {}", path);
}
}
windows环境,打开cmd窗口,执行echo %TEMP%获取临时文件夹,打开临时文件夹,创建子文件夹jave(最终路径例子C:UsersTNAppDataLocalTempjave)
下载ws.schild的maven对应系统jar包,并解压jave-nativebin-win64-3.0.1,找到里面的ffmpeg-amd64.exe复制到上面的目录下
linux通过脚本获取默认目录,原理是通过编译运行FfmpegHelper.java获得
#!/bin/bash
cd /tmp
# download
if [ -e "/tmp/FfmpegHelper.java" ]
then
echo "FfmpegHelper.java already exist!"
else
echo "FfmpegHelper.java does not exist,start download"
wget https://fs-im-kefu.7moor-fs1.com/29397395/4d2c3f00-7d4c-11e5-af15-41bf63ae4ea0/1705980395189/FfmpegHelper.java
fi
# compile
if [ -e "/tmp/FfmpegHelper.class" ]
then
echo "FfmpegHelper.class already exist!"
else
echo "FfmpegHelper.class does not exist,start compile"
javac FfmpegHelper.java
fi
# run
java FfmpegHelper
# clean
rm -rf /tmp/FfmpegHelper.class
rm -rf /tmp/FfmpegHelper.java
FfmpegHelper.java
import java.io.File;
public class FfmpegHelper {
public static String GetDefaultFFMPEGPath() {
return new File(System.getProperty("java.io.tmpdir"), "jave/").getAbsolutePath();
}
public static void main(String[] args) {
String defaultFFMPEGPath = GetDefaultFFMPEGPath();
System.out.println(defaultFFMPEGPath);
}
}
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
lockdep是内核提供协助发现死锁问题的功能。 本文首先介绍何为lockdep,然后如何在内核使能lockdep,并简单分析内核lockdep相关代码。 最后构造不同死锁用例,并分析如何根据lockdep输出发现问题根源。 Lockdep介绍 死锁是指两个或…