From 65c596c808106d4394d09929fb13db58598cc452 Mon Sep 17 00:00:00 2001 From: wanggeng888 <450292408@qq.com> Date: Thu, 10 Jun 2021 18:16:59 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../file/media/manager/MediaManager.java | 78 +++++++++++++++---- .../media/manager/process/IMediaStream.java | 24 ++++++ .../service/video/impl/VideoServiceImpl.java | 11 +-- .../transcoding/VideoTransCodingManager.java | 31 ++++++++ .../runnable/VideoTransCodingRunnable.java | 53 +++++++++++++ .../src/test/java/MediaTest.java | 50 ++++++++++++ 6 files changed, 229 insertions(+), 18 deletions(-) create mode 100644 module-file-media/src/main/java/ink/wgink/module/file/media/manager/process/IMediaStream.java create mode 100644 module-file-media/src/main/java/ink/wgink/module/file/media/task/transcoding/VideoTransCodingManager.java create mode 100644 module-file-media/src/main/java/ink/wgink/module/file/media/task/transcoding/runnable/VideoTransCodingRunnable.java diff --git a/module-file-media/src/main/java/ink/wgink/module/file/media/manager/MediaManager.java b/module-file-media/src/main/java/ink/wgink/module/file/media/manager/MediaManager.java index 1986f49b..e087107d 100644 --- a/module-file-media/src/main/java/ink/wgink/module/file/media/manager/MediaManager.java +++ b/module-file-media/src/main/java/ink/wgink/module/file/media/manager/MediaManager.java @@ -4,6 +4,7 @@ import ink.wgink.module.file.media.manager.domain.ImageMetaInfo; import ink.wgink.module.file.media.manager.domain.MusicMetaInfo; import ink.wgink.module.file.media.manager.domain.VideoMetaInfo; import ink.wgink.module.file.media.manager.domain.gif.AnimatedGifEncoder; +import ink.wgink.module.file.media.manager.process.IMediaStream; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; @@ -98,7 +99,8 @@ public class MediaManager { musicStreamPattern = Pattern.compile(musicStreamRegex); } - private MediaManager() {} + private MediaManager() { + } public static MediaManager getInstance() { return MEDIA_MANAGER; @@ -156,7 +158,6 @@ public class MediaManager { return true; } - /** * 执行FFmpeg命令 * @@ -212,21 +213,69 @@ public class MediaManager { } } + /** + * 执行命令 + * + * @param commonds + * @param mediaStream + * @return + */ + public void executeCommand(List commonds, IMediaStream mediaStream) { + if (CollectionUtils.isEmpty(commonds)) { + LOG.error("--- 指令执行失败,因为要执行的FFmpeg指令为空! ---"); + return; + } + LinkedList ffmpegCmds = new LinkedList<>(commonds); + ffmpegCmds.addFirst(FFMPEG_PATH); + LOG.info("--- 待执行的FFmpeg指令为:---" + ffmpegCmds); + + Runtime runtime = Runtime.getRuntime(); + Process ffmpeg = null; + try { + // 执行ffmpeg指令 + ProcessBuilder builder = new ProcessBuilder(); + builder.command(ffmpegCmds); + ffmpeg = builder.start(); + LOG.info("--- 开始执行FFmpeg指令:--- 执行线程名:" + builder.toString()); + + // 取出输出流和错误流的信息 + // 注意:必须要取出ffmpeg在执行命令过程中产生的输出信息,如果不取的话当输出流信息填满jvm存储输出留信息的缓冲区时,线程就回阻塞住 + mediaStream.error(ffmpeg.getErrorStream()); + mediaStream.input(ffmpeg.getInputStream()); + } catch (Exception e) { + LOG.error("--- FFmpeg命令执行出错! --- 出错信息: " + e.getMessage()); + } finally { + if (null != ffmpeg) { + ProcessKiller ffmpegKiller = new ProcessKiller(ffmpeg); + // JVM退出时,先通过钩子关闭FFmepg进程 + runtime.addShutdownHook(ffmpegKiller); + } + } + } /** * 视频转换 *

* 注意指定视频分辨率时,宽度和高度必须同时有值; * - * @param fileInput 源视频路径 - * @param fileOutPut 转换后的视频输出路径 - * @param withAudio 是否保留音频;true-保留,false-不保留 - * @param crf 指定视频的质量系数(值越小,视频质量越高,体积越大;该系数取值为0-51,直接影响视频码率大小),取值参考:CrfValueEnum.code - * @param preset 指定视频的编码速率(速率越快压缩率越低),取值参考:PresetVauleEnum.presetValue - * @param width 视频宽度;为空则保持源视频宽度 - * @param height 视频高度;为空则保持源视频高度 + * @param fileInput 源视频路径 + * @param fileOutPut 转换后的视频输出路径 + * @param withAudio 是否保留音频;true-保留,false-不保留 + * @param crf 指定视频的质量系数(值越小,视频质量越高,体积越大;该系数取值为0-51,直接影响视频码率大小),取值参考:CrfValueEnum.code + * @param preset 指定视频的编码速率(速率越快压缩率越低),取值参考:PresetVauleEnum.presetValue + * @param width 视频宽度;为空则保持源视频宽度 + * @param height 视频高度;为空则保持源视频高度 + * @param mediaStream 媒体流,处理转码的过程中产生的流信息 + * @return 转换结果 */ - public void convertVideo(File fileInput, File fileOutPut, boolean withAudio, Integer crf, String preset, Integer width, Integer height) { + public void convertVideo(File fileInput, + File fileOutPut, + boolean withAudio, + Integer crf, + String preset, + Integer width, + Integer height, + IMediaStream mediaStream) { if (null == fileInput || !fileInput.exists()) { throw new RuntimeException("源视频文件不存在,请检查源视频路径"); } @@ -268,10 +317,13 @@ public class MediaManager { commond.add("-y"); // 当已存在输出文件时,不提示是否覆盖 commond.add(fileOutPut.getAbsolutePath()); - executeCommand(commond); + if (mediaStream == null) { + executeCommand(commond); + return; + } + executeCommand(commond, mediaStream); } - /** * 视频帧抽取 * 默认抽取第10秒的帧画面 @@ -932,7 +984,7 @@ public class MediaManager { } } - public static class MediaManagerBuilder { + private static class MediaManagerBuilder { static MediaManager mediaManager = new MediaManager(); } diff --git a/module-file-media/src/main/java/ink/wgink/module/file/media/manager/process/IMediaStream.java b/module-file-media/src/main/java/ink/wgink/module/file/media/manager/process/IMediaStream.java new file mode 100644 index 00000000..301f2eb6 --- /dev/null +++ b/module-file-media/src/main/java/ink/wgink/module/file/media/manager/process/IMediaStream.java @@ -0,0 +1,24 @@ +package ink.wgink.module.file.media.manager.process; + +import ink.wgink.module.file.media.manager.MediaManager; + +import java.io.InputStream; + + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: MediaProcess + * @Description: 媒体流 + * @Author: wanggeng + * @Date: 2021/6/10 10:52 上午 + * @Version: 1.0 + */ +public interface IMediaStream { + + void input(InputStream inputStream) throws Exception; + + void error(InputStream errorStream) throws Exception; + +} diff --git a/module-file-media/src/main/java/ink/wgink/module/file/media/service/video/impl/VideoServiceImpl.java b/module-file-media/src/main/java/ink/wgink/module/file/media/service/video/impl/VideoServiceImpl.java index 7f22cfe2..129c846a 100644 --- a/module-file-media/src/main/java/ink/wgink/module/file/media/service/video/impl/VideoServiceImpl.java +++ b/module-file-media/src/main/java/ink/wgink/module/file/media/service/video/impl/VideoServiceImpl.java @@ -58,17 +58,17 @@ public class VideoServiceImpl extends DefaultBaseService implements IVideoServic long fileSize = video.getSize(); String fileType = mediaService.getFileType(fileName); String fileMd5 = mediaService.upload(video, filePath, fileName); - String fileFullPath = filePath + File.separator + fileName; + String fileFullPath = filePath; // 构建视频内容 VideoVO videoVO = new VideoVO(); videoVO.setFileName(fileName); videoVO.setFileFullPath(fileFullPath); - videoVO.setFilePath(VIDEO_PATH + File.separator + fileName); + videoVO.setFilePath(VIDEO_PATH); videoVO.setFileSize(fileSize); videoVO.setFileType(fileType); videoVO.setFileMd5(fileMd5); - File uploadFile = new File(fileFullPath); + File uploadFile = new File(fileFullPath + File.separator + fileName); VideoMetaInfo videoMetaInfo = MediaManager.getInstance().getVideoMetaInfo(uploadFile); if (videoMetaInfo == null) { throw new FileException("上传失败"); @@ -86,8 +86,9 @@ public class VideoServiceImpl extends DefaultBaseService implements IVideoServic videoVO.setAudioBitRate(musicMetaInfo.getBitRate()); videoVO.setAudioSampleRate(musicMetaInfo.getSampleRate()); } - - return save(token, videoVO); + String fileId = save(token, videoVO); + // 转码 + return fileId; } } diff --git a/module-file-media/src/main/java/ink/wgink/module/file/media/task/transcoding/VideoTransCodingManager.java b/module-file-media/src/main/java/ink/wgink/module/file/media/task/transcoding/VideoTransCodingManager.java new file mode 100644 index 00000000..4cd3dbb0 --- /dev/null +++ b/module-file-media/src/main/java/ink/wgink/module/file/media/task/transcoding/VideoTransCodingManager.java @@ -0,0 +1,31 @@ +package ink.wgink.module.file.media.task.transcoding; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: VideoTransCodingManager + * @Description: 视频转码 + * @Author: wanggeng + * @Date: 2021/6/10 9:46 上午 + * @Version: 1.0 + */ +public class VideoTransCodingManager { + + private static VideoTransCodingManager VIDEO_TRANS_CODING_MANAGER = VideoTransCodingManagerBuilder.videoTransCodingManager; + private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10, 30L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); + + private VideoTransCodingManager() {} + + + + private static class VideoTransCodingManagerBuilder { + public static VideoTransCodingManager videoTransCodingManager = new VideoTransCodingManager(); + } + +} diff --git a/module-file-media/src/main/java/ink/wgink/module/file/media/task/transcoding/runnable/VideoTransCodingRunnable.java b/module-file-media/src/main/java/ink/wgink/module/file/media/task/transcoding/runnable/VideoTransCodingRunnable.java new file mode 100644 index 00000000..3efed090 --- /dev/null +++ b/module-file-media/src/main/java/ink/wgink/module/file/media/task/transcoding/runnable/VideoTransCodingRunnable.java @@ -0,0 +1,53 @@ +package ink.wgink.module.file.media.task.transcoding.runnable; + +import ink.wgink.module.file.media.manager.MediaManager; +import ink.wgink.module.file.media.manager.domain.enums.CrfValueEnum; +import ink.wgink.module.file.media.manager.domain.enums.PresetVauleEnum; +import ink.wgink.module.file.media.manager.process.IMediaStream; +import ink.wgink.module.file.media.service.video.IVideoService; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * When you feel like quitting. Think about why you started + * 当你想要放弃的时候,想想当初你为何开始 + * + * @ClassName: VideoTransCodingRunnable + * @Description: 视频转码 + * @Author: wanggeng + * @Date: 2021/6/10 10:04 上午 + * @Version: 1.0 + */ +public class VideoTransCodingRunnable implements Runnable { + + private String sourcePath; + private String sourceName; + private IVideoService videoService; + + @Override + public void run() { + File sourceFile = new File(sourcePath + File.separator + sourceName); + File outFile = new File(sourcePath + File.separator + sourceName + ".mp4"); + MediaManager.getInstance().convertVideo(sourceFile, outFile, true, CrfValueEnum.HIGH_QUALITY.getCode(), PresetVauleEnum.MAX_FAST_ZIP_SPEED.getPresetValue(), null, null, new IMediaStream() { + @Override + public void input(InputStream inputStream) throws Exception { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + for (String line; (line = bufferedReader.readLine()) != null; ) { + System.out.println("input-" + line); + } + } + + @Override + public void error(InputStream errorStream) throws Exception { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(errorStream)); + for (String line; (line = bufferedReader.readLine()) != null; ) { + System.out.println("error-" + line); + } + } + }); + } + +} diff --git a/module-file-media/src/test/java/MediaTest.java b/module-file-media/src/test/java/MediaTest.java index 9bbd9978..4875641e 100644 --- a/module-file-media/src/test/java/MediaTest.java +++ b/module-file-media/src/test/java/MediaTest.java @@ -1,10 +1,16 @@ import ink.wgink.module.file.media.manager.MediaManager; import ink.wgink.module.file.media.manager.domain.VideoMetaInfo; +import ink.wgink.module.file.media.manager.domain.enums.CrfValueEnum; +import ink.wgink.module.file.media.manager.domain.enums.PresetVauleEnum; +import ink.wgink.module.file.media.manager.process.IMediaStream; import org.junit.Test; import java.io.BufferedReader; import java.io.File; +import java.io.InputStream; import java.io.InputStreamReader; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * When you feel like quitting. Think about why you started @@ -44,5 +50,49 @@ public class MediaTest { process.destroy(); } + @Test + public void t3() { + MediaManager.getInstance().setFFmpegPath("/Users/wanggeng/ffmpeg/ffmpeg"); + String sourcePath = "/Users/wanggeng/Desktop/UploadFiles/videos/20210119"; + String sourceName = "85355a761e3442cda765c3bc6f5bd526.mp4"; + File sourceFile = new File(sourcePath + File.separator + sourceName); + File outFile = new File(sourcePath + File.separator + sourceName + ".mp4"); + MediaManager.getInstance().convertVideo(sourceFile, outFile, true, CrfValueEnum.LOW_QUALITY.getCode(), PresetVauleEnum.MAX_FAST_ZIP_SPEED.getPresetValue(), null, null, new IMediaStream() { + @Override + public void input(InputStream inputStream) throws Exception { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + for (String line; (line = bufferedReader.readLine()) != null; ) { + System.out.println(line); + } + } + + @Override + public void error(InputStream errorStream) throws Exception { + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(errorStream)); + for (String line; (line = bufferedReader.readLine()) != null; ) { + System.out.println(line); + } + } + }); + } + + @Test + public void t4() { + String line = " Duration: 00:14:05.11, start: 0.000000, bitrate: 1022 kb/s"; + String line2 = "frame= 1 fps=0.0 q=0.0 size= 0kB time=00:00:00.13 bitrate= 2.8kbits/s speed=3.58x "; + Pattern durationPattern = Pattern.compile("Duration: \\d{2}:\\d{2}:\\d{2}\\.\\d{2}"); + Pattern timePattern = Pattern.compile("time=\\d{2}:\\d{2}:\\d{2}\\.\\d{2}"); + Matcher durationMatcher = durationPattern.matcher(line); + Matcher timeMatcher = timePattern.matcher(line2); + while(durationMatcher.find()) { + String count = durationMatcher.group(); + System.out.println(count); + } + while (timeMatcher.find()) { + String count = timeMatcher.group(); + System.out.println(count); + } + } + }