| | |
| | | import com.qxueyou.scc.teach.live.utils.FfmpegVideoInfo; |
| | | |
| | | /** |
| | | * 视频转码类 |
| | | * 视频转码类 |
| | | * @author cyq |
| | | * |
| | | */ |
| | | public class FfmpegMediaHelper { |
| | | /** |
| | | * 实例化log |
| | | * 实例化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的固定命令 |
| | | * 转mp4的固定命令 |
| | | */ |
| | | private static List<String> commandMp4 = new ArrayList<String>(10); |
| | | /** |
| | | * 转m3u8的固定命令 |
| | | * 转m3u8的固定命令 |
| | | */ |
| | | private static String commandM3u8; |
| | | |
| | | /** |
| | | * 是否为视频 |
| | | * 是否为视频 |
| | | */ |
| | | private static Boolean videoFlag = false; |
| | | |
| | | /** |
| | | * תmp4 |
| | | * 转mp4 |
| | | * @param fileVO |
| | | * @param videoInfo |
| | | * @return |
| | |
| | | return false; |
| | | } |
| | | try { |
| | | System.out.println("FfmpegMediaHelper--转码mp4中..."); |
| | | System.out.println("FfmpegMediaHelper--转码mp4中..."); |
| | | commandMp4.clear(); |
| | | commandMp4.add("ffmpeg"); |
| | | commandMp4.add("-i"); |
| | | commandMp4.add(fileVO.getInputPath()); |
| | | commandMp4.add("-ab"); // 设置音频码率 |
| | | commandMp4.add("-ab"); // 设置音频码率 |
| | | commandMp4.add("128*1024"); |
| | | commandMp4.add("-r"); |
| | | commandMp4.add("23"); |
| | |
| | | } else { |
| | | convertMp4Ld(fileVO, videoInfo); |
| | | } |
| | | System.out.println("FfmpegMediaHelper--转码mp4完成..."); |
| | | System.out.println("FfmpegMediaHelper--转码mp4完成..."); |
| | | return true; |
| | | } catch (Exception e) { |
| | | System.out.println("FfmpegMediaHelper--转码mp4异常..."); |
| | | log.error("转码mp4异常:" + e.getMessage()); |
| | | System.out.println("FfmpegMediaHelper--转码mp4异常..."); |
| | | log.error("转码mp4异常:" + e.getMessage()); |
| | | throw e; |
| | | } |
| | | } |
| | | /** |
| | | * תm3u8 |
| | | * 转m3u8 |
| | | * @param fileVO |
| | | * @param videoInfo |
| | | * @return |
| | |
| | | return false; |
| | | } |
| | | try { |
| | | System.out.println("FfmpegMediaHelper--转码m3h8中..."); |
| | | 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) { |
| | |
| | | } else { |
| | | convertM3u8Ld(fileVO, videoInfo); |
| | | } |
| | | System.out.println("FfmpegMediaHelper--转码m3u8完成..."); |
| | | System.out.println("FfmpegMediaHelper--转码m3u8完成..."); |
| | | return true; |
| | | } catch (Exception e) { |
| | | System.out.println("FfmpegMediaHelper--转码m3u8异常..."); |
| | | log.error("转码m3u8异常:" + e.getMessage()); |
| | | System.out.println("FfmpegMediaHelper--转码m3u8异常..."); |
| | | log.error("转码m3u8异常:" + e.getMessage()); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 转高清mp4 |
| | | * 转高清mp4 |
| | | */ |
| | | private static void convertMp4Hd(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { |
| | | try { |
| | | String resolution = (int) (1080 * videoInfo.getAspectRatio() / 2) * 2 + "*1080"; // 宽高必须是偶数 |
| | | String resolution = (int) (1080 * videoInfo.getAspectRatio() / 2) * 2 + "*1080"; // 宽高必须是偶数 |
| | | List<String> command = new ArrayList<String>(commandMp4); |
| | | if (videoInfo.getVideoBitRate() == null || videoInfo.getVideoBitRate() > bitRateHd) { |
| | | command.add("-vb"); // 视频码率 |
| | | command.add("-vb"); // 视频码率 |
| | | command.add(String.valueOf(bitRateHd)); |
| | | } |
| | | command.add("-s"); // 尺寸(分辨率) |
| | | 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中 |
| | | videoInfo.setMp4HdUrl(fileVO.getOutputPath() + videoInfo.getUuid() + "mp4-hd.mp4"); // 转码成功,将路径设置到videoInfo中 |
| | | } catch (Exception e) { |
| | | throw e; |
| | | } |
| | | } |
| | | /** |
| | | * 转高清m3u8 |
| | | * 转高清m3u8 |
| | | */ |
| | | private static void convertM3u8Hd(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { |
| | | try { |
| | | String resolution = (int) (1080 * videoInfo.getAspectRatio() / 2) * 2 + "*1080"; // 宽高必须是偶数 |
| | | String resolution = (int) (1080 * videoInfo.getAspectRatio() / 2) * 2 + "*1080"; // 宽高必须是偶数 |
| | | |
| | | StringBuffer commandBuffer = new StringBuffer(commandM3u8); |
| | | if (videoInfo.getVideoBitRate() == null || videoInfo.getVideoBitRate() > bitRateHd) { |
| | |
| | | } |
| | | |
| | | /** |
| | | * 转标清mp4 |
| | | * 转标清mp4 |
| | | */ |
| | | private static void convertMp4Sd(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { |
| | | try { |
| | |
| | | } |
| | | } |
| | | /** |
| | | * 转标清m3u8 |
| | | * 转标清m3u8 |
| | | */ |
| | | private static void convertM3u8Sd(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { |
| | | try { |
| | |
| | | } |
| | | |
| | | /** |
| | | * 转低清mp4 |
| | | * 转低清mp4 |
| | | */ |
| | | private static void convertMp4Ld(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { |
| | | try { |
| | |
| | | } |
| | | } |
| | | /** |
| | | * 转低清m3u8 |
| | | * 转低清m3u8 |
| | | */ |
| | | private static void convertM3u8Ld(FfmpegFileVO fileVO, FfmpegVideoInfo videoInfo) throws Exception { |
| | | try { |
| | |
| | | } |
| | | |
| | | /** |
| | | * 获取媒体信息 |
| | | * 获取媒体信息 |
| | | * |
| | | * @param fileVO |
| | | * @param videoInfo |
| | |
| | | if (!checkfile(fileVO.getInputPath())) { |
| | | return false; |
| | | } |
| | | BufferedReader buf = null; // 保存ffmpeg的输出结果流 |
| | | BufferedReader buf = null; // 保存ffmpeg的输出结果流 |
| | | String currLine = null; |
| | | String line = null; |
| | | try { |
| | | |
| | | log.debug("获取媒体信息..."); |
| | | log.debug("获取媒体信息..."); |
| | | List<String> command = new ArrayList<String>(); |
| | | |
| | | // ffprobe 命令获取媒体信息 |
| | | // ffprobe 命令获取媒体信息 |
| | | command.add("ffprobe"); |
| | | command.add("-print_format"); |
| | | |
| | | // json格式输出 |
| | | // json格式输出 |
| | | command.add("json"); |
| | | |
| | | command.add("-show_streams"); |
| | |
| | | |
| | | buf = new BufferedReader(new InputStreamReader(p.getInputStream())); |
| | | while ((currLine = buf.readLine()) != null) { |
| | | // 处理多余字符 |
| | | // 处理多余字符 |
| | | line = currLine.trim().replaceAll("\"", "").replaceAll(",", "").replace(" ", ""); |
| | | handleLine(line, videoInfo); |
| | | |
| | | // 获取文件时长(flv需要这么取) |
| | | // 获取文件时长(flv需要这么取) |
| | | if(currLine.trim().startsWith("Duration:") && currLine.contains(",") ){ |
| | | String strFirst = currLine.trim().split(",")[0].substring(9).trim().substring(0,8); |
| | | String[] arrTime = strFirst.split(":"); |
| | |
| | | Integer iSecond = Integer.parseInt(arrTime[2]); |
| | | |
| | | videoInfo.setPlayTime(iHour * 60 * 60 + iMinute * 60 + iSecond); |
| | | log.debug("视频时长:" + videoInfo.getPlayTime()); |
| | | log.debug("视频时长:" + videoInfo.getPlayTime()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | p.waitFor();// 这里线程阻塞,将等待外部转换进程运行成功运行结束后,才往下执行 |
| | | p.waitFor();// 这里线程阻塞,将等待外部转换进程运行成功运行结束后,才往下执行 |
| | | |
| | | log.debug("获取媒体信息完成"); |
| | | log.debug("获取媒体信息完成"); |
| | | return true; |
| | | |
| | | } catch (Exception e) { |
| | | log.debug("获取媒体信息异常:" + e.getMessage()); |
| | | log.debug("获取媒体信息异常:" + e.getMessage()); |
| | | throw e; |
| | | } |
| | | } |
| | |
| | | if (line.contains(":")) { |
| | | String[] arr = line.split(":"); |
| | | if (null != arr && arr.length == 2) { |
| | | // 1. 如果是标志位 |
| | | // 1. 如果是标志位 |
| | | 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()); |
| | | 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()); |
| | | log.debug("计算宽高比:" + videoInfo.getVideoWidth() + "/" + videoInfo.getVideoHeight() + ":" + videoInfo.getAspectRatio()); |
| | | } |
| | | } |
| | | |
| | | //获取视屏帧率表达式 |
| | | //获取视屏帧率表达式 |
| | | if (videoFlag && "avg_frame_rate".equals(arr[0])) { |
| | | log.info("开始计算帧率,"+arr[1]); |
| | | 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]); |
| | | log.info("计算帧率:frameRate="+frameRate+" frameRateExp="+arr[1]); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // 视频码率 |
| | | // 视频码率 |
| | | if (videoFlag && "bit_rate".equals(arr[0])) { |
| | | videoInfo.setVideoBitRate(Integer.valueOf(arr[1])); |
| | | log.debug("视频码率:" + videoInfo.getVideoBitRate()); |
| | | log.debug("视频码率:" + videoInfo.getVideoBitRate()); |
| | | } |
| | | // 文件时长 |
| | | // 文件时长 |
| | | if (videoFlag && "duration".equals(arr[0])) { |
| | | if (arr[1].contains(".")) { |
| | | //videoInfo.setPlayTime(Integer.valueOf(arr[1].indexOf('.'))); |
| | |
| | | } else { |
| | | videoInfo.setPlayTime(Integer.valueOf(arr[1])); |
| | | } |
| | | log.debug("视频时长:" + videoInfo.getPlayTime()); |
| | | log.debug("视频时长:" + videoInfo.getPlayTime()); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * 截图 |
| | | * 截图 |
| | | * |
| | | * @param fileVO |
| | | * @param outputPath |
| | |
| | | } |
| | | try { |
| | | |
| | | log.debug("FfmpegMediaHelper--视频截屏..."); |
| | | 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--视频截屏完成"); |
| | | log.debug("FfmpegMediaHelper--视频截屏完成"); |
| | | return true; |
| | | } catch (Exception e) { |
| | | log.error("视频截屏异常" + e.getMessage()); |
| | | log.error("视频截屏异常" + e.getMessage()); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据帧率截屏 |
| | | * @param fileVO 待截图视频文件信息 |
| | | * @param imgPrefixName 截取图片名称的前缀信息 |
| | | * @param framePerSecond 每秒图片帧数 |
| | | * @param width 截取图片宽度(ps:单位像素点) |
| | | * @param height 截取图片高度(ps:单位像素点) |
| | | * @return 是否截取成功 |
| | | * 根据帧率截屏 |
| | | * @param fileVO 待截图视频文件信息 |
| | | * @param imgPrefixName 截取图片名称的前缀信息 |
| | | * @param framePerSecond 每秒图片帧数 |
| | | * @param width 截取图片宽度(ps:单位像素点) |
| | | * @param height 截取图片高度(ps:单位像素点) |
| | | * @return 是否截取成功 |
| | | * @throws Exception |
| | | */ |
| | | |
| | | /** |
| | | * |
| | | * @param fileVO 文件信息 |
| | | * @param imgPrefixName 截取图片的前缀信息 |
| | | * @param framePerSecond 每秒多少帧 |
| | | * @param fileVO 文件信息 |
| | | * @param imgPrefixName 截取图片的前缀信息 |
| | | * @param framePerSecond 每秒多少帧 |
| | | * @return |
| | | * @throws Exception |
| | | */ |
| | |
| | | // 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); |
| | | 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()); |
| | | log.error("视频截屏异常" + e.getMessage()); |
| | | throw e; |
| | | }finally { |
| | | closeProcess(process); |
| | |
| | | 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); |
| | | 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()); |
| | | log.error("切割视频失败" + e.getMessage()); |
| | | throw e; |
| | | }finally { |
| | | closeProcess(process); |
| | |
| | | |
| | | |
| | | /** |
| | | * 合并视频 |
| | | * 合并视频 |
| | | * |
| | | * @param fileVO |
| | | * @return |
| | |
| | | } |
| | | |
| | | String comm = "ffmpeg -f concat -safe 0 -i " + fileVO.getInputPath() + " -c copy " + fileVO.getOutputPath(); |
| | | log.info("mergeVideo命令, "+ comm); |
| | | log.info("mergeVideo命令, "+ comm); |
| | | |
| | | Process videoProcess = Runtime.getRuntime().exec(comm); |
| | | new PrintStreamThread(videoProcess.getErrorStream()).start(); |
| | |
| | | |
| | | |
| | | /** |
| | | * 合并视频 |
| | | * 合并视频 |
| | | * |
| | | * @param fileVO |
| | | * @return |
| | |
| | | public static String converUploadVideoToLiveMp4(FfmpegFileVO fileVO,FfmpegVideoInfo videoInfo) throws Exception{ |
| | | String outputPath = null; |
| | | try { |
| | | // log.info("FfmpegMediaHelper.TransferUploadVideoToWyMp4,直播上传..."); |
| | | // log.info("FfmpegMediaHelper.TransferUploadVideoToWyMp4,直播上传..."); |
| | | List<String> command = new ArrayList<String>(); |
| | | String name = UUIDUtils.generateUUID().replace("-", ""); |
| | | outputPath = fileVO.getOutputPath() +name + ".mp4"; |
| | |
| | | } |
| | | |
| | | /** |
| | | * mp4转ts文件 |
| | | * mp4转ts文件 |
| | | * ffmpeg -i 1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts out1.ts |
| | | * |
| | | * @param fileVO |
| | |
| | | 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); |
| | | log.info("mp4toannexb命令, "+ comm); |
| | | process = Runtime.getRuntime().exec(comm); |
| | | new PrintStreamThread(process.getErrorStream()).start(); |
| | | process.waitFor(); |
| | |
| | | process.destroy(); |
| | | } |
| | | } catch (IOException e) { |
| | | log.error("关闭process失败。。", e); |
| | | log.error("关闭process失败。。", e); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 关闭进程 |
| | | * 关闭进程 |
| | | * |
| | | * @param process |
| | | * @return |
| | |
| | | } |
| | | |
| | | /** |
| | | * 解析成MP4格式 |
| | | * 解析成MP4格式 |
| | | * |
| | | * @param oldfilepath |
| | | * @return |
| | |
| | | } |
| | | |
| | | /** |
| | | * 解析m3u8格式 |
| | | * 解析m3u8格式 |
| | | * |
| | | * @param oldfilepath |
| | | * @return |
| | |
| | | } |
| | | |
| | | /** |
| | | * 检查转码输入文件是否存在 |
| | | * 检查转码输入文件是否存在 |
| | | * @param path |
| | | * @return |
| | | */ |
| | |
| | | } |
| | | } |
| | | /** |
| | | * 打印线程 |
| | | * 打印线程 |
| | | * @author cyq |
| | | * |
| | | */ |
| | | class PrintStreamThread extends Thread { |
| | | /** |
| | | * 输入流 |
| | | * 输入流 |
| | | */ |
| | | java.io.InputStream __is; |
| | | |
| | | /** |
| | | * 构造方法 |
| | | * 构造方法 |
| | | * @param is |
| | | */ |
| | | public PrintStreamThread(java.io.InputStream is) { |
| | |
| | | } |
| | | |
| | | /** |
| | | * 线程执行方法 |
| | | * 线程执行方法 |
| | | */ |
| | | public void run() { |
| | | try { |