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-deps3.0.1 ws.schild jave-nativebin-win32ws.schild jave-nativebin-linux32ws.schild jave-nativebin-osx64
第二种方式,不引入集成的ffmpeg,手动在程序运行的服务器上安,优点是打成的jar包较小,关于如何手动在服务器上配置ffmpeg,请见附录2
ws.schild jave-core3.0.1
工具类
package mis.shared.file; import lombok.extern.slf4j.Slf4j; 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; /** * 音视频操作 * * @since 2024/1/22 */ @Slf4j public class FfmpegUtil { /** * 通过本地路径获取多媒体文件信息(宽,高,时长,编码等) * * @param localPath 文件路径 * @return MultimediaInfo 媒体对象,包含 (宽,高,时长,编码等) */ public static MultimediaInfo GetMediaInfo(String localPath) { MultimediaInfo multimediaInfo = null; try { multimediaInfo = new MultimediaObject(new File(localPath)).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 4096×3112 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.info("花费时间:{}", System.currentTimeMillis() - start); 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; } 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("花费时间:{}", 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 localPath, String outputPath) { try { File target = new File(outputPath); if (target.exists()) { log.error("目标文件已存在,outputPath:{}", outputPath); return false; } MultimediaObject multimediaObject = new MultimediaObject(new File(localPath)); 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; } } /** * 获取视频缩略图 * 获取视频第0秒的第一帧图片 */ public static boolean GetThumbnail(String localPath, 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(localPath); ffmpeg.addArgument("-ss"); ffmpeg.addArgument("0"); ffmpeg.addArgument(outputPath); ffmpeg.execute(); //等待执行完成 WaitFfmpegFinish(ffmpeg); } catch (Exception e) { log.error("获取视频缩略图异常", e); return false; } finally { if (ffmpeg != null) { ffmpeg.destroy(); } } return true; } /** * 等待命令执行完成 */ 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//testOutput14.mp4"; //复制视频指定片段 CopyVideo(inputFile, outputFile, "00:00:10", "00:01:00"); //获取媒体信息 // MultimediaInfo multimediaInfo = GetMediaInfo(inputFile); // log.info("文件信息:{}", multimediaInfo); // VideoSize videoSize = multimediaInfo.getVideo().getSize(); // log.info("文件信息,宽:{},高:{}", videoSize.getWidth(), videoSize.getHeight()); //修改视频画质 //ChangeScale(inputFile, outputFile, 640, 480); } }
附录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:\Users\TN\AppData\Local\Temp\jave)
下载ws.schild的maven对应系统jar包,并解压jave-nativebin-win64-3.0.1,找到里面的ffmpeg-amd64.exe复制到上面的目录下
猜你喜欢
网友评论
- 搜索
- 最新文章
- 热门文章