| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- package com.goafanti.admin.controller;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import java.math.BigInteger;
- 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.FtpUtils;
- 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}")
- public String ffmpegPath = null;
- @Value(value = "${ffprobe.path}")
- public String ffprobePath = 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 filename = handleVideoFiles(res, req);
- 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();
- result.setData(adminVideoService.getVideoById(id));
- 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;
- }
-
- String filename = video.getUrl();
-
- // 生成保存到数据库的url
- String output = System.nanoTime() + ".mp4";
- video.setUrl(createFileName() + "/" + output);
-
- video.setTranscoding(1);
- // 保存到数据库
- result.setData(adminVideoService.insertVideo(video));
- // 压缩视频并将视频
- videoChange(filename, output, video.getUrl());
-
- 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 oldVideo = adminVideoService.getVideoById(video.getId());
- 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 (StringUtils.isNotBlank(video.getUrl()) && !video.getUrl().equals(oldVideo.getUrl())) { // 如果修改了视频
- video.setTranscoding(1);
- // 压缩视频并将视频
- String filename = video.getUrl();
- String output = System.nanoTime() + ".mp4";
- video.setUrl(createFileName() + "/" + output);
- videoChange(filename, output, video.getUrl());
- }else{
- video.setUrl(null);
- }
- result.setData(adminVideoService.updateVideo(video));
- return result;
- }
-
-
- // 将文件转码
- private String videoChange(String input, String output, String url) throws IOException,
- InterruptedException, ExecutionException {
- 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(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);
- FutureTask<Boolean> result = new FutureTask<Boolean>(subTask);
- // 4.提交任务
- service.submit(result);
- // 5.关闭线程池
- service.shutdown();
- // LOGGER.info("=============返回给前端=============");
- return "SUCESS";
- }
- private void updateSql(String url, String filename) {
- // 修改数据
- File file = new File(filename);
- JtVideo video = new JtVideo();
- video.setMd5(FileMD5(file));
- video.setTranscoding(2);
- video.setUrl(url);
- int c = adminVideoService.updateByUrl(video);
- if (c > 0){
- url = url.substring(0,url.lastIndexOf("/"));
- videoChange(url, filename);// 上传到远程
- }
- }
- private void videoChange(String url, String filename) {
-
- File f2 = new File(filename);// 临时压缩后的文件
- if (f2.exists()) {
- // 传到远程服务器
- FtpUtils ftp = new FtpUtils();
- ftp.uploadFile(url, filename, filename);
- // 删除视频
- f2.delete();
- }
- }
- // 获得文件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<Boolean> {
- private Boolean state = false;
- private FFmpegExecutor executor;
- private FFmpegBuilder builder;
- private FFmpegProbeResult probeResult;
- private String url;
- private String input;
- private String output;
- public SubTask(FFmpegExecutor executor, FFmpegBuilder builder,
- FFmpegProbeResult probeResult, String url, String input,
- String output) {
- this.executor = executor;
- this.builder = builder;
- this.probeResult = probeResult;
- this.url = url;
- this.input = input;
- this.output = output;
- }
- 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, output);
- // 删除未压缩的视频
- File f1 = new File(input);
- f1.delete();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return state;
- }
- }
- }
|