JAVA调用FFMpeg进行转码等操作
直接上代码:
public abstract class FFmpegUtils { FFmpegUtils ffmpegUtils; int timeLengthSec = 1; String timeLength = ""; Pattern pattern = Pattern.compile("Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s"); String frameRegexDuration = "size=([\\s\\S]*) time=(.*?) bitrate=([\\s\\S]*) speed=(.*?)x"; String videoframeRegexDuration = "frame=([\\s,\\d]*) fps=(.*?) q=(.*?) size=([\\s\\S]*) time=(.*?) bitrate=([\\s\\S]*) speed=(.*?)x"; Pattern framePattern = Pattern.compile(frameRegexDuration); public static void main(String[] args){ String target = ""; /* try { target = extractAsyn("D:\\ffmpeg4.2\\bin\\ffmpeg.exe", "-y -f image2 -ss 1 -t 0.001 -s 640x480", "E:\\迅雷下载\\电影\\test.avi", "E:\\迅雷下载\\电影\\test.avi.jpg"); System.out.println(target); } catch (Throwable e) { System.err.println(e.getMessage()); } */ try { new FFmpegUtils() { @Override public void dealLine(String line) { System.out.println(line); if(timeLength == null || timeLength.equals("")) { Matcher m = pattern.matcher(line.trim()); if (m.find()) { timeLength = m.group(1); if(timeLength!=null){ timeLengthSec = FFVideoUtil.getTimelen(timeLength); } System.out.println(timeLength+"||"+timeLengthSec); } } //获取视频信息 Matcher matcher = framePattern.matcher(line); if(matcher.find()){ try { String execTimeStr = matcher.group(2); int execTimeInt = FFVideoUtil.getTimelen(execTimeStr); double devnum = FFBigDecimalUtil.div(execTimeInt,timeLengthSec,5); double progressDouble = FFBigDecimalUtil.mul(devnum,100); System.out.println("execTimeInt:"+execTimeInt+"&,devnum:"+devnum+"&,progressDouble:"+progressDouble); } catch (IllegalAccessException e) { System.err.println("获取输出流异常:"+e.getMessage()); } } } @Override public void dealStream(Process process) { if (process == null) { return; } // 处理InputStream的线程 new Thread() { @Override public void run() { BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); String line = null; try { while ((line = in.readLine()) != null) { //logger.info("output: " + line); dealLine(line); } } catch (IOException e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } }.start(); // 处理ErrorStream的线程 new Thread() { @Override public void run() { BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream())); String line = null; try { while ((line = err.readLine()) != null) { dealLine(line); } } catch (IOException e) { e.printStackTrace(); } finally { try { err.close(); } catch (IOException e) { e.printStackTrace(); } } } }.start(); } }.processVideoSync("D:\\ffmpeg4.2\\bin\\ffmpeg.exe", " -f|mp3", "E:\\迅雷下载\\电影\\test.avi", "E:\\迅雷下载\\电影\\test.avi.mp3"); System.out.println(target); } catch (Throwable e) { System.err.println(e.getMessage()); } } //异步 适合抽帧等快速的操作 public static String extractAsyn( String ffmpegPath,String cmdParam, String sourceFile,String targetFile) throws Throwable { Runtime runtime = Runtime.getRuntime(); Process proce = null; // 视频截图命令,封面图。 8是代表第8秒的时候截图 String cmd = ""; String cut = ffmpegPath +" -i "+ sourceFile +" "+ cmdParam +" "+ targetFile; String cutCmd = cmd + cut; proce = runtime.exec(cutCmd); proce.getOutputStream(); System.out.println("抽帧命令是:"+cut); return targetFile; } public static boolean checkfile(String path) { File file = new File(path); if (!file.isFile()) { return false; } return true; } //异步处理 public boolean processVideoSync(String ffmpegPath,String cmdParam, String sourceFile,String targetFile) { // 文件命名 List<String> commond = new ArrayList<String>(); commond.add(ffmpegPath); commond.add("-i"); commond.add(sourceFile); commond.addAll(Arrays.asList(cmdParam.trim().split("\\|"))); commond.add(targetFile); if(new File(targetFile).exists()) { new File(targetFile).delete(); } String cmds = ""; for (String cmd : commond) { cmds = cmds + " " + cmd; } System.out.println("执行命令参数为:" + cmds); try { // 调用线程命令进行转码 Process videoProcess = new ProcessBuilder(commond).redirectErrorStream(true).start(); //new PrintStream(videoProcess.getInputStream()).start(); //videoProcess.waitFor(); /*new InputStreamReader(videoProcess.getErrorStream()); BufferedReader stdout = new BufferedReader(new InputStreamReader(videoProcess.getInputStream())); String line; while ((line = stdout.readLine()) != null) { dealLine(line); }*/ dealStream(videoProcess); videoProcess.waitFor(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } //处理输出流 public abstract void dealLine(String line); public abstract void dealStream(Process process ); }
@Component public class ProgressService extends FFmpegUtils{ public static Logger logger = LoggerFactory.getLogger(ProgressService.class); /** * 进度正则查询 */ private String frameRegexDuration = "frame=([\\s,\\d]*) fps=(.*?) q=(.*?) size=([\\s\\S]*) time=(.*?) bitrate=([\\s\\S]*) speed=(.*?)x"; /** * 正则模式 */ private Pattern framePattern = Pattern.compile(frameRegexDuration); /** * 秒数 */ private Integer timeLengthSec; /** * 时长 */ private String timeLength; /** * 开始时间 */ private String startTime; /** * 比特率 */ private String bitrate; /** * 时长 正则 */ private String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s"; /** * 正则模式 */ private Pattern pattern = Pattern.compile(regexDuration); public String getStartTime() { return startTime; } public void setStartTime(String startTime) { this.startTime = startTime; } public String getBitrate() { return bitrate; } public void setBitrate(String bitrate) { this.bitrate = bitrate; } private TranscodeTask task; public TranscodeTask getTask() { return task; } public void setTask(TranscodeTask task) { this.task = task; } @Autowired private TaskReposity _taskRep; @Override public void dealLine(String line) { logger.debug("{}输出信息:{}",task.getName(),line); //获取视频长度信息 if(timeLength == null || "".equals(timeLength)) { Matcher m = pattern.matcher(line.trim()); if (m.find()) { timeLength = m.group(1); if(timeLength!=null){ timeLengthSec = FFVideoUtil.getTimelen(timeLength); } startTime = m.group(2); bitrate = m.group(3); logger.debug("timeLength:{}, startTime:{},bitrate:{}",timeLength,startTime,bitrate); } } //获取视频信息 Matcher matcher = framePattern.matcher(line); if(matcher.find()){ try { String execTimeStr = matcher.group(5); int execTimeInt = FFVideoUtil.getTimelen(execTimeStr); double devnum = FFBigDecimalUtil.div(execTimeInt,timeLengthSec,5); double progressDouble = FFBigDecimalUtil.mul(devnum,100); logger.debug("execTimeInt:{},devnum:{},progressDouble:{}",execTimeInt,devnum,progressDouble); task.setProgress((float)progressDouble); _taskRep.saveAndFlush(this.task); } catch (IllegalAccessException e) { logger.error("获取输出流异常:{}",e.getMessage()); } } } /** * 处理process输出流和错误流,防止进程阻塞 * 在process.waitFor();前调用 * @param process */ @Override public void dealStream(Process process) { if (process == null) { return; } // 处理InputStream的线程 new Thread() { @Override public void run() { BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); String line = null; try { while ((line = in.readLine()) != null) { //logger.info("output: " + line); dealLine(line); } } catch (IOException e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } }.start(); // 处理ErrorStream的线程 new Thread() { @Override public void run() { BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream())); String line = null; try { while ((line = err.readLine()) != null) { logger.info("err: " + line); } } catch (IOException e) { e.printStackTrace(); } finally { try { err.close(); } catch (IOException e) { e.printStackTrace(); } } } }.start(); } }
@Component public class FileService { public static Logger logger = LoggerFactory.getLogger(FileService.class); // 下载小文件 public File downloadFile(String formUrl, String fileName) throws Throwable { File desc = null; CloseableHttpClient httpclient = HttpClients.createDefault(); HttpGet httpget = new HttpGet(formUrl); httpget.setConfig(RequestConfig.custom() // .setConnectionRequestTimeout(5000) // .setConnectTimeout(5000) // .setSocketTimeout(5000) // .build()); logger.debug("正在从{}下载文件到{}",formUrl,fileName); try (CloseableHttpResponse response = httpclient.execute(httpget)) { org.apache.http.HttpEntity entity = response.getEntity(); desc = new File(fileName); try (InputStream is = entity.getContent(); // OutputStream os = new FileOutputStream(desc)) { StreamUtils.copy(is, os); logger.debug("成功从{}下载文件到{}",formUrl,fileName); } } return desc; } public void downloadLittleFileToPath(String url, String target) { Instant now = Instant.now(); RestTemplate template = new RestTemplate(); ClientHttpRequestFactory clientFactory = new HttpComponentsClientHttpRequestFactory(); template.setRequestFactory(clientFactory); HttpHeaders header = new HttpHeaders(); List<MediaType> list = new ArrayList<MediaType>(); // 指定下载文件类型 list.add(MediaType.APPLICATION_OCTET_STREAM); header.setAccept(list); HttpEntity<byte[]> request = new HttpEntity<byte[]>(header); ResponseEntity<byte[]> rsp = template.exchange(url, HttpMethod.GET, request, byte[].class); logger.info("[下载文件] [状态码] code:{}", rsp.getStatusCode()); try { if(Paths.get(target).toFile().exists()) { Paths.get(target).toFile().delete(); } Files.write(Paths.get(target), Objects.requireNonNull(rsp.getBody(), "未获取到下载文件")); } catch (IOException e) { logger.error("[下载文件] 写入失败:", e); } logger.info("[下载文件] 完成,耗时:{}", ChronoUnit.MILLIS.between(now, Instant.now())); } public void downloadBigFileToPath(String url, String target) { Instant now = Instant.now(); try { RestTemplate template = new RestTemplate(); ClientHttpRequestFactory clientFactory = new HttpComponentsClientHttpRequestFactory(); template.setRequestFactory(clientFactory); //定义请求头的接收类型 RequestCallback requestCallback = request -> request.getHeaders() .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL)); // getForObject会将所有返回直接放到内存中,使用流来替代这个操作 ResponseExtractor<Void> responseExtractor = response -> { // Here I write the response to a file but do what you like if(Files.exists(Paths.get(target), LinkOption.NOFOLLOW_LINKS)) { Files.delete(Paths.get(target)); } Files.copy(response.getBody(), Paths.get(target)); return null; }; template.execute(url, HttpMethod.GET, requestCallback, responseExtractor); } catch (Throwable e) { logger.error("[下载文件] 写入失败:", e); } logger.info("[下载文件] 完成,耗时:{}", ChronoUnit.MILLIS.between(now, Instant.now())); } }
有个问题需要注意:
转码目标文件必须不存在才行,如果存在 先删除,不然就卡死。