├── .gitignore ├── README.md ├── pom.xml └── src ├── main └── java │ └── org │ └── mountcloud │ └── ffmepg │ ├── annotation │ ├── FFAnnotation.java │ ├── FFCmd.java │ └── FFCmdBean.java │ ├── excption │ ├── FFMpegOperationConvertExcption.java │ └── FFMpegOperationNotFoundExcption.java │ ├── operation │ ├── FFOperationBase.java │ └── ffmpeg │ │ ├── FFMpegOperationBase.java │ │ └── vidoe │ │ ├── FFMpegVideoFormatM3u8.java │ │ ├── FFMpegVideoInfo.java │ │ └── FFMpegVideoScreenShot.java │ ├── result │ ├── FFResult.java │ └── defaultResult │ │ ├── FFDefaultResult.java │ │ └── FFVideoInfoResult.java │ ├── task │ ├── bean │ │ ├── FFTask.java │ │ ├── FFTaskProgress.java │ │ ├── FFTaskStateEnum.java │ │ ├── FFVideoTask.java │ │ ├── read │ │ │ ├── FFTaskReadState.java │ │ │ ├── FFTaskReadThread.java │ │ │ └── FFTaskReadType.java │ │ └── tasks │ │ │ ├── FFMepgVideoFormatM3u8Task.java │ │ │ ├── FFMepgVideoInfoTask.java │ │ │ └── FFMpegVideoScreenShotTask.java │ ├── context │ │ └── FFTaskContext.java │ └── threads │ │ ├── FFThread.java │ │ └── FFThreadManager.java │ └── util │ ├── FFAnnotationUtil.java │ ├── FFBigDecimalUtil.java │ ├── FFTerminalCreater.java │ ├── FFVideoUtil.java │ ├── OSUtils.java │ ├── ObjectUtil.java │ ├── StringUtil.java │ └── UUIDUtil.java └── test └── java └── org └── mountcloud └── ffmpeg └── TestTask.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | bin/* 3 | !.mvn/wrapper/maven-wrapper.jar 4 | .swp 5 | .DS_Store 6 | ### STS ### 7 | .apt_generated 8 | .classpath 9 | .factorypath 10 | .project 11 | .settings 12 | .springBeans 13 | bootstrap.yml 14 | 15 | ### IntelliJ IDEA ### 16 | .idea 17 | *.iws 18 | *.iml 19 | *.ipr 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # USE 使用 2 | 3 | 4 | org.mountcloud 5 | ffmpeg-operate 6 | 1.2 7 | 8 | 9 | # English 10 | 11 | ## Description 12 |   The prerequisite for this project is that FFMPEG is installed in the environment and FFMPEG commands can be used in the console. This project is very convenient for java to use FFMPEG function. 13 | 14 |   There are 3 built-in video operations in the project, 1: video format is changed to M3U8 (FFMpegVideoFormatM3u8), 2: view video attribute (FFMpegVideoInfo), 3: video screenshot (FFMpegVideoScreenShot). It can also be extended by inheriting FFMpegOperationBase. 15 | 16 | ## Features 17 | 1: You can get the execution percentage progress of the task. 18 | 19 | 2: Very high scalability. 20 | 21 | 3: Multi-threaded mode of operation. 22 | 23 | 4: Tasks can be performed synchronously or asynchronously. 24 | 25 | # 中文 26 | 27 | ## 描述 28 | 29 |   这个项目使用的前提条件是环境中安装了FFMPEG,并且可以在控制台中使用FFMPEG命令。本项目极大方便了java使用FFMPEG功能。 30 | 31 |   项目中内置了3个对视频的操作,1:视频格式转为M3U8(FFMpegVideoFormatM3u8),2:查看视频属性(FFMpegVideoInfo),3:视频截图(FFMpegVideoScreenShot)。还可以通过继承FFMpegOperationBase来进行扩展。 32 | 33 | ## 特点 34 | 35 | 1:可以获取任务的执行百分比进度。 36 | 37 | 2:非常高的可扩展性。 38 | 39 | 3:多线程方式进行操作。 40 | 41 | 4:可同步或异步执行。 42 | 43 | 44 | # Demo 用例 45 | 46 | 47 | @Test 48 | public void convertM3u8() { 49 | 50 | //create result bean 51 | FFVideoInfoResult result = new FFVideoInfoResult(); 52 | 53 | //find video info 54 | FFMpegVideoInfo ffMpegVideoInfo = new FFMpegVideoInfo(); 55 | ffMpegVideoInfo.setVideoUrl("D:\\cma_15307640036trzll1p.mp4"); 56 | FFMepgVideoInfoTask videoInfoTask = new FFMepgVideoInfoTask(result,ffMpegVideoInfo); 57 | 58 | FFTaskContext.getContext().submit(videoInfoTask,null); 59 | 60 | String bitrate = "5286k"; 61 | 62 | //create to m3u8 operation 63 | FFMpegVideoFormatM3u8 m3u8Operation = new FFMpegVideoFormatM3u8(); 64 | m3u8Operation.setVideoFileName("D:\\cma_15307640036trzll1p.mp4"); 65 | m3u8Operation.setBitrate(bitrate); 66 | m3u8Operation.setTimes(5); 67 | m3u8Operation.setM3u8File("D:\\cma_15307640036trzll1p\\cma_15307640036trzll1p.m3u8"); 68 | m3u8Operation.setTsFiles("D:\\cma_15307640036trzll1p\\cma_15307640036trzll1p%5d.ts"); 69 | 70 | //to m3u8 task 71 | FFMepgVideoFormatM3u8Task task = new FFMepgVideoFormatM3u8Task(m3u8Operation); 72 | 73 | //add task 74 | FFTaskContext.getContext().addTask(task); 75 | 76 | while(!task.getProgress().getState().equals(FFTaskStateEnum.COMPLETE)){ 77 | try { 78 | Thread.sleep(2000); 79 | } catch (InterruptedException e) { 80 | } 81 | } 82 | 83 | System.out.println("COMPLETE"); 84 | 85 | } 86 | 87 | ## note 笔记 88 | 1: FFTaskContext.getContext().addTask(task) is an asynchronous task, FFTaskContext.getContext().submit(taks) is a synchronous task. 89 | 90 | 1:FFTaskContext.getContext().addTask(task) 为异步任务,FFTaskContext.getContext().submit(taks)为同步任务 91 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | org.mountcloud 8 | ffmpeg-operate 9 | 1.2 10 | 11 | A FFmpeg Java Operate 12 | 13 | 14 | jar 15 | 16 | 17 | 18 | 19 | The Apache Software License, Version 2.0 20 | http://www.apache.org/licenses/LICENSE-2.0.txt 21 | 22 | 23 | 24 | 25 | ${project.groupId}:${project.artifactId} 26 | 27 | https://github.com/MountCloud/ffmpeg-operate 28 | 29 | 30 | 31 | 32 | 33 | zhanghaishan 34 | moutcloud@outlook.com 35 | 36 | 37 | 38 | 39 | 40 | scm:git:git@github.com:MountCloud/ffmpeg-operate.git 41 | scm:git:git@github.com:MountCloud/ffmpeg-operate.git 42 | git@github.com:MountCloud/ffmpeg-operate.git 43 | 44 | 45 | 46 | UTF-8 47 | UTF-8 48 | 1.8 49 | 1.8 50 | 51 | 52 | 53 | 54 | 55 | junit 56 | junit 57 | 4.12 58 | 59 | test 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | release 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-source-plugin 74 | 2.2.1 75 | 76 | 77 | package 78 | 79 | jar-no-fork 80 | 81 | 82 | 83 | 84 | 85 | 86 | org.apache.maven.plugins 87 | maven-javadoc-plugin 88 | 2.9.1 89 | 90 | 91 | package 92 | 93 | jar 94 | 95 | 96 | 97 | 98 | 99 | 100 | org.apache.maven.plugins 101 | maven-gpg-plugin 102 | 1.5 103 | 104 | 105 | verify 106 | 107 | sign 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | mountoss 117 | https://oss.sonatype.org/content/repositories/snapshots/ 118 | 119 | 120 | mountoss 121 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-compiler-plugin 133 | 134 | 1.8 135 | 1.8 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/annotation/FFAnnotation.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.annotation; 2 | 3 | /** 4 | * ffmpeg的注解,用于注解属性 5 | * com.ugirls.ffmepg.annotation 6 | * 2018/6/6. 7 | * 8 | * @author zhanghaishan 9 | * @version V1.0 10 | */ 11 | public class FFAnnotation{ 12 | 13 | /** 14 | * 注解的key 15 | */ 16 | private String key; 17 | 18 | /** 19 | * 注解的值 20 | */ 21 | private String value; 22 | 23 | public String getKey() { 24 | return key; 25 | } 26 | 27 | public void setKey(String key) { 28 | this.key = key; 29 | } 30 | 31 | public String getValue() { 32 | return value; 33 | } 34 | 35 | public void setValue(String value) { 36 | this.value = value; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/annotation/FFCmd.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 命令注解,用于注解类 7 | * com.ugirls.ffmepg.annotation 8 | * 2018/6/6. 9 | * 10 | * @author zhanghaishan 11 | * @version V1.0 12 | */ 13 | @Target({ElementType.FIELD,ElementType.TYPE}) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Documented 16 | public @interface FFCmd { 17 | 18 | /** 19 | * 命令的key 20 | * @return 命令Key 21 | */ 22 | public String key() default ""; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/annotation/FFCmdBean.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.annotation; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 命令的父类 7 | * com.ugirls.ffmepg.annotation 8 | * 2018/6/6. 9 | * 10 | * @author zhanghaishan 11 | * @version V1.0 12 | */ 13 | public class FFCmdBean { 14 | 15 | private FFAnnotation cmdName; 16 | 17 | private List cmdParameter; 18 | 19 | public FFAnnotation getCmdName() { 20 | return cmdName; 21 | } 22 | 23 | public void setCmdName(FFAnnotation cmdName) { 24 | this.cmdName = cmdName; 25 | } 26 | 27 | public List getCmdParameter() { 28 | return cmdParameter; 29 | } 30 | 31 | public void setCmdParameter(List cmdParameter) { 32 | this.cmdParameter = cmdParameter; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/excption/FFMpegOperationConvertExcption.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.excption; 2 | 3 | /** 4 | * 操作转换异常 5 | * org.mountcloud.ffmepg.excption 6 | * 2018/11/29. 7 | * 8 | * @author zhanghaishan 9 | * @version V1.0 10 | */ 11 | public class FFMpegOperationConvertExcption extends RuntimeException{ 12 | 13 | public FFMpegOperationConvertExcption(String ex){ 14 | super(ex); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/excption/FFMpegOperationNotFoundExcption.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.excption; 2 | 3 | /** 4 | * 未找到异常 5 | * org.mountcloud.ffmepg.excption 6 | * 2018/11/29. 7 | * 8 | * @author zhanghaishan 9 | * @version V1.0 10 | */ 11 | public class FFMpegOperationNotFoundExcption extends RuntimeException { 12 | public FFMpegOperationNotFoundExcption(String ex){ 13 | super(ex); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/operation/FFOperationBase.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.operation; 2 | 3 | 4 | import org.mountcloud.ffmepg.annotation.FFAnnotation; 5 | import org.mountcloud.ffmepg.annotation.FFCmdBean; 6 | import org.mountcloud.ffmepg.excption.FFMpegOperationConvertExcption; 7 | import org.mountcloud.ffmepg.util.FFAnnotationUtil; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * TODO: 14 | * com.ugirls.ffmepg.operation 15 | * 2018/6/6. 16 | * 17 | * @author zhanghaishan 18 | * @version V1.0 19 | */ 20 | public abstract class FFOperationBase { 21 | 22 | private String stringvalue = null; 23 | private String command = null; 24 | private List commandParams = new ArrayList<>(); 25 | 26 | /** 27 | * 返回命令 28 | * @return 命令 29 | */ 30 | public String getCommand(){ 31 | if(command == null){ 32 | command = toString(); 33 | } 34 | return command; 35 | } 36 | 37 | /** 38 | * 返回命令参数 39 | * @return 参数 40 | */ 41 | public List getCommandParams(){ 42 | if(commandParams.size()==0){ 43 | toString(); 44 | } 45 | return commandParams; 46 | } 47 | 48 | 49 | /** 50 | * 直接转成命令 51 | * @return 命令 52 | */ 53 | @Override 54 | public String toString() { 55 | 56 | String str = null; 57 | FFAnnotationUtil ffAnnotationUtil = new FFAnnotationUtil(); 58 | try { 59 | FFCmdBean cmdBean = ffAnnotationUtil.getClassAnnocation(this); 60 | String execname = command = cmdBean.getCmdName().getKey(); 61 | List list = cmdBean.getCmdParameter(); 62 | 63 | str = execname; 64 | 65 | for(int i=0;i0&&aValue!=null){ 71 | str = str +" "+aKey; 72 | commandParams.add(aKey); 73 | } 74 | 75 | if(aValue!=null&&aValue.length()>0){ 76 | str = str +" "+aValue; 77 | commandParams.add(aValue); 78 | } 79 | } 80 | 81 | } catch (IllegalAccessException e) { 82 | str = null; 83 | } 84 | if(str==null){ 85 | throw new FFMpegOperationConvertExcption("FFMpegOperation To String Is Null!"); 86 | } 87 | 88 | stringvalue = str; 89 | 90 | return stringvalue; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/operation/ffmpeg/FFMpegOperationBase.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.operation.ffmpeg; 2 | 3 | import org.mountcloud.ffmepg.annotation.FFCmd; 4 | import org.mountcloud.ffmepg.operation.FFOperationBase; 5 | 6 | /** 7 | * TODO: ffmepg命令处理抽象父类 8 | * com.ugirls.ffmepg.operation.ffmpeg 9 | * 2018/6/6. 10 | * 11 | * @author zhanghaishan 12 | * @version V1.0 13 | */ 14 | @FFCmd(key = "ffmpeg") 15 | public abstract class FFMpegOperationBase extends FFOperationBase { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/operation/ffmpeg/vidoe/FFMpegVideoFormatM3u8.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.operation.ffmpeg.vidoe; 2 | 3 | 4 | import org.mountcloud.ffmepg.annotation.FFCmd; 5 | import org.mountcloud.ffmepg.operation.ffmpeg.FFMpegOperationBase; 6 | 7 | /** 8 | * 视频转码操作 com.ugirls.ffmepg.operation.ffmpeg.vidoe 2018/6/6. 9 | * 10 | * @author zhanghaishan 11 | * @version V1.0 12 | */ 13 | public class FFMpegVideoFormatM3u8 extends FFMpegOperationBase { 14 | 15 | /** 16 | * type 17 | */ 18 | @FFCmd(key = "-re") 19 | private String type = ""; 20 | 21 | /** 22 | * 视频文件 23 | */ 24 | @FFCmd(key = "-i") 25 | private String videoFileName; 26 | 27 | /** 28 | * vcodec 29 | */ 30 | @FFCmd(key = "-vcodec") 31 | private String vcodec = "libx264"; 32 | 33 | /** 34 | * acodec 35 | */ 36 | @FFCmd(key = "-acodec") 37 | private String acodec = "copy"; 38 | 39 | /** 40 | * 分辨率大小 1366x768 41 | */ 42 | @FFCmd(key = "-s") 43 | private String size; 44 | 45 | /** 46 | * 比特率 47 | */ 48 | @FFCmd(key = "-b:v") 49 | private String bitrate; 50 | 51 | @FFCmd(key = "-force_key_frames") 52 | private String forceKeyFrames; 53 | 54 | @FFCmd(key = "-f") 55 | private String formate = "segment"; 56 | 57 | @FFCmd(key = "-segment_list_type") 58 | private String listType = "m3u8"; 59 | 60 | @FFCmd(key = "-segment_list_size") 61 | private String listSize = "0"; 62 | 63 | @FFCmd(key = "-segment_time") 64 | private String time; 65 | 66 | @FFCmd(key = "-segment_time_delta") 67 | private String timeDelta = "0.1"; 68 | 69 | @FFCmd(key = "-segment_list") 70 | private String m3u8File; 71 | 72 | @FFCmd 73 | private String tsFiles; 74 | 75 | public void setTimes(Integer time) { 76 | //this.forceKeyFrames = "\"expr:gte(t,n_forced*" + time + ")\""; 77 | this.time = String.valueOf(Double.parseDouble(time.toString())); 78 | } 79 | 80 | public String getType() { 81 | return type; 82 | } 83 | 84 | public void setType(String type) { 85 | this.type = type; 86 | } 87 | 88 | public String getVideoFileName() { 89 | return videoFileName; 90 | } 91 | 92 | public void setVideoFileName(String videoFileName) { 93 | this.videoFileName = videoFileName; 94 | } 95 | 96 | public String getVcodec() { 97 | return vcodec; 98 | } 99 | 100 | public void setVcodec(String vcodec) { 101 | this.vcodec = vcodec; 102 | } 103 | 104 | public String getAcodec() { 105 | return acodec; 106 | } 107 | 108 | public void setAcodec(String acodec) { 109 | this.acodec = acodec; 110 | } 111 | 112 | public String getSize() { 113 | return size; 114 | } 115 | 116 | public void setSize(String size) { 117 | this.size = size; 118 | } 119 | 120 | public String getBitrate() { 121 | return bitrate; 122 | } 123 | 124 | public void setBitrate(String bitrate) { 125 | this.bitrate = bitrate; 126 | } 127 | 128 | public String getForceKeyFrames() { 129 | return forceKeyFrames; 130 | } 131 | 132 | public void setForceKeyFrames(String forceKeyFrames) { 133 | this.forceKeyFrames = forceKeyFrames; 134 | } 135 | 136 | public String getFormate() { 137 | return formate; 138 | } 139 | 140 | public void setFormate(String formate) { 141 | this.formate = formate; 142 | } 143 | 144 | public String getListType() { 145 | return listType; 146 | } 147 | 148 | public void setListType(String listType) { 149 | this.listType = listType; 150 | } 151 | 152 | public String getListSize() { 153 | return listSize; 154 | } 155 | 156 | public void setListSize(String listSize) { 157 | this.listSize = listSize; 158 | } 159 | 160 | public String getTime() { 161 | return time; 162 | } 163 | 164 | public void setTime(String time) { 165 | this.time = time; 166 | } 167 | 168 | public String getTimeDelta() { 169 | return timeDelta; 170 | } 171 | 172 | public void setTimeDelta(String timeDelta) { 173 | this.timeDelta = timeDelta; 174 | } 175 | 176 | public String getM3u8File() { 177 | return m3u8File; 178 | } 179 | 180 | public void setM3u8File(String m3u8File) { 181 | this.m3u8File = m3u8File; 182 | } 183 | 184 | public String getTsFiles() { 185 | return tsFiles; 186 | } 187 | 188 | public void setTsFiles(String tsFiles) { 189 | this.tsFiles = tsFiles; 190 | } 191 | } -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/operation/ffmpeg/vidoe/FFMpegVideoInfo.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.operation.ffmpeg.vidoe; 2 | 3 | import org.mountcloud.ffmepg.annotation.FFCmd; 4 | import org.mountcloud.ffmepg.operation.ffmpeg.FFMpegOperationBase; 5 | 6 | /** 7 | * FFmpeg操作父类 8 | * com.ugirls.ffmepg.operation.ffmpeg.vidoe 9 | * 2018/6/11. 10 | * 11 | * @author zhanghaishan 12 | * @version V1.0 13 | */ 14 | public class FFMpegVideoInfo extends FFMpegOperationBase { 15 | 16 | @FFCmd(key = "-i") 17 | private String videoUrl; 18 | 19 | public String getVideoUrl() { 20 | return videoUrl; 21 | } 22 | 23 | public void setVideoUrl(String videoUrl) { 24 | this.videoUrl = videoUrl; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/operation/ffmpeg/vidoe/FFMpegVideoScreenShot.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.operation.ffmpeg.vidoe; 2 | 3 | 4 | import org.mountcloud.ffmepg.annotation.FFCmd; 5 | import org.mountcloud.ffmepg.operation.ffmpeg.FFMpegOperationBase; 6 | 7 | /** 8 | * 视频截图操作 9 | * com.ugirls.ffmepg.operation.ffmpeg.vidoe 10 | * 2018/6/6. 11 | * 12 | * @author zhanghaishan 13 | * @version V1.0 14 | */ 15 | public class FFMpegVideoScreenShot extends FFMpegOperationBase { 16 | 17 | /** 18 | * 视频截图操作 19 | * @param video 视频地址 20 | * @param startTime 截图开始时间 21 | * @param vframes 帧数 22 | * @param image 输出文件路径 23 | */ 24 | public FFMpegVideoScreenShot(String video,String startTime, String vframes, String image) { 25 | this.video = video; 26 | this.startTime = startTime; 27 | this.vframes = vframes; 28 | this.image = image; 29 | } 30 | 31 | /** 32 | * 输入的视频文件 33 | */ 34 | @FFCmd(key = "-i") 35 | private String video; 36 | 37 | /** 38 | * 输出的图片是否覆盖形式生成,如果为空则不添加该key 39 | */ 40 | @FFCmd(key = "-y") 41 | private String isOverWrite = ""; 42 | 43 | /** 44 | * 图片输出的格式 默认 image2 45 | */ 46 | @FFCmd(key = "-f") 47 | private String imageFormat = "image2"; 48 | 49 | /** 50 | * 开始时间秒 51 | */ 52 | @FFCmd(key = "-ss") 53 | private String startTime; 54 | 55 | /** 56 | * 时间 57 | */ 58 | @FFCmd(key = "-t") 59 | private String time; 60 | 61 | /** 62 | * 帧数 63 | */ 64 | @FFCmd(key = "-vframes") 65 | private String vframes; 66 | 67 | /** 68 | * 截图的分辨率大小 例如:1020x960 69 | */ 70 | @FFCmd(key = "-s") 71 | private String size; 72 | 73 | /** 74 | * 输出文件 75 | */ 76 | @FFCmd 77 | private String image; 78 | 79 | public String getVideo() { 80 | return video; 81 | } 82 | 83 | public void setVideo(String video) { 84 | this.video = video; 85 | } 86 | 87 | public String getIsOverWrite() { 88 | return isOverWrite; 89 | } 90 | 91 | public void setIsOverWrite(String isOverWrite) { 92 | this.isOverWrite = isOverWrite; 93 | } 94 | 95 | public String getImageFormat() { 96 | return imageFormat; 97 | } 98 | 99 | public void setImageFormat(String imageFormat) { 100 | this.imageFormat = imageFormat; 101 | } 102 | 103 | public String getTime() { 104 | return time; 105 | } 106 | 107 | public void setTime(String time) { 108 | this.time = time; 109 | } 110 | 111 | public String getVframes() { 112 | return vframes; 113 | } 114 | 115 | public void setVframes(String vframes) { 116 | this.vframes = vframes; 117 | } 118 | 119 | public String getSize() { 120 | return size; 121 | } 122 | 123 | public void setSize(String size) { 124 | this.size = size; 125 | } 126 | 127 | public String getImage() { 128 | return image; 129 | } 130 | 131 | public void setImage(String image) { 132 | this.image = image; 133 | } 134 | 135 | public String getStartTime() { 136 | return startTime; 137 | } 138 | 139 | public void setStartTime(String startTime) { 140 | this.startTime = startTime; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/result/FFResult.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.result; 2 | 3 | /** 4 | * FFMpeg结果 5 | * com.ugirls.ffmepg.result 6 | * 2018/6/7. 7 | * 8 | * @author zhanghaishan 9 | * @version V1.0 10 | */ 11 | public abstract class FFResult { 12 | 13 | /** 14 | * 结果的字符串 15 | */ 16 | private String resultString; 17 | 18 | /** 19 | * 异常字符串 20 | */ 21 | private String error; 22 | 23 | public FFResult(String resultStr){ 24 | this.resultString = resultStr; 25 | } 26 | 27 | public String getResultString() { 28 | return resultString; 29 | } 30 | 31 | public void setResultString(String resultString) { 32 | this.resultString = resultString; 33 | } 34 | 35 | public String getError() { 36 | return error; 37 | } 38 | 39 | public void setError(String error) { 40 | this.error = error; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/result/defaultResult/FFDefaultResult.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.result.defaultResult; 2 | 3 | import org.mountcloud.ffmepg.result.FFResult; 4 | 5 | /** 6 | * FFMpeg 默认的结果 7 | * com.ugirls.ffmepg.result 8 | * 2018/6/7. 9 | * 10 | * @author zhanghaishan 11 | * @version V1.0 12 | */ 13 | public class FFDefaultResult extends FFResult { 14 | public FFDefaultResult(String resultStr) { 15 | super(resultStr); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/result/defaultResult/FFVideoInfoResult.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.result.defaultResult; 2 | 3 | /** 4 | * 视频信息结果 5 | * com.ugirls.ffmepg.result.defaultResult 6 | * 2018/6/12. 7 | * 8 | * @author zhanghaishan 9 | * @version V1.0 10 | */ 11 | public class FFVideoInfoResult extends FFDefaultResult{ 12 | 13 | 14 | /** 15 | * 秒数 16 | */ 17 | private Integer timeLengthSec; 18 | 19 | /** 20 | * 时长 21 | */ 22 | private String timeLength; 23 | 24 | /** 25 | * 开始时间 26 | */ 27 | private String startTime; 28 | 29 | /** 30 | * 比特率 31 | */ 32 | private String bitrate; 33 | 34 | 35 | /** 36 | * 宽度 37 | */ 38 | private String width; 39 | 40 | /** 41 | * 高度 42 | */ 43 | private String height; 44 | 45 | /** 46 | * fps 47 | */ 48 | private String fps; 49 | 50 | /** 51 | * tbr 52 | */ 53 | private String tbr; 54 | 55 | /** 56 | * tbn 57 | */ 58 | private String tbn; 59 | 60 | /** 61 | * tbc 62 | */ 63 | private String tbc; 64 | 65 | public FFVideoInfoResult() { 66 | super(""); 67 | } 68 | 69 | public FFVideoInfoResult(String resultStr) { 70 | super(resultStr); 71 | } 72 | 73 | public Integer getTimeLengthSec() { 74 | return timeLengthSec; 75 | } 76 | 77 | public void setTimeLengthSec(Integer timeLengthSec) { 78 | this.timeLengthSec = timeLengthSec; 79 | } 80 | 81 | public String getTimeLength() { 82 | return timeLength; 83 | } 84 | 85 | public void setTimeLength(String timeLength) { 86 | this.timeLength = timeLength; 87 | } 88 | 89 | public String getStartTime() { 90 | return startTime; 91 | } 92 | 93 | public void setStartTime(String startTime) { 94 | this.startTime = startTime; 95 | } 96 | 97 | public String getBitrate() { 98 | return bitrate; 99 | } 100 | 101 | public void setBitrate(String bitrate) { 102 | this.bitrate = bitrate; 103 | } 104 | 105 | public String getWidth() { 106 | return width; 107 | } 108 | 109 | public void setWidth(String width) { 110 | this.width = width; 111 | } 112 | 113 | public String getHeight() { 114 | return height; 115 | } 116 | 117 | public void setHeight(String height) { 118 | this.height = height; 119 | } 120 | 121 | public String getFps() { 122 | return fps; 123 | } 124 | 125 | public void setFps(String fps) { 126 | this.fps = fps; 127 | } 128 | 129 | public String getTbr() { 130 | return tbr; 131 | } 132 | 133 | public void setTbr(String tbr) { 134 | this.tbr = tbr; 135 | } 136 | 137 | public String getTbn() { 138 | return tbn; 139 | } 140 | 141 | public void setTbn(String tbn) { 142 | this.tbn = tbn; 143 | } 144 | 145 | public String getTbc() { 146 | return tbc; 147 | } 148 | 149 | public void setTbc(String tbc) { 150 | this.tbc = tbc; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/bean/FFTask.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.bean; 2 | 3 | import org.mountcloud.ffmepg.operation.FFOperationBase; 4 | import org.mountcloud.ffmepg.task.bean.read.FFTaskReadState; 5 | import org.mountcloud.ffmepg.task.bean.read.FFTaskReadThread; 6 | import org.mountcloud.ffmepg.task.bean.read.FFTaskReadType; 7 | import org.mountcloud.ffmepg.task.context.FFTaskContext; 8 | import org.mountcloud.ffmepg.task.threads.FFThread; 9 | import org.mountcloud.ffmepg.util.FFTerminalCreater; 10 | import org.mountcloud.ffmepg.util.UUIDUtil; 11 | 12 | import java.io.IOException; 13 | import java.util.*; 14 | 15 | /** 16 | * 任务父类 17 | * com.ugirls.ffmepg.task 18 | * 2018/6/11. 19 | * 20 | * @author zhanghaishan 21 | * @version V1.0 22 | */ 23 | public abstract class FFTask implements FFThread { 24 | 25 | /** 26 | * 任务id 27 | */ 28 | private String taskId; 29 | 30 | /** 31 | * 执行结果的全部内容 32 | */ 33 | protected StringBuffer result = new StringBuffer(""); 34 | 35 | /** 36 | * 内容操作 37 | */ 38 | protected T operationBase; 39 | 40 | /** 41 | * 任务进度 42 | */ 43 | private FFTaskProgress progress; 44 | 45 | /** 46 | * 任务命令行 47 | */ 48 | protected FFTerminalCreater.FFTerminal terminal = null; 49 | 50 | /** 51 | * 任务创建时间 52 | */ 53 | private Date createTime; 54 | 55 | /** 56 | * 读取任务的执行是否完成 57 | */ 58 | private Map readState = new HashMap<>(); 59 | 60 | /** 61 | * 读取线程 62 | */ 63 | protected List ffTaskReadThreadList = new ArrayList<>(); 64 | 65 | @Override 66 | public void run() { 67 | 68 | //执行开始前回调 69 | callExecStart(); 70 | 71 | //任务开始 72 | progress.setState(FFTaskStateEnum.START); 73 | 74 | //执行的命令 75 | String cmd = operationBase.toString(); 76 | 77 | //任务执行状态 78 | boolean state = true; 79 | 80 | try { 81 | terminal = FFTerminalCreater.getCreater().getTerminal(cmd); 82 | //初始化读取线程 83 | initReadThread(); 84 | 85 | //等待执行完毕 86 | while (!this.checkReadIsEnd()){ 87 | Thread.sleep(500); 88 | } 89 | 90 | //执行完毕则获取执行状态 91 | state = this.checkReadState(); 92 | 93 | } catch (IOException e) { 94 | state =false; 95 | e.printStackTrace(); 96 | } catch (InterruptedException e) { 97 | state =false; 98 | e.printStackTrace(); 99 | } 100 | 101 | //设置状态 102 | if(state){ 103 | progress.setState(FFTaskStateEnum.COMPLETE); 104 | } else { 105 | progress.setState(FFTaskStateEnum.FAILED); 106 | } 107 | progress.setProgress(100); 108 | FFTaskContext.getContext().removeTask(this.getTaskId()); 109 | //执行结束回调 110 | callExecEnd(); 111 | } 112 | 113 | /** 114 | * 判断是否在读取中 115 | * @return false 读取中,true 读取完成 116 | */ 117 | private boolean checkReadIsEnd(){ 118 | for(FFTaskReadState state : readState.values()){ 119 | if(!state.isEnd()){ 120 | return false; 121 | } 122 | } 123 | return true; 124 | } 125 | 126 | /** 127 | * 判断读取状态 128 | * @return false 读取报错,true 读取成功 129 | */ 130 | private boolean checkReadState(){ 131 | for(FFTaskReadState state : readState.values()){ 132 | if(!state.getState()){ 133 | return false; 134 | } 135 | } 136 | return true; 137 | } 138 | 139 | /** 140 | * 初始化读取线程 141 | */ 142 | private void initReadThread(){ 143 | 144 | //创建读取线程,读取error和in 145 | FFTaskReadThread ffTaskReadErrorThread = new FFTaskReadThread(this, FFTaskReadType.ERROR_IN); 146 | FFTaskReadThread ffTaskReadThread = new FFTaskReadThread(this, FFTaskReadType.IN); 147 | readState.put(ffTaskReadErrorThread.getReadType(),new FFTaskReadState()); 148 | readState.put(ffTaskReadThread.getReadType(),new FFTaskReadState()); 149 | 150 | //加入线程数组,如果以后需要可以扩展 151 | ffTaskReadThreadList.add(ffTaskReadErrorThread); 152 | ffTaskReadThreadList.add(ffTaskReadThread); 153 | 154 | //开启这两个读取线程,此处不优雅,如果有更好的方案请告诉我 155 | new Thread(ffTaskReadErrorThread).start(); 156 | new Thread(ffTaskReadThread).start(); 157 | } 158 | 159 | 160 | /** 161 | * 传入一行结果 162 | * @param line 一行日志 163 | */ 164 | public synchronized void putResultLine(String line){ 165 | result.append(line); 166 | callRsultLine(line); 167 | } 168 | 169 | 170 | /** 171 | * 执行开始 172 | */ 173 | public abstract void callExecStart(); 174 | 175 | /** 176 | * 正确结果行 177 | * @param line 一行结果 178 | */ 179 | public abstract void callRsultLine(String line); 180 | 181 | /** 182 | * 执行结束 183 | */ 184 | public abstract void callExecEnd(); 185 | 186 | 187 | /** 188 | * 任务构造 189 | * @param operation 操作 190 | */ 191 | public FFTask(T operation){ 192 | this.operationBase = operation; 193 | this.taskId = UUIDUtil.getUUIDSimpl(); 194 | this.createTime = new Date(); 195 | this.progress = new FFTaskProgress(); 196 | } 197 | 198 | public FFTask(){ 199 | this.progress = new FFTaskProgress(); 200 | } 201 | 202 | public String getTaskId() { 203 | return taskId; 204 | } 205 | 206 | public FFTaskProgress getProgress() { 207 | return progress; 208 | } 209 | 210 | public void setProgress(FFTaskProgress progress) { 211 | this.progress = progress; 212 | } 213 | 214 | public FFTerminalCreater.FFTerminal getTerminal(){ 215 | return terminal; 216 | } 217 | 218 | public FFTaskReadState getReadState(FFTaskReadType ffTaskReadType){ 219 | return this.readState.get(ffTaskReadType); 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/bean/FFTaskProgress.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.bean; 2 | 3 | /** 4 | * 任务状态 5 | * com.ugirls.ffmepg.task 6 | * 2018/6/11. 7 | * 8 | * @author zhanghaishan 9 | * @version V1.0 10 | */ 11 | public class FFTaskProgress { 12 | 13 | public FFTaskProgress(){ 14 | this.progress = 0; 15 | this.state = FFTaskStateEnum.QUEUE; 16 | } 17 | 18 | private int progress; 19 | 20 | private FFTaskStateEnum state; 21 | 22 | public FFTaskStateEnum getState() { 23 | return state; 24 | } 25 | 26 | public void setState(FFTaskStateEnum state) { 27 | this.state = state; 28 | } 29 | 30 | public int getProgress() { 31 | return progress; 32 | } 33 | 34 | public void setProgress(int progress) { 35 | this.progress = progress; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/bean/FFTaskStateEnum.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.bean; 2 | 3 | /** 4 | * 任务状态枚举 5 | * com.ugirls.ffmepg.task.bean 6 | * 2018/6/11. 7 | * 8 | * @author zhanghaishan 9 | * @version V1.0 10 | */ 11 | public enum FFTaskStateEnum { 12 | 13 | /** 14 | * 队列 15 | */ 16 | QUEUE(2), 17 | /** 18 | * 进行中 19 | */ 20 | START(1), 21 | /** 22 | * 已完成 23 | */ 24 | COMPLETE(0), 25 | /** 26 | * 失败 27 | */ 28 | FAILED(-1); 29 | 30 | private Integer val = null; 31 | 32 | FFTaskStateEnum(int i) { 33 | this.val = i; 34 | } 35 | 36 | /** 37 | * 获取枚举 38 | * @param i 值 39 | * @return 枚举 40 | */ 41 | public static FFTaskStateEnum getEnum(int i){ 42 | switch (i){ 43 | case 2: 44 | return QUEUE; 45 | case 1: 46 | return START; 47 | case 0: 48 | return COMPLETE; 49 | } 50 | return FAILED; 51 | } 52 | 53 | /** 54 | * 返回值 55 | * @return 值 56 | */ 57 | public int getValue(){ 58 | return val; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/bean/FFVideoTask.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.bean; 2 | 3 | 4 | import org.mountcloud.ffmepg.operation.FFOperationBase; 5 | import org.mountcloud.ffmepg.util.FFVideoUtil; 6 | import org.mountcloud.ffmepg.util.StringUtil; 7 | 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * 视频操作任务父类 13 | * com.ugirls.ffmepg.task.bean 14 | * 2018/6/11. 15 | * 16 | * @author zhanghaishan 17 | * @version V1.0 18 | */ 19 | public abstract class FFVideoTask extends FFTask{ 20 | 21 | /** 22 | * 秒数 23 | */ 24 | private Integer timeLengthSec; 25 | 26 | /** 27 | * 时长 28 | */ 29 | private String timeLength; 30 | 31 | /** 32 | * 开始时间 33 | */ 34 | private String startTime; 35 | 36 | /** 37 | * 比特率 38 | */ 39 | private String bitrate; 40 | 41 | /** 42 | * 宽度 43 | */ 44 | private String width; 45 | 46 | /** 47 | * 高度 48 | */ 49 | private String height; 50 | 51 | /** 52 | * fps 53 | */ 54 | private String fps; 55 | 56 | /** 57 | * tbr 58 | */ 59 | private String tbr; 60 | 61 | /** 62 | * tbn 63 | */ 64 | private String tbn; 65 | 66 | /** 67 | * tbc 68 | */ 69 | private String tbc; 70 | 71 | /** 72 | * 正则 73 | */ 74 | private String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*)"; 75 | 76 | 77 | /** 78 | * 尺寸 79 | */ 80 | private String sizeDuration = "Stream (.*?): Video: (.*?) \\d*x\\d*"; 81 | 82 | /** 83 | * 正则模式 84 | */ 85 | private Pattern pattern = Pattern.compile(regexDuration); 86 | 87 | /** 88 | * 正则模式 89 | */ 90 | private Pattern sizePattern = Pattern.compile(sizeDuration); 91 | 92 | 93 | public FFVideoTask(T operation){ 94 | super(operation); 95 | } 96 | 97 | /** 98 | * 设置结果 99 | * @param line 一行结果 100 | */ 101 | @Override 102 | public void callRsultLine(String line) { 103 | 104 | //获取视频时长信息 105 | //Duration: 00:03:38.80, start: 1.427433, bitrate: 0 kb/s 106 | Matcher m = pattern.matcher(line.trim()); 107 | if (m.find()) { 108 | timeLength = m.group(1); 109 | if(timeLength!=null){ 110 | timeLengthSec = FFVideoUtil.getTimelen(timeLength); 111 | } 112 | startTime = m.group(2); 113 | bitrate = m.group(3); 114 | } 115 | 116 | /** 117 | * 获取视频尺寸 118 | */ 119 | //Stream #0:0: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p, 1024x576 [SAR 1:1 DAR 16:9], 20 fps, 20 tbr, 90k tbn, 40 tbc 120 | Matcher sizem = sizePattern.matcher(line.trim()); 121 | if(sizem.find()){ 122 | findSize(line); 123 | } 124 | 125 | 126 | callBackResultLine(line); 127 | } 128 | 129 | /** 130 | * 子类需要返回的 131 | * @param line 一行结果 132 | */ 133 | public abstract void callBackResultLine(String line); 134 | 135 | public String getTimeLength() { 136 | return timeLength; 137 | } 138 | 139 | public void setTimeLength(String timeLength) { 140 | this.timeLength = timeLength; 141 | } 142 | 143 | public String getStartTime() { 144 | return startTime; 145 | } 146 | 147 | public void setStartTime(String startTime) { 148 | this.startTime = startTime; 149 | } 150 | 151 | public String getBitrate() { 152 | return bitrate; 153 | } 154 | 155 | public void setBitrate(String bitrate) { 156 | this.bitrate = bitrate; 157 | } 158 | 159 | public Integer getTimeLengthSec() { 160 | return timeLengthSec; 161 | } 162 | 163 | public void setTimeLengthSec(Integer timeLengthSec) { 164 | this.timeLengthSec = timeLengthSec; 165 | } 166 | 167 | //格式:"00:00:10.68" 168 | protected int getTimelen(String timelen){ 169 | int min=0; 170 | String strs[] = timelen.split(":"); 171 | if (strs[0].compareTo("0") > 0) { 172 | min+=Integer.valueOf(strs[0])*60*60;//秒 173 | } 174 | if(strs[1].compareTo("0")>0){ 175 | min+=Integer.valueOf(strs[1])*60; 176 | } 177 | if(strs[2].compareTo("0")>0){ 178 | min+=Math.round(Float.valueOf(strs[2])); 179 | } 180 | return min; 181 | } 182 | 183 | /** 184 | * 获取长度的信息 185 | * @param line 186 | */ 187 | private void findSize(String line){ 188 | 189 | String sizeRegs = ", \\d*x\\d*"; 190 | String sizeResul = StringUtil.findStringsByRegsOne(sizeRegs,line); 191 | sizeResul = sizeResul.replace(", ",""); 192 | String[] size = sizeResul.split("x"); 193 | String w = size[0]; 194 | String h = size[1]; 195 | setWidth(w); 196 | setHeight(h); 197 | 198 | String fpsRegs = "\\d* fps"; 199 | String fpsResul = StringUtil.findStringsByRegsOne(fpsRegs,line); 200 | if(fpsResul!=null){ 201 | fpsResul = fpsResul.replace(" fps",""); 202 | setFps(fpsResul); 203 | } 204 | 205 | String tbrRegs = "\\d* tbr"; 206 | String tbrResul = StringUtil.findStringsByRegsOne(tbrRegs,line); 207 | if(tbrResul!=null){ 208 | tbrResul = tbrResul.replace(" tbr",""); 209 | setTbr(tbrResul); 210 | } 211 | 212 | String tbnRegs = "[0-9a-z]* tbn"; 213 | String tbnResul = StringUtil.findStringsByRegsOne(tbnRegs,line); 214 | if(tbnResul!=null){ 215 | tbnResul = tbnResul.replace(" tbn",""); 216 | setTbn(tbnResul); 217 | } 218 | 219 | String tbcRegs = "\\d* tbc"; 220 | String tbcResul = StringUtil.findStringsByRegsOne(tbcRegs,line); 221 | if(tbcResul!=null){ 222 | tbcResul = tbcResul.replace(" tbc",""); 223 | setTbc(tbcResul); 224 | } 225 | 226 | } 227 | 228 | public String getWidth() { 229 | return width; 230 | } 231 | 232 | public void setWidth(String width) { 233 | this.width = width; 234 | } 235 | 236 | public String getHeight() { 237 | return height; 238 | } 239 | 240 | public void setHeight(String height) { 241 | this.height = height; 242 | } 243 | 244 | public String getFps() { 245 | return fps; 246 | } 247 | 248 | public void setFps(String fps) { 249 | this.fps = fps; 250 | } 251 | 252 | public String getTbr() { 253 | return tbr; 254 | } 255 | 256 | public void setTbr(String tbr) { 257 | this.tbr = tbr; 258 | } 259 | 260 | public String getTbn() { 261 | return tbn; 262 | } 263 | 264 | public void setTbn(String tbn) { 265 | this.tbn = tbn; 266 | } 267 | 268 | public String getTbc() { 269 | return tbc; 270 | } 271 | 272 | public void setTbc(String tbc) { 273 | this.tbc = tbc; 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/bean/read/FFTaskReadState.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.bean.read; 2 | 3 | /** 4 | * 任务读取状态 5 | * com.ugirls.ffmepg.task.bean.read 6 | * 2020/6/16. 7 | * 8 | * @author zhanghaishan 9 | * @version V1.0 10 | */ 11 | public class FFTaskReadState { 12 | 13 | /** 14 | * 状态,true是成功,false是失败 15 | */ 16 | private Boolean state = false; 17 | 18 | /** 19 | * 是否结束,true是结束,false是进行中 20 | */ 21 | private Boolean end = false; 22 | 23 | public Boolean getState() { 24 | return state; 25 | } 26 | 27 | public void setState(Boolean state) { 28 | this.state = state; 29 | } 30 | 31 | public Boolean isEnd() { 32 | return end; 33 | } 34 | 35 | public void setEnd(Boolean end) { 36 | this.end = end; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/bean/read/FFTaskReadThread.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.bean.read; 2 | 3 | import org.mountcloud.ffmepg.task.bean.FFTask; 4 | import org.mountcloud.ffmepg.task.threads.FFThread; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * 任务读取异常线程 10 | * com.ugirls.ffmepg.task.bean.read 11 | * 2020/6/16. 12 | * 13 | * @author zhanghaishan 14 | * @version V1.0 15 | */ 16 | public class FFTaskReadThread implements FFThread { 17 | 18 | /** 19 | * 任务 20 | */ 21 | private FFTask ffTask; 22 | 23 | /** 24 | * 读取类型 25 | */ 26 | private FFTaskReadType readType; 27 | 28 | /** 29 | * 构造一个读取线程 30 | * @param ffTask 任务 31 | * @param readType 读取类型 32 | */ 33 | public FFTaskReadThread(FFTask ffTask,FFTaskReadType readType){ 34 | this.ffTask = ffTask; 35 | this.readType = readType; 36 | } 37 | 38 | @Override 39 | public void run() { 40 | //结果 41 | String str = null; 42 | boolean state = false; 43 | try { 44 | if(readType.equals(FFTaskReadType.ERROR_IN)){ 45 | while ((str=ffTask.getTerminal().readErrorLine())!=null){ 46 | ffTask.putResultLine(str); 47 | } 48 | }else{ 49 | while ((str=ffTask.getTerminal().readLine())!=null){ 50 | ffTask.putResultLine(str); 51 | } 52 | } 53 | state = true; 54 | }catch (IOException e){ 55 | state = false; 56 | e.printStackTrace(); 57 | } 58 | //结束了 59 | FFTaskReadState readState = ffTask.getReadState(this.getReadType()); 60 | readState.setEnd(true); 61 | readState.setState(state); 62 | } 63 | 64 | public FFTaskReadType getReadType(){ 65 | return this.readType; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/bean/read/FFTaskReadType.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.bean.read; 2 | 3 | /** 4 | * 任务读取异常线程 5 | * com.ugirls.ffmepg.task.bean.read 6 | * 2020/6/16. 7 | * 8 | * @author zhanghaishan 9 | * @version V1.0 10 | */ 11 | public enum FFTaskReadType{ 12 | ERROR_IN,//读取异常流 13 | IN//读取流 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/bean/tasks/FFMepgVideoFormatM3u8Task.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.bean.tasks; 2 | 3 | import org.mountcloud.ffmepg.operation.ffmpeg.vidoe.FFMpegVideoFormatM3u8; 4 | import org.mountcloud.ffmepg.task.bean.FFVideoTask; 5 | import org.mountcloud.ffmepg.util.FFBigDecimalUtil; 6 | import org.mountcloud.ffmepg.util.FFVideoUtil; 7 | 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * 视频转m3u8任务 13 | * com.ugirls.ffmepg.task.bean.tasks 14 | * 2018/6/11. 15 | * 16 | * @author zhanghaishan 17 | * @version V1.0 18 | */ 19 | public class FFMepgVideoFormatM3u8Task extends FFVideoTask { 20 | 21 | /** 22 | * 进度正则查询 23 | */ 24 | private String frameRegexDuration = "frame=([\\s,\\d]*) fps=(.*?) q=(.*?) size=([\\s\\S]*) time=(.*?) bitrate=([\\s\\S]*) speed=(.*?)x"; 25 | 26 | /** 27 | * 正则模式 28 | */ 29 | private Pattern framePattern = Pattern.compile(frameRegexDuration); 30 | 31 | @Override 32 | public void callExecStart() { 33 | } 34 | 35 | @Override 36 | public void callExecEnd() { 37 | } 38 | 39 | /** 40 | * 任务构造 41 | * @param format 操作 42 | */ 43 | public FFMepgVideoFormatM3u8Task(FFMpegVideoFormatM3u8 format){ 44 | super(format); 45 | } 46 | 47 | /** 48 | * 回调 49 | * @param line 一行结果 50 | */ 51 | @Override 52 | public void callBackResultLine(String line) { 53 | 54 | if(super.getTimeLengthSec()!=null){ 55 | //获取视频信息 56 | Matcher m = framePattern.matcher(line); 57 | if(m.find()){ 58 | try { 59 | String execTimeStr = m.group(5); 60 | int execTimeInt = FFVideoUtil.getTimelen(execTimeStr); 61 | double devnum = FFBigDecimalUtil.div(execTimeInt,super.getTimeLengthSec(),5); 62 | double progressDouble = FFBigDecimalUtil.mul(devnum,100); 63 | super.getProgress().setProgress((int) progressDouble); 64 | } catch (IllegalAccessException e) { 65 | e.printStackTrace(); 66 | } 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/bean/tasks/FFMepgVideoInfoTask.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.bean.tasks; 2 | 3 | 4 | import org.mountcloud.ffmepg.operation.ffmpeg.vidoe.FFMpegVideoInfo; 5 | import org.mountcloud.ffmepg.result.defaultResult.FFVideoInfoResult; 6 | import org.mountcloud.ffmepg.task.bean.FFVideoTask; 7 | 8 | /** 9 | * 视频信息查询任务 10 | * com.ugirls.ffmepg.task.bean.tasks 11 | * 2018/6/12. 12 | * 13 | * @author zhanghaishan 14 | * @version V1.0 15 | */ 16 | public class FFMepgVideoInfoTask extends FFVideoTask { 17 | 18 | private FFVideoInfoResult data; 19 | 20 | public FFMepgVideoInfoTask(FFMpegVideoInfo operation) { 21 | super(operation); 22 | } 23 | 24 | public FFMepgVideoInfoTask(FFVideoInfoResult result,FFMpegVideoInfo operation) { 25 | super(operation); 26 | this.data = result; 27 | } 28 | 29 | @Override 30 | public void callExecStart() { 31 | 32 | } 33 | 34 | @Override 35 | public void callBackResultLine(String line) { 36 | 37 | } 38 | 39 | @Override 40 | public void callExecEnd() { 41 | if(data==null){ 42 | //返回结果 43 | data = new FFVideoInfoResult(result.toString()); 44 | } 45 | 46 | //结果 47 | data.setTimeLengthSec(getTimeLengthSec()); 48 | data.setTimeLength(getTimeLength()); 49 | data.setStartTime(getStartTime()); 50 | data.setBitrate(getBitrate()); 51 | data.setWidth(getWidth()); 52 | data.setHeight(getHeight()); 53 | data.setFps(getFps()); 54 | data.setTbr(getTbr()); 55 | data.setTbn(getTbn()); 56 | data.setTbc(getTbc()); 57 | } 58 | 59 | public FFVideoInfoResult getData() { 60 | return data; 61 | } 62 | 63 | public void setData(FFVideoInfoResult data) { 64 | this.data = data; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/bean/tasks/FFMpegVideoScreenShotTask.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.bean.tasks; 2 | 3 | import org.mountcloud.ffmepg.operation.ffmpeg.vidoe.FFMpegVideoScreenShot; 4 | import org.mountcloud.ffmepg.task.bean.FFVideoTask; 5 | 6 | /** 7 | * 视频截图任务 8 | * com.ugirls.ffmepg.task.bean.tasks 9 | * 2018/6/12. 10 | * 11 | * @author zhanghaishan 12 | * @version V1.0 13 | */ 14 | public class FFMpegVideoScreenShotTask extends FFVideoTask { 15 | 16 | public FFMpegVideoScreenShotTask(FFMpegVideoScreenShot operation) { 17 | super(operation); 18 | } 19 | 20 | @Override 21 | public void callBackResultLine(String line) { 22 | 23 | } 24 | 25 | @Override 26 | public void callExecStart() { 27 | 28 | } 29 | 30 | @Override 31 | public void callExecEnd() { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/context/FFTaskContext.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.context; 2 | 3 | import org.mountcloud.ffmepg.task.bean.FFTask; 4 | import org.mountcloud.ffmepg.task.threads.FFThreadManager; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.concurrent.locks.Lock; 9 | import java.util.concurrent.locks.ReentrantLock; 10 | 11 | /** 12 | * 任务上下文 13 | * com.ugirls.ffmepg.task 14 | * 2018/6/11. 15 | * 16 | * @author zhanghaishan 17 | * @version V1.0 18 | */ 19 | public class FFTaskContext { 20 | 21 | /** 22 | * 任务列表锁 23 | */ 24 | private Lock lock = new ReentrantLock(); 25 | 26 | /** 27 | * 上下文单例 28 | */ 29 | private static FFTaskContext context; 30 | 31 | /** 32 | * 任务线程池 33 | */ 34 | private FFThreadManager.ThreadPool ffThreadPool; 35 | 36 | /** 37 | * 任务列表 38 | */ 39 | private Map taskMap = new HashMap(); 40 | 41 | /** 42 | * 添加一个任务 43 | * @param task 任务 44 | */ 45 | public void addTask(FFTask task){ 46 | try{ 47 | lock.lock(); 48 | taskMap.put(task.getTaskId(),task); 49 | }finally { 50 | lock.unlock(); 51 | } 52 | ffThreadPool.execute(task); 53 | } 54 | 55 | /** 56 | * 提交一个任务 57 | * @param task 提交任务 58 | */ 59 | public void submit(FFTask task){ 60 | try{ 61 | lock.lock(); 62 | taskMap.put(task.getTaskId(),task); 63 | }finally { 64 | lock.unlock(); 65 | } 66 | 67 | try { 68 | ffThreadPool.submit(task); 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | /** 74 | * 提交一个任务 75 | * @param task 提交任务 76 | * @param bean 数据 77 | * @param 数据 78 | * @return 执行结果 79 | */ 80 | public T submit(FFTask task,T bean){ 81 | T data = null; 82 | 83 | try{ 84 | lock.lock(); 85 | taskMap.put(task.getTaskId(),task); 86 | }finally { 87 | lock.unlock(); 88 | } 89 | 90 | try { 91 | data = ffThreadPool.submit(task,bean); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | return data; 96 | } 97 | 98 | /** 99 | * 任务ID 100 | * @param taskId 任务id 101 | */ 102 | public void removeTask(String taskId){ 103 | try{ 104 | lock.lock(); 105 | taskMap.remove(taskId); 106 | }finally { 107 | lock.unlock(); 108 | } 109 | } 110 | 111 | /** 112 | * 提供上下文 113 | * @return 上下文 114 | */ 115 | public static FFTaskContext getContext(){ 116 | if(context == null){ 117 | context = new FFTaskContext(); 118 | } 119 | return context; 120 | } 121 | 122 | /** 123 | * 私密的上下文 124 | */ 125 | private FFTaskContext(){ 126 | ffThreadPool = FFThreadManager.getInstance(); 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/threads/FFThread.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.threads; 2 | 3 | /** 4 | * TODO: 线程对象 5 | * com.ugirls.ffmepg.task.threads 6 | * 2018/6/11. 7 | * 8 | * @author zhanghaishan 9 | * @version V1.0 10 | */ 11 | public interface FFThread extends Runnable { 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/task/threads/FFThreadManager.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.task.threads; 2 | 3 | import java.util.concurrent.*; 4 | 5 | /** 6 | * 任务的线程池 7 | * com.ugirls.ffmepg.task.threads 8 | * 2018/6/11. 9 | * 10 | * @author zhanghaishan 11 | * @version V1.0 12 | */ 13 | public class FFThreadManager { 14 | 15 | public static ThreadPool instance; 16 | 17 | // 获取单例的线程池对象 18 | public static ThreadPool getInstance() { 19 | if (instance == null) { 20 | synchronized (FFThreadManager.class) { 21 | if (instance == null) { 22 | int cpuNum = Runtime.getRuntime().availableProcessors();// 获取处理器数量 23 | int threadNum = cpuNum * 2 + 1;// 根据cpu数量,计算出合理的线程并发数 24 | instance = new ThreadPool(threadNum-1, threadNum, Integer.MAX_VALUE);//默认是双核的cpu 每个核心走一个线程 一个等待线程 25 | } 26 | } 27 | } 28 | return instance; 29 | } 30 | 31 | public static class ThreadPool { 32 | private ThreadPoolExecutor mExecutor; 33 | private int corePoolSize; 34 | private int maximumPoolSize; 35 | private long keepAliveTime; 36 | 37 | private ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime) { 38 | this.corePoolSize = corePoolSize; 39 | this.maximumPoolSize = maximumPoolSize; 40 | this.keepAliveTime = keepAliveTime; 41 | 42 | mExecutor = new ThreadPoolExecutor(corePoolSize,// 核心线程数 43 | maximumPoolSize, // 最大线程数 44 | keepAliveTime, // 闲置线程存活时间 45 | TimeUnit.MILLISECONDS,// 时间单位 46 | new LinkedBlockingDeque(Integer.MAX_VALUE),// 线程队列 47 | Executors.defaultThreadFactory(),// 线程工厂 48 | new ThreadPoolExecutor.AbortPolicy() {// 队列已满,而且当前线程数已经超过最大线程数时的异常处理策略 49 | @Override 50 | public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 51 | super.rejectedExecution(r, e); 52 | } 53 | } 54 | ); 55 | } 56 | 57 | public void execute(Runnable runnable) { 58 | if (runnable == null) { 59 | return; 60 | } 61 | mExecutor.execute(runnable); 62 | } 63 | 64 | /** 65 | * 提交一个可返回值的线程 66 | * @param runnable 线程 67 | * @param bean 结果类 68 | * @param 类型 69 | * @return 返回结果 70 | * @throws ExecutionException 提交异常 71 | * @throws InterruptedException 提交异常 72 | */ 73 | public T submit(Runnable runnable,T bean) throws ExecutionException, InterruptedException { 74 | if (runnable == null) { 75 | return null; 76 | } 77 | Future future = mExecutor.submit(runnable,bean); 78 | return future.get(); 79 | } 80 | 81 | /** 82 | * 提交一个可返回值的线程 83 | * @param runnable 线程 84 | */ 85 | public void submit(Runnable runnable){ 86 | if (runnable == null) { 87 | return; 88 | } 89 | mExecutor.submit(runnable); 90 | } 91 | 92 | 93 | // 从线程队列中移除对象 94 | public void cancel(Runnable runnable) { 95 | if (mExecutor != null) { 96 | mExecutor.getQueue().remove(runnable); 97 | } 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/util/FFAnnotationUtil.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.util; 2 | 3 | 4 | import org.mountcloud.ffmepg.annotation.FFAnnotation; 5 | import org.mountcloud.ffmepg.annotation.FFCmd; 6 | import org.mountcloud.ffmepg.annotation.FFCmdBean; 7 | import org.mountcloud.ffmepg.excption.FFMpegOperationNotFoundExcption; 8 | import org.mountcloud.ffmepg.operation.FFOperationBase; 9 | 10 | import java.lang.reflect.Field; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * 注解工具 16 | * com.ugirls.ffmepg.util 17 | * 2018/6/6. 18 | * 19 | * @author zhanghaishan 20 | * @version V1.0 21 | */ 22 | public class FFAnnotationUtil { 23 | 24 | /** 25 | * 提取一个操作中的注解 26 | * @param bean 操作 27 | * @param 操作的类型 28 | * @return 返回操作中的所有注解 29 | * @throws IllegalAccessException 异常 30 | */ 31 | public FFCmdBean getClassAnnocation(T bean) throws IllegalAccessException { 32 | FFCmdBean ffCmdBean = new FFCmdBean(); 33 | 34 | Class beanClass = bean.getClass(); 35 | 36 | //获取类的注解 37 | FFCmd classFFCmd = getClassFFCmd(beanClass); 38 | FFAnnotation classFFAnnotation = getFFOperation(classFFCmd,null,bean); 39 | 40 | if(classFFAnnotation==null){ 41 | throw new FFMpegOperationNotFoundExcption(beanClass.getName()+" not found FFCmd."); 42 | } 43 | 44 | //获取属性注解 45 | List fields = new ArrayList(); 46 | 47 | ObjectUtil objectUtil = new ObjectUtil(); 48 | //获取全部属性 49 | objectUtil.getFields(beanClass,fields,null); 50 | 51 | if(fields==null||fields.size()==0){ 52 | throw new FFMpegOperationNotFoundExcption(beanClass.getName()+" fields length is 0"); 53 | } 54 | 55 | List ffAnnotations = new ArrayList(); 56 | 57 | for(int i=0;i type 80 | * @return FFcmd 81 | */ 82 | private FFCmd getClassFFCmd(Class cls){ 83 | FFCmd ffCmd = cls.getAnnotation(FFCmd.class); 84 | if(ffCmd==null){ 85 | Class superCls = cls.getSuperclass(); 86 | if(superCls.equals(Object.class)){ 87 | return null; 88 | }else{ 89 | return getClassFFCmd(superCls); 90 | } 91 | } 92 | return ffCmd; 93 | } 94 | 95 | /** 96 | * 从一组Annotation中提取FFCmd 97 | * @param ffCmd 注解 98 | * @return FFAnnotation 99 | */ 100 | private FFAnnotation getFFOperation(FFCmd ffCmd, Field field,T bean) throws IllegalAccessException { 101 | FFAnnotation ffAnnotation = null; 102 | //如果没有注解的返回空 103 | if(ffCmd==null){ 104 | return ffAnnotation; 105 | } 106 | 107 | //获取key值 108 | String key = ffCmd.key(); 109 | 110 | ffAnnotation = new FFAnnotation(); 111 | 112 | if(field==null){ 113 | ffAnnotation.setKey(key); 114 | return ffAnnotation; 115 | } 116 | //设置访问最高权限可访问private属性 117 | field.setAccessible(true); 118 | 119 | ffAnnotation.setKey(key); 120 | Object value = field.get(bean); 121 | if(value!=null){ 122 | String valueStr = value.toString(); 123 | ffAnnotation.setValue(valueStr); 124 | } 125 | 126 | return ffAnnotation; 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/util/FFBigDecimalUtil.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.util; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * BigDecimal计算工具 7 | * @author zhanghaishan 8 | * @version V1.0 9 | * com.ugirls.statisticalservice.util 10 | * 2018/1/16. 11 | */ 12 | public class FFBigDecimalUtil { 13 | 14 | //------------------------------------------------------------------------------------ 15 | /** 16 | * 提供精确加法计算的add方法 17 | * @param value1 被加数 18 | * @param value2 加数 19 | * @return 两个参数的和 20 | */ 21 | public static double add(double value1,double value2){ 22 | BigDecimal b1 = new BigDecimal(Double.valueOf(value1)); 23 | BigDecimal b2 = new BigDecimal(Double.valueOf(value2)); 24 | return b1.add(b2).doubleValue(); 25 | } 26 | 27 | /** 28 | * 提供精确减法运算的sub方法 29 | * @param value1 被减数 30 | * @param value2 减数 31 | * @return 两个参数的差 32 | */ 33 | public static double sub(double value1,double value2){ 34 | BigDecimal b1 = new BigDecimal(Double.valueOf(value1)); 35 | BigDecimal b2 = new BigDecimal(Double.valueOf(value2)); 36 | return b1.subtract(b2).doubleValue(); 37 | } 38 | 39 | /** 40 | * 提供精确乘法运算的mul方法 41 | * @param value1 被乘数 42 | * @param value2 乘数 43 | * @return 两个参数的积 44 | */ 45 | public static double mul(double value1,double value2){ 46 | BigDecimal b1 = new BigDecimal(Double.valueOf(value1)); 47 | BigDecimal b2 = new BigDecimal(Double.valueOf(value2)); 48 | return b1.multiply(b2).doubleValue(); 49 | } 50 | 51 | /** 52 | * 提供精确的除法运算方法div 53 | * @param value1 被除数 54 | * @param value2 除数 55 | * @param scale 精确范围 56 | * @return 两个参数的商 57 | * @throws IllegalAccessException 异常 58 | */ 59 | public static double div(double value1,double value2,int scale) throws IllegalAccessException{ 60 | //如果精确范围小于0,抛出异常信息 61 | if(scale<0){ 62 | throw new IllegalAccessException("精确度不能小于0"); 63 | } 64 | BigDecimal b1 = new BigDecimal(Double.valueOf(value1)); 65 | BigDecimal b2 = new BigDecimal(Double.valueOf(value2)); 66 | BigDecimal devVal = b1.divide(b2, scale,BigDecimal.ROUND_HALF_UP); 67 | return devVal.doubleValue(); 68 | } 69 | 70 | 71 | //----------------------------------------------------------------------------------------------- 72 | 73 | 74 | /** 75 | * 多个求和 76 | * @param decimals 多个值 77 | * @return 结果 78 | */ 79 | public static BigDecimal addDecimals(BigDecimal ...decimals){ 80 | BigDecimal val = new BigDecimal(0); 81 | for(BigDecimal dc:decimals){ 82 | val = addDecimal(val,dc); 83 | } 84 | return val; 85 | } 86 | 87 | /** 88 | * 提供精确加法计算的add方法 89 | * @param value1 被加数 90 | * @param value2 加数 91 | * @return 两个参数的和 92 | */ 93 | public static BigDecimal addDecimal(BigDecimal value1,BigDecimal value2){ 94 | if(value1==null&&value2==null){ 95 | return new BigDecimal(0); 96 | } 97 | if(value1==null){ 98 | return value2; 99 | } 100 | if(value2==null){ 101 | return value1; 102 | } 103 | return value1.add(value2); 104 | } 105 | 106 | /** 107 | * 提供精确减法运算的sub方法 108 | * @param value1 被减数 109 | * @param value2 减数 110 | * @return 两个参数的差 111 | */ 112 | public static BigDecimal subDecimal(BigDecimal value1,BigDecimal value2){ 113 | return value1.subtract(value2); 114 | } 115 | 116 | /** 117 | * 提供精确乘法运算的mul方法 118 | * @param value1 被乘数 119 | * @param value2 乘数 120 | * @return 两个参数的积 121 | */ 122 | public static BigDecimal mulDecimal(BigDecimal value1,BigDecimal value2){ 123 | return value1.multiply(value2); 124 | } 125 | 126 | /** 127 | * 提供精确的除法运算方法div 128 | * @param value1 被除数 129 | * @param value2 除数 130 | * @param scale 精确范围 131 | * @return 两个参数的商 132 | * @throws IllegalAccessException 异常 133 | */ 134 | public static BigDecimal divDecimal(BigDecimal value1,BigDecimal value2,int scale) throws IllegalAccessException{ 135 | //如果精确范围小于0,抛出异常信息 136 | if(scale<0){ 137 | throw new IllegalAccessException("精确度不能小于0"); 138 | } 139 | return value1.divide(value2, scale); 140 | } 141 | 142 | /** 143 | * 整数相加 144 | * @param i1 值1 145 | * @param i2 值2 146 | * @return 相加结果 147 | */ 148 | public static Integer addInteger(Integer i1,Integer i2){ 149 | if(i1==null&&i2==null){ 150 | return 0; 151 | } 152 | if(i1==null){ 153 | return i2; 154 | } 155 | if(i2==null){ 156 | return i1; 157 | } 158 | return i1+i2; 159 | } 160 | 161 | /** 162 | * 返回浮点型累加数量 163 | * @param values 多个相加的值 164 | * @return 结果 165 | */ 166 | public static Float addAllFloat(Float ...values){ 167 | BigDecimal od = new BigDecimal(0.0); 168 | for(int i=0;i params) throws IOException { 68 | String[] tempParams = new String[params.size()]; 69 | for(int i=0;i 0) { 22 | min+=Integer.valueOf(strs[0])*60*60;//秒 23 | } 24 | if(strs[1].compareTo("0")>0){ 25 | min+=Integer.valueOf(strs[1])*60; 26 | } 27 | if(strs[2].compareTo("0")>0){ 28 | min+=Math.round(Float.valueOf(strs[2])); 29 | } 30 | return min; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/util/OSUtils.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.util; 2 | 3 | public class OSUtils { 4 | 5 | public enum OSType { 6 | OS_TYPE_LINUX, OS_TYPE_WIN, OS_TYPE_SOLARIS, OS_TYPE_MAC, OS_TYPE_FREEBSD, OS_TYPE_OTHER 7 | } 8 | 9 | static private OSType getOSType() { 10 | String osName = System.getProperty("os.name"); 11 | if (osName.startsWith("Windows")) { 12 | return OSType.OS_TYPE_WIN; 13 | } else if (osName.contains("SunOS") || osName.contains("Solaris")) { 14 | return OSType.OS_TYPE_SOLARIS; 15 | } else if (osName.contains("Mac")) { 16 | return OSType.OS_TYPE_MAC; 17 | } else if (osName.contains("FreeBSD")) { 18 | return OSType.OS_TYPE_FREEBSD; 19 | } else if (osName.startsWith("Linux")) { 20 | return OSType.OS_TYPE_LINUX; 21 | } else { 22 | // Some other form of Unix 23 | return OSType.OS_TYPE_OTHER; 24 | } 25 | } 26 | 27 | public static final OSType osType = getOSType(); 28 | // Helper static vars for each platform 29 | public static final boolean WINDOWS = (osType == OSType.OS_TYPE_WIN); 30 | public static final boolean SOLARIS = (osType == OSType.OS_TYPE_SOLARIS); 31 | public static final boolean MAC = (osType == OSType.OS_TYPE_MAC); 32 | public static final boolean FREEBSD = (osType == OSType.OS_TYPE_FREEBSD); 33 | public static final boolean LINUX = (osType == OSType.OS_TYPE_LINUX); 34 | public static final boolean OTHER = (osType == OSType.OS_TYPE_OTHER); 35 | 36 | public static void main(String[] args) { 37 | System.out.println(OSUtils.WINDOWS); 38 | System.out.println(OSUtils.LINUX); 39 | System.out.println(OSUtils.MAC); 40 | System.out.println("OSUtils.main()"); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/util/ObjectUtil.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.util; 2 | 3 | import java.beans.PropertyDescriptor; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Method; 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.Date; 9 | import java.util.List; 10 | 11 | /** 12 | * Object对象工具 13 | * @author zhanghaishan 14 | * @version V1.0 15 | * 2017/12/18. 16 | */ 17 | public class ObjectUtil { 18 | 19 | /** 20 | * 获取所有函数 21 | * @param t 类 22 | * @param methods 方法 23 | * @param index 深度 24 | */ 25 | public void getMethods(Class t, List methods, Integer index){ 26 | 27 | methods.addAll(Arrays.asList(t.getMethods())); 28 | 29 | if(index!=null){ 30 | index = index -1; 31 | if(index<=0){ 32 | return; 33 | } 34 | } 35 | 36 | Class t1 = t.getSuperclass(); 37 | if (t1.getSimpleName().equals("Object")) { 38 | return; 39 | } 40 | getMethods(t1, methods,index); 41 | } 42 | 43 | /** 44 | * 获取所有属性 45 | * @param t 类 46 | * @param fields 属性 47 | * @param index 深度 48 | */ 49 | public void getFields(Class t, List fields, Integer index) { 50 | 51 | fields.addAll(Arrays.asList(t.getDeclaredFields())); 52 | 53 | if(index!=null){ 54 | index = index -1; 55 | if(index<=0){ 56 | return; 57 | } 58 | } 59 | 60 | Class t1 = t.getSuperclass(); 61 | if (t1.getSimpleName().equals("Object")) { 62 | return; 63 | } 64 | getFields(t1, fields,index); 65 | } 66 | 67 | /** 68 | * 将空属性附上默认值 69 | * @param obj 对象 70 | * @param notSet 不设置为空的属性 71 | */ 72 | public void setNullFields(T obj, List notSet){ 73 | if(obj==null){ 74 | return; 75 | } 76 | Class entityClass = obj.getClass(); 77 | 78 | List entityFields = new ArrayList(); 79 | getFields(entityClass, entityFields,null); 80 | 81 | for(Field field:entityFields){ 82 | if(field.getName().equals("serialVersionUID")){ 83 | continue; 84 | } 85 | 86 | if(notSet!=null&¬Set.contains(field.getName())){ 87 | continue; 88 | } 89 | 90 | try{ 91 | PropertyDescriptor pd = new PropertyDescriptor(field.getName(), entityClass); 92 | Method get = pd.getReadMethod(); 93 | Object getValue = get.invoke(obj); 94 | 95 | if(getValue==null){ 96 | 97 | Method set = pd.getWriteMethod(); 98 | 99 | Class fieldClass = field.getType(); 100 | if(fieldClass.equals(String.class)){ 101 | set.invoke(obj,new String("")); 102 | } 103 | 104 | if(fieldClass.equals(Integer.class)){ 105 | set.invoke(obj,new Integer(0)); 106 | } 107 | 108 | if(fieldClass.equals(Double.class)){ 109 | set.invoke(obj,new Double(0)); 110 | } 111 | 112 | if(fieldClass.equals(Float.class)){ 113 | set.invoke(obj,new Float(0)); 114 | } 115 | 116 | if(fieldClass.equals(Date.class)){ 117 | set.invoke(obj,new Date()); 118 | } 119 | } 120 | 121 | }catch (Exception e){ 122 | e.printStackTrace();; 123 | } 124 | 125 | } 126 | 127 | } 128 | 129 | /** 130 | * 给空属性附上默认值 131 | * @param objs 对象集合 132 | * @param notSet 不设置空的属性 133 | */ 134 | public void setNullFields(List objs,List notSet){ 135 | if(objs!=null){ 136 | for(int i=0;i map){ 107 | if(map!=null){ 108 | Set keys = map.keySet(); 109 | for(String key : keys){ 110 | String val = map.get(key); 111 | str = str.replaceAll(key,val); 112 | } 113 | } 114 | return str; 115 | } 116 | 117 | /** 118 | * 提取字符串里的数字 119 | * @param str 字符串 120 | * @return 数字 121 | */ 122 | public static String findNumber(String str){ 123 | String regEx="[^0-9]"; 124 | String newStr = findString(str,regEx); 125 | return newStr; 126 | } 127 | 128 | /** 129 | * 根据正则提取字符串 130 | * @param str 字符串 131 | * @param regEx 正则 132 | * @return 结果 133 | */ 134 | public static String findString(String str,String regEx){ 135 | if(str==null){ 136 | return null; 137 | } 138 | Pattern p = Pattern.compile(regEx); 139 | Matcher m = p.matcher(str); 140 | String newStr = m.replaceAll("").trim(); 141 | return newStr; 142 | } 143 | 144 | /** 145 | * 根据需要复制的字符串长度进行复制拼接字符串 146 | * @param str 字符串 147 | * @param cloneNum 需要拼接的次数 148 | * @return 结果 149 | */ 150 | public static String cloneAndAppend(String str,int cloneNum){ 151 | String newStr = str; 152 | for(int i=0;i findStringsByRegs(String regex, String str){ 165 | List strs = new ArrayList<>(); 166 | Pattern r = Pattern.compile(regex); 167 | Matcher ma = r.matcher(str); 168 | while (ma.find()){ 169 | String findStr = ma.group(0); 170 | if(findStr!=null&&findStr.length()>0){ 171 | strs.add(findStr); 172 | } 173 | } 174 | return strs; 175 | } 176 | 177 | /** 178 | * 查询字符串 179 | * @param regex 正则 180 | * @param str 字符串 181 | * @return 结果 182 | */ 183 | public static String findStringsByRegsOne(String regex, String str){ 184 | List strs = findStringsByRegs(regex,str); 185 | String result = strs.size()==0? null : strs.get(0); 186 | return result; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/org/mountcloud/ffmepg/util/UUIDUtil.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmepg.util; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * UUID工具 7 | * @author zhanghaishan 8 | * @version V1.0 9 | * com.ugirls.util 10 | * 2017/12/28. 11 | */ 12 | public class UUIDUtil { 13 | 14 | /** 15 | * 返回一个全UUID 16 | * @return 完全UUID 17 | */ 18 | public static String getUUID(){ 19 | return UUID.randomUUID().toString(); 20 | } 21 | 22 | /** 23 | * 返回一个没有减号的UUID 24 | * @return 精简后的UUID 25 | */ 26 | public static String getUUIDSimpl(){ 27 | UUID uuid = UUID.randomUUID(); 28 | return uuid.toString().replaceAll("\\-", ""); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/org/mountcloud/ffmpeg/TestTask.java: -------------------------------------------------------------------------------- 1 | package org.mountcloud.ffmpeg; 2 | 3 | import org.junit.Test; 4 | import org.mountcloud.ffmepg.operation.ffmpeg.vidoe.FFMpegVideoFormatM3u8; 5 | import org.mountcloud.ffmepg.operation.ffmpeg.vidoe.FFMpegVideoInfo; 6 | import org.mountcloud.ffmepg.result.defaultResult.FFVideoInfoResult; 7 | import org.mountcloud.ffmepg.task.bean.FFTaskStateEnum; 8 | import org.mountcloud.ffmepg.task.bean.tasks.FFMepgVideoFormatM3u8Task; 9 | import org.mountcloud.ffmepg.task.bean.tasks.FFMepgVideoInfoTask; 10 | import org.mountcloud.ffmepg.task.context.FFTaskContext; 11 | 12 | public class TestTask { 13 | 14 | @Test 15 | public void testM3u8(){ 16 | FFMpegVideoFormatM3u8 m3u8Operation = new FFMpegVideoFormatM3u8(); 17 | m3u8Operation.setVideoFileName("D:\\test\\test.mp4"); 18 | m3u8Operation.setBitrate("2048k"); 19 | m3u8Operation.setTimes(5); 20 | m3u8Operation.setM3u8File("D:\\test\\m3u8\\test.m3u8"); 21 | m3u8Operation.setTsFiles("D:\\test\\m3u8\\test%5d.ts"); 22 | 23 | System.out.println(m3u8Operation.toString()); 24 | 25 | FFMepgVideoFormatM3u8Task task = new FFMepgVideoFormatM3u8Task(m3u8Operation); 26 | 27 | FFTaskContext.getContext().addTask(task); 28 | 29 | while (!task.getProgress().getState().equals(FFTaskStateEnum.COMPLETE)){ 30 | System.out.println(task.getProgress().getProgress()); 31 | try { 32 | Thread.sleep(1000); 33 | } catch (InterruptedException e) { 34 | e.printStackTrace(); 35 | } 36 | } 37 | } 38 | 39 | 40 | @Test 41 | public void testInfo(){ 42 | FFVideoInfoResult result = new FFVideoInfoResult(); 43 | 44 | FFMpegVideoInfo ffMpegVideoInfo = new FFMpegVideoInfo(); 45 | ffMpegVideoInfo.setVideoUrl("D:\\test\\test.mp4"); 46 | FFMepgVideoInfoTask videoInfoTask = new FFMepgVideoInfoTask(result,ffMpegVideoInfo); 47 | 48 | FFTaskContext.getContext().submit(videoInfoTask,null); 49 | 50 | System.out.println(result.getTimeLengthSec()); 51 | System.out.println(result.getTimeLength()); 52 | System.out.println(result.getStartTime()); 53 | System.out.println(result.getBitrate()); 54 | System.out.println(result.getWidth()); 55 | System.out.println(result.getHeight()); 56 | System.out.println(result.getFps()); 57 | System.out.println(result.getTbr()); 58 | System.out.println(result.getTbn()); 59 | System.out.println(result.getTbc()); 60 | } 61 | } 62 | --------------------------------------------------------------------------------