AdminVideoApiController.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. package com.goafanti.admin.controller;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileNotFoundException;
  5. import java.io.IOException;
  6. import java.math.BigInteger;
  7. import java.security.MessageDigest;
  8. import java.security.NoSuchAlgorithmException;
  9. import java.util.Base64;
  10. import java.util.Calendar;
  11. import java.util.concurrent.Callable;
  12. import java.util.concurrent.ExecutionException;
  13. import java.util.concurrent.ExecutorService;
  14. import java.util.concurrent.Executors;
  15. import java.util.concurrent.FutureTask;
  16. import java.util.concurrent.TimeUnit;
  17. import javax.annotation.Resource;
  18. import javax.servlet.http.HttpServletRequest;
  19. import javax.validation.Valid;
  20. import net.bramp.ffmpeg.FFmpeg;
  21. import net.bramp.ffmpeg.FFmpegExecutor;
  22. import net.bramp.ffmpeg.FFmpegUtils;
  23. import net.bramp.ffmpeg.FFprobe;
  24. import net.bramp.ffmpeg.builder.FFmpegBuilder;
  25. import net.bramp.ffmpeg.job.FFmpegJob;
  26. import net.bramp.ffmpeg.probe.FFmpegProbeResult;
  27. import net.bramp.ffmpeg.progress.Progress;
  28. import net.bramp.ffmpeg.progress.ProgressListener;
  29. import org.springframework.beans.factory.annotation.Value;
  30. import org.springframework.stereotype.Controller;
  31. import org.springframework.validation.BindingResult;
  32. import org.springframework.web.bind.annotation.RequestMapping;
  33. import org.springframework.web.bind.annotation.RequestMethod;
  34. import org.springframework.web.bind.annotation.ResponseBody;
  35. import com.goafanti.admin.service.AdminVideoService;
  36. import com.goafanti.common.bo.Result;
  37. import com.goafanti.common.constant.ErrorConstants;
  38. import com.goafanti.common.controller.CertifyApiController;
  39. import com.goafanti.common.enums.AttachmentType;
  40. import com.goafanti.common.enums.VideoFields;
  41. import com.goafanti.common.model.JtVideo;
  42. import com.goafanti.common.utils.FtpUtils;
  43. import com.goafanti.common.utils.StringUtils;
  44. import com.goafanti.core.shiro.token.TokenManager;
  45. @Controller
  46. @RequestMapping(value = "/api/admin/video")
  47. public class AdminVideoApiController extends CertifyApiController {
  48. @Resource
  49. private AdminVideoService adminVideoService;
  50. @Value(value = "${ffmpeg.path}")
  51. private String ffmpegPath = null;
  52. @Value(value = "${ffprobe.path}")
  53. private String ffprobePath = null;
  54. @Value(value = "${video.path}")
  55. private String videoPath = null;
  56. @Value(value = "${video.temppath}")
  57. private String videoTemppath = null;
  58. // 上传视频只做MP4格式的
  59. @RequestMapping(value = "/upload", method = RequestMethod.POST)
  60. @ResponseBody
  61. public Result uploadPicture(HttpServletRequest req, String sign) {
  62. Result res = new Result();
  63. AttachmentType attachmentType = AttachmentType.getField(sign);
  64. if (attachmentType == AttachmentType.VIDEO) {
  65. // 先将视频存在本地文件中
  66. String dir = videoTemppath + "/convert_before";
  67. String filename = handleVideoFiles(res, req, dir);
  68. res.setData(filename);
  69. } else if (attachmentType == AttachmentType.VIDEO_COVER) {
  70. String picturebase = req.getParameter("picturebase");
  71. String filename = req.getParameter("filename");
  72. byte[] bs = Base64.getDecoder().decode(picturebase);
  73. filename = System.nanoTime()
  74. + filename.substring(filename.indexOf("."));
  75. res.setData(handleBaseFiles(res, "/video_cover/", false, bs, sign,
  76. TokenManager.getUserId(), filename));
  77. } else {
  78. res.getError().add(
  79. buildError(ErrorConstants.PARAM_ERROR, "", "文件标示"));
  80. }
  81. return res;
  82. }
  83. private String createFileName(String local) {
  84. // 年月日
  85. Calendar now = Calendar.getInstance();
  86. int year = now.get(Calendar.YEAR);
  87. int month = now.get(Calendar.MONTH) + 1;
  88. int day = now.get(Calendar.DAY_OF_MONTH);
  89. String dir = "/" + local + "/" + year + "/" + month + "/" + day;
  90. return dir;
  91. }
  92. // 查看所有视频信息
  93. @RequestMapping(value = "/getVideoList", method = RequestMethod.GET)
  94. @ResponseBody
  95. public Result getVideoList(JtVideo video, Integer pageNo, Integer pageSize) {
  96. Result result = new Result();
  97. result.setData(adminVideoService.getVideoList(video, pageNo, pageSize,1));
  98. return result;
  99. }
  100. // 查看单个信息
  101. @RequestMapping(value = "/getVideoById", method = RequestMethod.GET)
  102. @ResponseBody
  103. public Result getVideoById(String id) {
  104. Result result = new Result();
  105. result.setData(adminVideoService.getVideoById(id));
  106. return result;
  107. }
  108. // 新增视频信息
  109. @RequestMapping(value = "/insertVideo", method = RequestMethod.POST)
  110. @ResponseBody
  111. public Result insertVideo(@Valid JtVideo video , BindingResult bindingResult) throws IOException,
  112. InterruptedException, ExecutionException {
  113. // 数据限制
  114. Result result = new Result();
  115. if (bindingResult.hasErrors()) {
  116. result.getError().add(buildErrorByMsg(bindingResult.getFieldError().getDefaultMessage(),
  117. VideoFields.getFieldDesc(bindingResult.getFieldError().getField())));
  118. return result;
  119. }
  120. if(StringUtils.isBlank(video.getName())){
  121. result.getError().add(
  122. buildError( "视频名称不能是空", "视频名称不能是空"));
  123. return result;
  124. }
  125. //名称重名限制
  126. if(adminVideoService.nameCheck(video.getName().trim()) > 0){
  127. result.getError().add(
  128. buildError(ErrorConstants.PARAM_ERROR, "", "视频名称重复不能新增"));
  129. return result;
  130. }
  131. String filename = video.getUrl();
  132. // 生成保存到数据库的url
  133. String output = System.nanoTime() + ".mp4";
  134. video.setUrl(createFileName(videoPath) + "/" + output);
  135. video.setTranscoding(1);
  136. // 保存到数据库
  137. result.setData(adminVideoService.insertVideo(video));
  138. // 压缩视频并将视频
  139. //output = videoTemppath + "/convert_after";
  140. videoChange(filename, output, video.getUrl(), videoTemppath + "/convert_after");
  141. return result;
  142. }
  143. // 修改视频
  144. @RequestMapping(value = "/updateVideo", method = RequestMethod.POST)
  145. @ResponseBody
  146. public Result updateVideo(@Valid JtVideo video , BindingResult bindingResult) throws IOException,
  147. InterruptedException, ExecutionException {
  148. Result result = new Result();
  149. if (bindingResult.hasErrors()) {
  150. result.getError().add(buildErrorByMsg(bindingResult.getFieldError().getDefaultMessage(),
  151. VideoFields.getFieldDesc(bindingResult.getFieldError().getField())));
  152. return result;
  153. }
  154. JtVideo oldVideo = adminVideoService.getVideoById(video.getId());
  155. if (StringUtils.isBlank(video.getId())) {
  156. result.getError().add(
  157. buildError(ErrorConstants.PARAM_ERROR, "", "视频id不能为空"));
  158. return result;
  159. } else if (null == oldVideo) {
  160. result.getError().add(
  161. buildError(ErrorConstants.PARAM_ERROR, "", "视频id不存在"));
  162. return result;
  163. }
  164. if(null != video.getName() && "".equals(video.getName().trim())){
  165. result.getError().add(
  166. buildError( "视频名称不能是空", "视频名称不能是空"));
  167. return result;
  168. }
  169. //如果是删除视频
  170. if(null != video.getStatus() && video.getStatus() == 3){
  171. if(oldVideo.getStatus() == 1){
  172. result.getError().add(
  173. buildError( "视频已经发布不能删除", "视频已经发布不能删除"));
  174. return result;
  175. }else{
  176. deleteFtpVideo(oldVideo.getUrl());
  177. }
  178. }
  179. if (StringUtils.isNotBlank(video.getUrl()) && !video.getUrl().equals(oldVideo.getUrl())) { // 如果修改了视频
  180. video.setTranscoding(1);
  181. // 压缩视频并将视频
  182. String filename = video.getUrl();
  183. String output = System.nanoTime() + ".mp4";
  184. video.setUrl(createFileName(videoPath) + "/" + output);
  185. videoChange(filename, output, video.getUrl(), videoTemppath + "/convert_after");
  186. //删除原来远程的视频
  187. deleteFtpVideo(oldVideo.getUrl());
  188. }else{
  189. video.setUrl(null);
  190. }
  191. result.setData(adminVideoService.updateVideo(video));
  192. return result;
  193. }
  194. private Boolean deleteFtpVideo(String url){
  195. FtpUtils ftp = new FtpUtils();
  196. return ftp.deleteFile("",url);
  197. }
  198. // 将文件转码
  199. private String videoChange(String input, String output, String url,String outputdir) throws IOException,
  200. InterruptedException, ExecutionException {
  201. FFmpeg ffmpeg = new FFmpeg(ffmpegPath);
  202. FFprobe ffprobe = new FFprobe(ffprobePath);
  203. FFmpegProbeResult in = ffprobe.probe(input);
  204. FFmpegBuilder builder = new FFmpegBuilder();
  205. builder.setInput(in) // 输入文件
  206. .overrideOutputFiles(true) // 覆盖重复文件
  207. .addOutput(outputdir+"/"+output) // 输出文件
  208. .setFormat("mp4") // 设置格式
  209. .setTargetSize(250_000) // 目标大小
  210. .disableSubtitle() // 没有子标题
  211. .setAudioChannels(1) // 声道
  212. .setAudioSampleRate(48_000) // 音频采样率
  213. .setAudioBitRate(32768) // 音频传输率
  214. .setVideoCodec("libx264") // 视频编解码器
  215. .setVideoFrameRate(24, 1) // 视频帧速率
  216. .setVideoResolution(640, 480) // 视频分辨率
  217. .setStrict(FFmpegBuilder.Strict.EXPERIMENTAL) // 严格形式
  218. .done();
  219. FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe);
  220. ExecutorService service = Executors.newCachedThreadPool();
  221. // 3.直接new一个FutureTask
  222. SubTask subTask = new SubTask(executor, builder, in, url, input, output, outputdir+"/"+output);
  223. FutureTask<Boolean> result = new FutureTask<Boolean>(subTask);
  224. // 4.提交任务
  225. service.submit(result);
  226. // 5.关闭线程池
  227. service.shutdown();
  228. // LOGGER.info("=============返回给前端=============");
  229. //System.out.println("task运行结果" + result.get());
  230. return "SUCESS";
  231. }
  232. private void updateSql(String url, String tempdir, String filename) {
  233. // 修改数据
  234. File file = new File(tempdir);
  235. JtVideo video = new JtVideo();
  236. video.setMd5(FileMD5(file));
  237. video.setTranscoding(2);
  238. video.setUrl(url);
  239. url = url.substring(0,url.lastIndexOf("/"));
  240. if(videoChange(url, tempdir, filename)){
  241. System.out.println("修改数据库000000000000000000000");
  242. adminVideoService.updateByUrl(video);//修改状态
  243. }// 上传到远程
  244. }
  245. private boolean videoChange(String url, String tempdir, String filename) {
  246. System.out.println("文件路径url:============"+url);
  247. System.out.println("上传到ftpfilename:=============="+filename);
  248. System.out.println("本地tempdir:=============="+tempdir);
  249. boolean t =false;
  250. File f2 = new File(tempdir);// 临时压缩后的文件
  251. if (f2.exists()) {
  252. // 传到远程服务器
  253. FtpUtils ftp = new FtpUtils();
  254. System.out.println("上传到远程服务器............................");
  255. t = ftp.uploadFile(url, filename, tempdir);
  256. System.out.println("上传远程服务器完成/////////////////////////");
  257. // 删除视频
  258. f2.delete();
  259. }
  260. return t;
  261. }
  262. // 获得文件md5
  263. private String FileMD5(File file) {
  264. try {
  265. FileInputStream fis = new FileInputStream(file);
  266. MessageDigest md = MessageDigest.getInstance("MD5");
  267. byte[] buffer = new byte[1024];
  268. int length = -1;
  269. while ((length = fis.read(buffer, 0, 1024)) != -1) {
  270. md.update(buffer, 0, length);
  271. }
  272. BigInteger bigInt = new BigInteger(1, md.digest());
  273. return bigInt.toString(16);
  274. } catch (FileNotFoundException e) {
  275. e.printStackTrace();
  276. } catch (NoSuchAlgorithmException e) {
  277. e.printStackTrace();
  278. } catch (IOException e) {
  279. e.printStackTrace();
  280. }
  281. return null;
  282. }
  283. class SubTask implements Callable<Boolean> {
  284. private Boolean state = false;
  285. private FFmpegExecutor executor;
  286. private FFmpegBuilder builder;
  287. private FFmpegProbeResult probeResult;
  288. private String url;
  289. private String input;
  290. private String output;
  291. private String tempdir;
  292. public SubTask(FFmpegExecutor executor, FFmpegBuilder builder,
  293. FFmpegProbeResult probeResult, String url, String input,
  294. String output, String tempdir) {
  295. this.executor = executor;
  296. this.builder = builder;
  297. this.probeResult = probeResult;
  298. this.url = url;
  299. this.input = input;
  300. this.output = output;
  301. this.tempdir = tempdir;
  302. }
  303. public FFmpegExecutor getExecutor() {
  304. return executor;
  305. }
  306. public FFmpegBuilder getBuilder() {
  307. return builder;
  308. }
  309. public String getUrl() {
  310. return url;
  311. }
  312. public String getInput() {
  313. return input;
  314. }
  315. public String getOutput() {
  316. return output;
  317. }
  318. public Boolean getState() {
  319. return state;
  320. }
  321. @Override
  322. public Boolean call() throws Exception {
  323. final double duration_ns = probeResult.getFormat().duration
  324. * TimeUnit.SECONDS.toNanos(1);
  325. try {
  326. FFmpegJob job = executor.createJob(builder,
  327. new ProgressListener() {
  328. @Override
  329. public void progress(Progress progress) {
  330. double percentage = progress.out_time_ns
  331. / duration_ns;
  332. System.out.println(String
  333. .format("[%.0f%%] status:%s frame:%d time:%s ms fps:%.0f speed:%.2fx",
  334. percentage * 100,
  335. progress.status,
  336. progress.frame,
  337. FFmpegUtils.toTimecode(
  338. progress.out_time_ns,
  339. TimeUnit.NANOSECONDS),
  340. progress.fps.doubleValue(),
  341. progress.speed));
  342. }
  343. });
  344. job.run();
  345. // 6.阻塞 call
  346. // System.out.println("设置 sate");
  347. // latch.wait();
  348. state = true;
  349. updateSql(url, tempdir,output);
  350. // 删除未压缩的视频
  351. File f1 = new File(input);
  352. f1.delete();
  353. } catch (Exception e) {
  354. e.printStackTrace();
  355. }
  356. return state;
  357. }
  358. }
  359. }