package com.qxueyou.scc.teach.live.utils; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.qxueyou.scc.base.util.UUIDUtils; import com.qxueyou.scc.teach.live.utils.FfmpegFileVO; import com.qxueyou.scc.teach.live.utils.FfmpegVideoInfo; /** * ÊÓÆµ×ªÂëÀà * @author cyq * */ public class FfmpegMediaHelper { /** * ʵÀý»¯log */ private static final Logger log = LogManager.getLogger(FfmpegMediaHelper.class); /** * ¸ßÇåÂëÂÊ */ private static int bitRateHd = 800 * 1024; /** * ±êÇåÂëÂÊ */ private static int bitRateSd = 400 * 1024; /** * µÍÇåÂëÂÊ */ private static int bitRateLd = 200 * 1024; /** * תmp4µÄ¹Ì¶¨ÃüÁî */ private static List commandMp4 = new ArrayList(10); /** * תm3u8µÄ¹Ì¶¨ÃüÁî */ private static String commandM3u8; /** * ÊÇ·ñΪÊÓÆµ */ private static Boolean videoFlag = false; /** * תmp4 * @param fileVO * @param videoInfo * @return */ public static boolean convertMp4(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { if (!checkfile(fileVO.getInputPath())) { return false; } try { System.out.println("FfmpegMediaHelper--תÂëmp4ÖÐ..."); commandMp4.clear(); commandMp4.add("ffmpeg"); commandMp4.add("-i"); commandMp4.add(fileVO.getInputPath()); commandMp4.add("-ab"); // ÉèÖÃÒôƵÂëÂÊ commandMp4.add("128*1024"); commandMp4.add("-r"); commandMp4.add("23"); if (videoInfo.getVideoHeight() >= 1000) { convertMp4Hd(fileVO, videoInfo); convertMp4Sd(fileVO, videoInfo); convertMp4Ld(fileVO, videoInfo); } else if (videoInfo.getVideoHeight() >= 700) { convertMp4Sd(fileVO, videoInfo); convertMp4Ld(fileVO, videoInfo); } else { convertMp4Ld(fileVO, videoInfo); } System.out.println("FfmpegMediaHelper--תÂëmp4Íê³É..."); return true; } catch (Exception e) { System.out.println("FfmpegMediaHelper--תÂëmp4Òì³£..."); log.error("תÂëmp4Òì³£:" + e.getMessage()); throw e; } } /** * תm3u8 * @param fileVO * @param videoInfo * @return */ public static boolean convertM3u8(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { if (!checkfile(fileVO.getInputPath())) { return false; } try { System.out.println("FfmpegMediaHelper--תÂëm3h8ÖÐ..."); commandM3u8 = "ffmpeg -i " + fileVO.getInputPath() + " -strict -2 -c:v libx264 -c:a aac -f hls -hls_time 10 -hls_list_size 0"; if (videoInfo.getVideoHeight() >= 1000) { convertM3u8Hd(fileVO, videoInfo); convertM3u8Sd(fileVO, videoInfo); convertM3u8Ld(fileVO, videoInfo); } else if (videoInfo.getVideoHeight() >= 700) { convertM3u8Sd(fileVO, videoInfo); convertM3u8Ld(fileVO, videoInfo); } else { convertM3u8Ld(fileVO, videoInfo); } System.out.println("FfmpegMediaHelper--תÂëm3u8Íê³É..."); return true; } catch (Exception e) { System.out.println("FfmpegMediaHelper--תÂëm3u8Òì³£..."); log.error("תÂëm3u8Òì³£:" + e.getMessage()); throw e; } } /** * ת¸ßÇåmp4 */ private static void convertMp4Hd(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { try { String resolution = (int) (1080 * videoInfo.getAspectRatio() / 2) * 2 + "*1080"; // ¿í¸ß±ØÐëÊÇżÊý List command = new ArrayList(commandMp4); if (videoInfo.getVideoBitRate() == null || videoInfo.getVideoBitRate() > bitRateHd) { command.add("-vb"); // ÊÓÆµÂëÂÊ command.add(String.valueOf(bitRateHd)); } command.add("-s"); // ³ß´ç£¨·Ö±æÂÊ£© command.add(resolution); command.add(fileVO.getOutputPath() + videoInfo.getUuid() + "mp4-hd.mp4"); convertMp4(command); videoInfo.setMp4HdUrl(fileVO.getOutputPath() + videoInfo.getUuid() + "mp4-hd.mp4"); // תÂë³É¹¦£¬½«Â·¾¶ÉèÖõ½videoInfoÖÐ } catch (Exception e) { throw e; } } /** * ת¸ßÇåm3u8 */ private static void convertM3u8Hd(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { try { String resolution = (int) (1080 * videoInfo.getAspectRatio() / 2) * 2 + "*1080"; // ¿í¸ß±ØÐëÊÇżÊý StringBuffer commandBuffer = new StringBuffer(commandM3u8); if (videoInfo.getVideoBitRate() == null || videoInfo.getVideoBitRate() > bitRateHd) { commandBuffer.append(" -vb ").append(String.valueOf(bitRateHd)).append(' '); } commandBuffer.append(" -s ").append(resolution).append(' '); commandBuffer.append(fileVO.getOutputPath()); commandBuffer.append(videoInfo.getUuid()).append("m3u8-hd.m3u8"); convertM3u8(commandBuffer.toString()); videoInfo.setM3u8HdUrl(fileVO.getOutputPath() + videoInfo.getUuid() + "m3u8-hd.m3u8"); } catch (Exception e) { throw e; } } /** * ת±êÇåmp4 */ private static void convertMp4Sd(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { try { String resolution = (int) (720 * videoInfo.getAspectRatio() / 2) * 2 + "*720"; List command = new ArrayList(commandMp4); if (videoInfo.getVideoBitRate() == null || videoInfo.getVideoBitRate() > bitRateSd) { command.add("-vb"); command.add(String.valueOf(bitRateSd)); } command.add("-s"); command.add(resolution); command.add(fileVO.getOutputPath() + videoInfo.getUuid() + "mp4-sd.mp4"); convertMp4(command); videoInfo.setMp4SdUrl(fileVO.getOutputPath() + videoInfo.getUuid() + "mp4-sd.mp4"); } catch (Exception e) { throw e; } } /** * ת±êÇåm3u8 */ private static void convertM3u8Sd(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { try { String resolution = (int) (720 * videoInfo.getAspectRatio() / 2) * 2 + "*720"; StringBuffer commandBuffer = new StringBuffer(commandM3u8); if (videoInfo.getVideoBitRate() == null || videoInfo.getVideoBitRate() > bitRateSd) { commandBuffer.append(" -vb ").append(String.valueOf(bitRateSd)).append(' '); } commandBuffer.append(" -s ").append(resolution).append(' '); commandBuffer.append(fileVO.getOutputPath()).append(videoInfo.getUuid()).append("m3u8-sd.m3u8"); convertM3u8(commandBuffer.toString()); videoInfo.setM3u8SdUrl(fileVO.getOutputPath() + videoInfo.getUuid() + "m3u8-sd.m3u8"); } catch (Exception e) { throw e; } } /** * תµÍÇåmp4 */ private static void convertMp4Ld(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { try { String resolution = (int) (480 * videoInfo.getAspectRatio() / 2) * 2 + "*480"; List command = new ArrayList(commandMp4); if (videoInfo.getVideoBitRate() == null || videoInfo.getVideoBitRate() > bitRateLd) { command.add("-vb"); command.add(String.valueOf(bitRateLd)); } command.add("-s"); command.add(resolution); command.add(fileVO.getOutputPath() + videoInfo.getUuid() + "mp4-ld.mp4"); convertMp4(command); videoInfo.setMp4LdUrl(fileVO.getOutputPath() + videoInfo.getUuid() + "mp4-ld.mp4"); } catch (Exception e) { throw e; } } /** * תµÍÇåm3u8 */ private static void convertM3u8Ld(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { try { String resolution = (int) (480 * videoInfo.getAspectRatio() / 2) * 2 + "*480"; StringBuffer commandBuffer = new StringBuffer(commandM3u8); if (videoInfo.getVideoBitRate() == null || videoInfo.getVideoBitRate() > bitRateLd) { commandBuffer.append(" -vb ").append(String.valueOf(bitRateSd)).append(' '); } commandBuffer.append(" -s ").append(resolution).append(' '); commandBuffer.append(fileVO.getOutputPath()).append(videoInfo.getUuid()).append("m3u8-ld.m3u8"); convertM3u8(commandBuffer.toString()); videoInfo.setM3u8LdUrl(fileVO.getOutputPath() + videoInfo.getUuid() + "m3u8-ld.m3u8"); } catch (Exception e) { throw e; } } /** * »ñȡýÌåÐÅÏ¢ * * @param fileVO * @param videoInfo * @return */ public static boolean mediaInfo(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { if (!checkfile(fileVO.getInputPath())) { return false; } BufferedReader buf = null; // ±£´æffmpegµÄÊä³ö½á¹ûÁ÷ String currLine = null; String line = null; try { log.debug("»ñȡýÌåÐÅÏ¢..."); List command = new ArrayList(); // ffprobe ÃüÁî»ñȡýÌåÐÅÏ¢ command.add("ffprobe"); command.add("-print_format"); // json¸ñʽÊä³ö command.add("json"); command.add("-show_streams"); command.add(fileVO.getInputPath()); ProcessBuilder process = new ProcessBuilder(); process.command(command); process.redirectErrorStream(true); Process p = process.start(); buf = new BufferedReader(new InputStreamReader(p.getInputStream())); while ((currLine = buf.readLine()) != null) { // ´¦Àí¶àÓà×Ö·û line = currLine.trim().replaceAll("\"", "").replaceAll(",", "").replace(" ", ""); handleLine(line, videoInfo); // »ñÈ¡Îļþʱ³¤(flvÐèÒªÕâôȡ) if(currLine.trim().startsWith("Duration:") && currLine.contains(",") ){ String strFirst = currLine.trim().split(",")[0].substring(9).trim().substring(0,8); String[] arrTime = strFirst.split(":"); if(null != arrTime && arrTime.length == 3 ){ Integer iHour = Integer.parseInt(arrTime[0]); Integer iMinute = Integer.parseInt(arrTime[1]); Integer iSecond = Integer.parseInt(arrTime[2]); videoInfo.setPlayTime(iHour * 60 * 60 + iMinute * 60 + iSecond); log.debug("ÊÓÆµÊ±³¤£º" + videoInfo.getPlayTime()); } } } p.waitFor();// ÕâÀïÏß³Ì×èÈû£¬½«µÈ´ýÍⲿת»»½ø³ÌÔËÐгɹ¦ÔËÐнáÊøºó£¬²ÅÍùÏÂÖ´ÐÐ log.debug("»ñȡýÌåÐÅÏ¢Íê³É"); return true; } catch (Exception e) { log.debug("»ñȡýÌåÐÅÏ¢Òì³£:" + e.getMessage()); throw e; } } private static void handleLine(String line, FfmpegVideoInfo videoInfo) { if (line.contains(":")) { String[] arr = line.split(":"); if (null != arr && arr.length == 2) { // 1. Èç¹ûÊDZê־λ if ("codec_type".equals(arr[0].trim())) { // ÊÓÆµ if ("video".equals(arr[1].trim())) { videoFlag = true; } // ÒôƵ if ("audio".equals(arr[1].trim())) { videoFlag = false; } } // ÊÓÆµ¿í¶È if (videoFlag && "width".equals(arr[0])) { videoInfo.setVideoWidth(Integer.valueOf(arr[1])); if (videoInfo.getVideoHeight() != null) { videoInfo.setAspectRatio((float) (videoInfo.getVideoWidth().floatValue() / videoInfo.getVideoHeight().floatValue())); log.debug("¼ÆËã¿í¸ß±È£º" + videoInfo.getVideoWidth() + "/" + videoInfo.getVideoHeight() + ":" + videoInfo.getAspectRatio()); } } // ÊÓÆµ¸ß¶È if (videoFlag && "height".equals(arr[0])) { videoInfo.setVideoHeight(Integer.valueOf(arr[1])); if (videoInfo.getVideoWidth() != null) { videoInfo.setAspectRatio((float) (videoInfo.getVideoWidth().floatValue() / videoInfo.getVideoHeight().floatValue())); log.debug("¼ÆËã¿í¸ß±È£º" + videoInfo.getVideoWidth() + "/" + videoInfo.getVideoHeight() + ":" + videoInfo.getAspectRatio()); } } //»ñÈ¡ÊÓÆÁÖ¡Âʱí´ïʽ if (videoFlag && "avg_frame_rate".equals(arr[0])) { log.info("¿ªÊ¼¼ÆËãÖ¡ÂÊ£¬"+arr[1]); if(StringUtils.isNotEmpty(arr[1])){ String [] strFrameArr = arr[1].split("/"); if(strFrameArr!=null && strFrameArr.length==2 && Double.valueOf(strFrameArr[1].trim())>0){ int frameRate = (int) Math.ceil((Double.valueOf(strFrameArr[0].trim())/Double.valueOf(strFrameArr[1].trim()))); videoInfo.setFrameRate(frameRate); videoInfo.setFrameRateExp(arr[1]); log.info("¼ÆËãÖ¡ÂÊ£ºframeRate="+frameRate+" frameRateExp="+arr[1]); } } } // ÊÓÆµÂëÂÊ if (videoFlag && "bit_rate".equals(arr[0])) { videoInfo.setVideoBitRate(Integer.valueOf(arr[1])); log.debug("ÊÓÆµÂëÂÊ£º" + videoInfo.getVideoBitRate()); } // Îļþʱ³¤ if (videoFlag && "duration".equals(arr[0])) { if (arr[1].contains(".")) { //videoInfo.setPlayTime(Integer.valueOf(arr[1].indexOf('.'))); videoInfo.setPlayTime(Integer.parseInt(arr[1].substring(0, arr[1].indexOf('.')))); } else { videoInfo.setPlayTime(Integer.valueOf(arr[1])); } log.debug("ÊÓÆµÊ±³¤£º" + videoInfo.getPlayTime()); } } } } /** * ½ØÍ¼ * * @param fileVO * @param outputPath * @return */ public static boolean snapshot(FfmpegFileVO fileVO) throws Exception { if (!checkfile(fileVO.getInputPath())) { return false; } try { log.debug("FfmpegMediaHelper--ÊÓÆµ½ØÆÁ..."); String comm = "ffmpeg -ss 00:00:01 -i " + fileVO.getInputPath() + " -t 0.1 -f mjpeg -y " + fileVO.getOutputPath() + "snapshot.jpg"; Process videoProcess = Runtime.getRuntime().exec(comm); new PrintStreamThread(videoProcess.getErrorStream()).start(); videoProcess.waitFor(); log.debug("FfmpegMediaHelper--ÊÓÆµ½ØÆÁÍê³É"); return true; } catch (Exception e) { log.error("ÊÓÆµ½ØÆÁÒì³£" + e.getMessage()); throw e; } } /** * ¸ù¾ÝÖ¡ÂÊ½ØÆÁ * @param fileVO ´ý½ØÍ¼ÊÓÆµÎļþÐÅÏ¢ * @param imgPrefixName ½ØÈ¡Í¼Æ¬Ãû³ÆµÄǰ׺ÐÅÏ¢ * @param framePerSecond ÿÃëͼƬ֡Êý * @param width ½ØÈ¡Í¼Æ¬¿í¶È(ps£ºµ¥Î»ÏñËØµã) * @param height ½ØÈ¡Í¼Æ¬¸ß¶È(ps£ºµ¥Î»ÏñËØµã) * @return ÊÇ·ñ½ØÈ¡³É¹¦ * @throws Exception */ /** * * @param fileVO ÎļþÐÅÏ¢ * @param imgPrefixName ½ØÈ¡Í¼Æ¬µÄǰ׺ÐÅÏ¢ * @param framePerSecond ÿÃë¶àÉÙÖ¡ * @return * @throws Exception */ public static boolean screenShot(FfmpegFileVO fileVO,String imgPrefixName,int framePerSecond,int width,int height) throws Exception { if (!checkfile(fileVO.getInputPath())) { return false; } if(framePerSecond<=0 || width * height<=0){ return false; } Process process = null; try { // ffmpeg -ss 00:00:00.000 -i D:/usr/qxueyou/huifang/test.mp4 -f image2 -vf fps=fps=1/5 D:/usr/qxueyou/huifang/pic/new_%d.jpg String comm = "ffmpeg -ss 00:00:00.000 -i " + fileVO.getInputPath() + " -f image2 -s " + width + "*" + height + " -vf fps=fps=1/"+ framePerSecond +" "+ fileVO.getOutputPath()+"/" + imgPrefixName + "%d.jpg"; log.info("screenShotÃüÁî, "+ comm); process = Runtime.getRuntime().exec(comm); new PrintStreamThread(process.getErrorStream()).start(); process.waitFor(); return true; } catch (Exception e) { log.error("ÊÓÆµ½ØÆÁÒì³£" + e.getMessage()); throw e; }finally { closeProcess(process); } } public static boolean dividVideo(FfmpegFileVO fileVO,int statTime,int duration) throws Exception { if (!checkfile(fileVO.getInputPath())) { return false; } Process process = null; try { String comm = "ffmpeg -ss "+ statTime + " -t " + duration + " -i "+ fileVO.getInputPath() + " -y -vcodec copy -acodec copy " + fileVO.getOutputPath(); log.info("dividVideoÃüÁî, "+ comm); process = Runtime.getRuntime().exec(comm); new PrintStreamThread(process.getErrorStream()).start(); process.waitFor(); return true; } catch (Exception e) { log.error("ÇиîÊÓÆµÊ§°Ü" + e.getMessage()); throw e; }finally { closeProcess(process); } } /** * ºÏ²¢ÊÓÆµ * * @param fileVO * @return * @throws Exception */ public static Process mergeVideo(FfmpegFileVO fileVO) throws Exception { if (!checkfile(fileVO.getInputPath())) { return null; } String comm = "ffmpeg -f concat -safe 0 -i " + fileVO.getInputPath() + " -c copy " + fileVO.getOutputPath(); log.info("mergeVideoÃüÁî, "+ comm); Process videoProcess = Runtime.getRuntime().exec(comm); new PrintStreamThread(videoProcess.getErrorStream()).start(); videoProcess.waitFor(); return videoProcess; } /** * ºÏ²¢ÊÓÆµ * * @param fileVO * @return * @throws Exception */ public static boolean concatVideo(FfmpegFileVO fileVO) throws Exception { Process process = null; try { List command = new ArrayList(20); command.add("ffmpeg"); command.add("-f"); command.add("concat"); command.add("-safe"); command.add("0"); command.add("-i"); command.add( fileVO.getInputPath()); command.add( "-y"); command.add( "-c"); command.add( "copy"); command.add( "-bsf:a"); command.add( "aac_adtstoasc"); command.add( "-f"); command.add( "mp4"); command.add(fileVO.getOutputPath()); ProcessBuilder pBuilder = new ProcessBuilder(command); pBuilder.directory(new File(fileVO.getInputPath()).getParentFile()); log.info("ProcessBuilder directory:"+pBuilder.directory().getAbsolutePath()); process = pBuilder.start(); new PrintStreamThread(process.getErrorStream()).start(); process.waitFor(); return true; } catch (Exception e) { throw e; } finally { closeProcess(process); } } public static String converUploadVideoToLiveMp4(FfmpegFileVO fileVO,FfmpegVideoInfo videoInfo) throws Exception{ String outputPath = null; try { // log.info("FfmpegMediaHelper.TransferUploadVideoToWyMp4,Ö±²¥ÉÏ´«..."); List command = new ArrayList(); String name = UUIDUtils.generateUUID().replace("-", ""); outputPath = fileVO.getOutputPath() +name + ".mp4"; command.add("ffmpeg"); command.add("-i"); command.add(fileVO.getInputPath()); command.add("-q:v"); command.add("6"); // command.add(" -r "); // command.add(videoInfo.getFrameRateExp()); command.add("-pix_fmt"); command.add("yuv420p"); command.add(outputPath); convertMp4(command); log.error(command.toString()); } catch (Exception e) { throw e; } return outputPath; } /** * mp4תtsÎļþ * ffmpeg -i 1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts out1.ts * * @param fileVO * @return * @throws Exception */ public static boolean mp4toannexb(FfmpegFileVO fileVO) throws Exception { Process process= null; try { String comm = "ffmpeg -i " + fileVO.getInputPath() + " -c copy -bsf:v h264_mp4toannexb -y -f mpegts " + fileVO.getOutputPath(); log.info("mp4toannexbÃüÁî, "+ comm); process = Runtime.getRuntime().exec(comm); new PrintStreamThread(process.getErrorStream()).start(); process.waitFor(); return true; } catch (Exception e) { throw e; }finally { closeProcess(process); } } private static void closeProcess(Process process) throws Exception { try { if (process != null) { if (process.getInputStream() != null) { process.getInputStream().close(); } if (process.getOutputStream() != null) { process.getOutputStream().close(); } if (process.getErrorStream() != null) { process.getErrorStream().close(); } process.destroy(); } } catch (IOException e) { log.error("¹Ø±Õprocessʧ°Ü¡£¡£", e); } } /** * ¹Ø±Õ½ø³Ì * * @param process * @return */ public static boolean stop(Process process) { if (process != null) { process.destroy(); return true; } return false; } /** * ½âÎö³ÉMP4¸ñʽ * * @param oldfilepath * @return */ private static boolean convertMp4(List command) throws Exception { Process process = null; try { process = new ProcessBuilder(command).redirectErrorStream(true).start(); new PrintStreamThread(process.getInputStream()).start(); process.waitFor(); return true; } catch (Exception e) { throw e; } finally { if (process != null) { try { process.getInputStream().close(); process.getOutputStream().close(); process.getErrorStream().close(); } catch (IOException e) { throw e; } } } } /** * ½âÎöm3u8¸ñʽ * * @param oldfilepath * @return */ private static boolean convertM3u8(String command) throws Exception { Process process = null; try { process = Runtime.getRuntime().exec(command); new PrintStreamThread(process.getErrorStream()).start(); process.waitFor(); return true; } catch (Exception e) { throw e; } finally { if (process != null) { try { process.getInputStream().close(); process.getOutputStream().close(); process.getErrorStream().close(); } catch (IOException e) { throw e; } } } } /** * ¼ì²éתÂëÊäÈëÎļþÊÇ·ñ´æÔÚ * @param path * @return */ private static boolean checkfile(String path) { File file = new File(path); return file.isFile(); } } /** * ´òÓ¡Ïß³Ì * @author cyq * */ class PrintStreamThread extends Thread { /** * ÊäÈëÁ÷ */ java.io.InputStream __is; /** * ¹¹Ôì·½·¨ * @param is */ public PrintStreamThread(java.io.InputStream is) { __is = is; } /** * Ïß³ÌÖ´Ðз½·¨ */ public void run() { try { while (this != null) { if (__is.read() == -1) { break; } } } catch (Exception e) { e.printStackTrace(); } } }