package com.goafanti.admin.controller; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.math.BigInteger; import java.nio.channels.FileChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Calendar; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import net.bramp.ffmpeg.FFmpeg; import net.bramp.ffmpeg.FFmpegExecutor; import net.bramp.ffmpeg.FFmpegUtils; import net.bramp.ffmpeg.FFprobe; import net.bramp.ffmpeg.builder.FFmpegBuilder; import net.bramp.ffmpeg.job.FFmpegJob; import net.bramp.ffmpeg.probe.FFmpegProbeResult; import net.bramp.ffmpeg.progress.Progress; import net.bramp.ffmpeg.progress.ProgressListener; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.goafanti.admin.service.AdminVideoService; import com.goafanti.common.bo.Result; import com.goafanti.common.constant.ErrorConstants; import com.goafanti.common.controller.CertifyApiController; import com.goafanti.common.enums.AttachmentType; import com.goafanti.common.enums.VideoFields; import com.goafanti.common.model.JtVideo; import com.goafanti.common.utils.StringUtils; import com.goafanti.core.shiro.token.TokenManager; @Controller @RequestMapping(value = "/api/admin/video") public class AdminVideoApiController extends CertifyApiController { @Resource private AdminVideoService adminVideoService; @Value(value = "${ffmpeg.path}") private String ffmpegPath = null; @Value(value = "${ffprobe.path}") private String ffprobePath = null; @Value(value = "${video.temppath}") private String videoTemppath = null; // 上传视频只做MP4格式的 @RequestMapping(value = "/upload", method = RequestMethod.POST) @ResponseBody public Result uploadPicture(HttpServletRequest req, String sign) { Result res = new Result(); AttachmentType attachmentType = AttachmentType.getField(sign); if (attachmentType == AttachmentType.VIDEO) { // 先将视频存在本地文件中 String dir = videoTemppath + "/convert_before"; String filename = handleVideoFiles(res, req, dir); res.setData(filename); } else if (attachmentType == AttachmentType.VIDEO_COVER) { String picturebase = req.getParameter("picturebase"); String filename = req.getParameter("filename"); byte[] bs = Base64.getDecoder().decode(picturebase); filename = System.nanoTime() + filename.substring(filename.indexOf(".")); res.setData(handleBaseFiles(res, "/video_cover/", false, bs, sign, TokenManager.getUserId(), filename)); } else { res.getError().add( buildError(ErrorConstants.PARAM_ERROR, "", "文件标示")); } return res; } private String createFileName() { // 年月日 Calendar now = Calendar.getInstance(); int year = now.get(Calendar.YEAR); int month = now.get(Calendar.MONTH) + 1; int day = now.get(Calendar.DAY_OF_MONTH); String dir = "/" + year + "/" + month + "/" + day; return dir; } // 查看所有视频信息 @RequestMapping(value = "/getVideoList", method = RequestMethod.GET) @ResponseBody public Result getVideoList(JtVideo video, Integer pageNo, Integer pageSize) { Result result = new Result(); result.setData(adminVideoService.getVideoList(video, pageNo, pageSize,1)); return result; } // 查看单个信息 @RequestMapping(value = "/getVideoById", method = RequestMethod.GET) @ResponseBody public Result getVideoById(String id) { Result result = new Result(); JtVideo video = new JtVideo(); video.setId(id); result.setData(adminVideoService.getVideoById(video)); return result; } // 新增视频信息 @RequestMapping(value = "/insertVideo", method = RequestMethod.POST) @ResponseBody public Result insertVideo(@Valid JtVideo video , BindingResult bindingResult) throws IOException, InterruptedException, ExecutionException { // 数据限制 Result result = new Result(); if (bindingResult.hasErrors()) { result.getError().add(buildErrorByMsg(bindingResult.getFieldError().getDefaultMessage(), VideoFields.getFieldDesc(bindingResult.getFieldError().getField()))); return result; } if(StringUtils.isBlank(video.getName())){ result.getError().add( buildError( "视频名称不能是空", "视频名称不能是空")); return result; } //名称重名限制 if(adminVideoService.nameCheck(video.getName().trim()) > 0){ result.getError().add( buildError(ErrorConstants.PARAM_ERROR, "", "视频名称重复不能新增")); return result; } String filename = video.getUrl(); // 生成保存到数据库的url String output = System.nanoTime() + ".mp4"; String filepath = createFileName(); video.setUrl(filepath + "/" + output); video.setTranscoding(1); // 保存到数据库 result.setData(adminVideoService.insertVideo(video)); // 压缩视频并将视频存储 FileChannel fc= null; try { File f= new File(filename); if (f.exists() && f.isFile()){ FileInputStream fis= new FileInputStream(f); fc= fis.getChannel(); Long fsize = fc.size()/1024/1024; System.out.println(fc.size()); if(fsize.longValue() > 50){ videoChange(filename, output, video.getUrl(), videoTemppath + "/convert_after" + filepath ); }else{ copy(filename, output, videoTemppath + "/convert_after" + filepath + "/" ); //修改数据库 updateSql(video.getUrl(), videoTemppath + "/convert_after" + filepath ,output); } }else{ System.out.println("file doesn't exist or is not a file"); } } catch (FileNotFoundException e) { System.out.println(e); } catch (IOException e) { System.out.println(e); } finally { if (null!=fc){ try{ fc.close(); }catch(IOException e){ System.out.println(e); } } } return result; } // 修改视频 @RequestMapping(value = "/updateVideo", method = RequestMethod.POST) @ResponseBody public Result updateVideo(@Valid JtVideo video , BindingResult bindingResult) throws IOException, InterruptedException, ExecutionException { Result result = new Result(); if (bindingResult.hasErrors()) { result.getError().add(buildErrorByMsg(bindingResult.getFieldError().getDefaultMessage(), VideoFields.getFieldDesc(bindingResult.getFieldError().getField()))); return result; } JtVideo v = new JtVideo(); v.setId(video.getId()); JtVideo oldVideo = adminVideoService.getVideoById(v); if (StringUtils.isBlank(video.getId())) { result.getError().add( buildError(ErrorConstants.PARAM_ERROR, "", "视频id不能为空")); return result; } else if (null == oldVideo) { result.getError().add( buildError(ErrorConstants.PARAM_ERROR, "", "视频id不存在")); return result; } if(null != video.getName() && "".equals(video.getName().trim())){ result.getError().add( buildError( "视频名称不能是空", "视频名称不能是空")); return result; } //如果是删除视频 if(null != video.getStatus() && video.getStatus() == 3){ if(oldVideo.getStatus() == 1){ result.getError().add( buildError( "视频已经发布不能删除", "视频已经发布不能删除")); return result; }else{ deleteFtpVideo( videoTemppath + "/convert_after" + oldVideo.getUrl()); } } Boolean flag = false; String filepath = ""; String output = ""; if (StringUtils.isNotBlank(video.getUrl()) && !video.getUrl().equals(oldVideo.getUrl())) { // 如果修改了视频 video.setTranscoding(1); // 压缩视频并将视频 String filename = video.getUrl(); output = System.nanoTime() + ".mp4"; filepath = createFileName() ; video.setUrl(filepath + "/" + output); FileChannel fc= null; try { File f= new File(filename); if (f.exists() && f.isFile()){ FileInputStream fis= new FileInputStream(f); fc= fis.getChannel(); Long fsize = fc.size()/1024/1024; System.out.println(fc.size()); if(fsize.longValue() > 50){ videoChange(filename, output, video.getUrl(), videoTemppath + "/convert_after" + filepath ); }else{ copy(filename, output, videoTemppath + "/convert_after" + filepath + "/" ); flag = true; } }else{ System.out.println("file doesn't exist or is not a file"); } } catch (FileNotFoundException e) { System.out.println(e); } catch (IOException e) { System.out.println(e); } finally { if (null!=fc){ try{ fc.close(); }catch(IOException e){ System.out.println(e); } } } // videoChange(filename, output, video.getUrl(), videoTemppath + "/convert_after" + filepath); //删除原来远程的视频 deleteFtpVideo(videoTemppath + "/convert_after" + oldVideo.getUrl()); }else{ video.setUrl(null); } result.setData(adminVideoService.updateVideo(video)); if(flag){ updateSql(video.getUrl(), videoTemppath + "/convert_after" + filepath ,output); } return result; } private Boolean deleteFtpVideo(String url){ boolean b = false; //判断文件是否存在 File f= new File(url); if(f.exists()){ System.out.println("+++++++++++++++++++++++++++++文件存在,删除视频"+url); b = f.delete(); } return b; } // 将文件转码 private String videoChange(String input, String output, String url,String outputdir) throws IOException, InterruptedException, ExecutionException { File file = new File(outputdir); if(!file.isDirectory()){ file.mkdirs();//创建文件夹 } FFmpeg ffmpeg = new FFmpeg(ffmpegPath); FFprobe ffprobe = new FFprobe(ffprobePath); FFmpegProbeResult in = ffprobe.probe(input); FFmpegBuilder builder = new FFmpegBuilder(); builder.setInput(in) // 输入文件 .overrideOutputFiles(true) // 覆盖重复文件 .addOutput(outputdir + "/" + output) // 输出文件 .setFormat("mp4") // 设置格式 .setTargetSize(250_000) // 目标大小 .disableSubtitle() // 没有子标题 .setAudioChannels(1) // 声道 .setAudioSampleRate(48_000) // 音频采样率 .setAudioBitRate(32768) // 音频传输率 .setVideoCodec("libx264") // 视频编解码器 .setVideoFrameRate(24, 1) // 视频帧速率 .setVideoResolution(640, 480) // 视频分辨率 .setStrict(FFmpegBuilder.Strict.EXPERIMENTAL) // 严格形式 .done(); FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe); ExecutorService service = Executors.newCachedThreadPool(); // 3.直接new一个FutureTask SubTask subTask = new SubTask(executor, builder, in, url, input, output, outputdir); FutureTask result = new FutureTask(subTask); // 4.提交任务 service.submit(result); // 5.关闭线程池 service.shutdown(); // LOGGER.info("=============返回给前端============="); //System.out.println("task运行结果" + result.get()); return "SUCESS"; } private void updateSql(String url, String tempdir, String filename) { // 修改数据 File file = new File(tempdir+"/"+filename); JtVideo video = new JtVideo(); video.setMd5(FileMD5(file)); video.setTranscoding(2); video.setUrl(url); url = url.substring(0,url.lastIndexOf("/")); System.out.println("修改数据库000000000000000000000"+url); adminVideoService.updateByUrl(video);//修改状态 } // 获得文件md5 private String FileMD5(File file) { try { FileInputStream fis = new FileInputStream(file); MessageDigest md = MessageDigest.getInstance("MD5"); byte[] buffer = new byte[1024]; int length = -1; while ((length = fis.read(buffer, 0, 1024)) != -1) { md.update(buffer, 0, length); } BigInteger bigInt = new BigInteger(1, md.digest()); return bigInt.toString(16); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } class SubTask implements Callable { private Boolean state = false; private FFmpegExecutor executor; private FFmpegBuilder builder; private FFmpegProbeResult probeResult; private String url; private String input; private String output; private String tempdir; public SubTask(FFmpegExecutor executor, FFmpegBuilder builder, FFmpegProbeResult probeResult, String url, String input, String output, String tempdir) { this.executor = executor; this.builder = builder; this.probeResult = probeResult; this.url = url; this.input = input; this.output = output; this.tempdir = tempdir; } public FFmpegExecutor getExecutor() { return executor; } public FFmpegBuilder getBuilder() { return builder; } public String getUrl() { return url; } public String getInput() { return input; } public String getOutput() { return output; } public Boolean getState() { return state; } @Override public Boolean call() throws Exception { final double duration_ns = probeResult.getFormat().duration * TimeUnit.SECONDS.toNanos(1); try { FFmpegJob job = executor.createJob(builder, new ProgressListener() { @Override public void progress(Progress progress) { double percentage = progress.out_time_ns / duration_ns; System.out.println(String .format("[%.0f%%] status:%s frame:%d time:%s ms fps:%.0f speed:%.2fx", percentage * 100, progress.status, progress.frame, FFmpegUtils.toTimecode( progress.out_time_ns, TimeUnit.NANOSECONDS), progress.fps.doubleValue(), progress.speed)); } }); job.run(); // 6.阻塞 call // System.out.println("设置 sate"); // latch.wait(); state = true; updateSql(url, tempdir,output); // 删除未压缩的视频 File f1 = new File(input); f1.delete(); } catch (Exception e) { e.printStackTrace(); } return state; } } public static void copy(String file1, String file2, String dir) { File file = new File(dir); if(!file.isDirectory()){ file.mkdirs();//创建文件夹 } System.out.println(file1); System.out.println(file2); File src = new File(file1); InputStream in = null; OutputStream out = null; System.out.println(file1.substring(file1.lastIndexOf("/"),file1.length()));//获取单个文件的源文件的名称 try { in = new BufferedInputStream(new FileInputStream(src), 16 * 1024); FileOutputStream f = new FileOutputStream(dir+file2);// 一定要加上文件名称 out = new BufferedOutputStream(f, 16 * 1024); byte[] buffer = new byte[16 * 1024]; int len = 0; while ((len = in.read(buffer)) > 0) { out.write(buffer, 0, len); } } catch (Exception e) { e.printStackTrace(); } finally { if (null != in) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (null != out) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) { copy("D:/Downloads/环保小视频.mp4","haha.mp4","F:/data/local/video/convert_after/2018/11/9/"); } }