新增视频转码、视频预览功能

This commit is contained in:
wanggeng888 2021-06-14 18:45:45 +08:00
parent 4c6c5d00c9
commit ee812fc4c5
13 changed files with 364 additions and 125 deletions

View File

@ -61,14 +61,9 @@ public class VideoController extends DefaultBaseController {
@ApiImplicitParam(name = "ids", value = "ID列表用下划线分隔", paramType = "path", example = "1_2_3")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@DeleteMapping("remove/{isRemoveSource}/{ids}")
public synchronized SuccessResult remove(@PathVariable() Integer isRemoveSource, @PathVariable("ids") String ids) {
if (isRemoveSource == 0) {
videoService.remove(Arrays.asList(ids.split("\\_")));
}
if (isRemoveSource == 1) {
videoService.delete(Arrays.asList(ids.split("\\_")));
}
@DeleteMapping("delete/{ids}")
public synchronized SuccessResult delete(@PathVariable("ids") String ids) {
videoService.delete(Arrays.asList(ids.split("\\_")));
return new SuccessResult();
}

View File

@ -1,14 +1,23 @@
package ink.wgink.module.file.media.controller.route.video;
import ink.wgink.exceptions.ParamsException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.file.media.service.video.IVideoService;
import ink.wgink.pojo.result.ErrorResult;
import ink.wgink.properties.media.video.VideoProperties;
import io.swagger.annotations.Api;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.CompletableFuture;
/**
* When you feel like quitting. Think about why you started
* 当你想要放弃的时候想想当初你为何开始
@ -19,13 +28,15 @@ import org.springframework.web.servlet.ModelAndView;
* @Date: 2021/6/12 11:16 下午
* @Version: 1.0
*/
@Api(tags = ISystemConstant.API_TAGS_SYSTEM_PREFIX + "文件管理接口")
@Api(tags = ISystemConstant.API_TAGS_SYSTEM_PREFIX + "视频管理接口")
@Controller
@RequestMapping(ISystemConstant.ROUTE_PREFIX + "/file/media/video")
public class VideoRouteController {
@Autowired
private VideoProperties videoProperties;
@Autowired
private IVideoService videoService;
@GetMapping("list")
public ModelAndView list() {
@ -40,4 +51,29 @@ public class VideoRouteController {
return modelAndView;
}
@GetMapping("preview")
public ModelAndView preview() {
return new ModelAndView("file/media/video/preview");
}
@ApiOperation(value = "下载视频", notes = "下载视频接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "isOpen", value = "是否打开,true和false", paramType = "path"),
@ApiImplicitParam(name = "fileId", value = "文件ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping(value = "download/{isOpen}/{fileId}", produces = "video/mp4")
public void downLoad(HttpServletRequest request,
HttpServletResponse response,
@PathVariable("isOpen") Boolean isOpen,
@PathVariable("fileId") String fileId) {
if (isOpen == null) {
throw new ParamsException("参数错误");
}
if (fileId.indexOf(".") > 0) {
fileId = fileId.split("\\.")[0];
}
videoService.downLoad(request, response, isOpen, fileId);
}
}

View File

@ -17,8 +17,20 @@ import java.io.InputStream;
*/
public interface IMediaStream {
/**
* 输入
*
* @param inputStream
* @throws Exception
*/
void input(InputStream inputStream) throws Exception;
/**
* 错误
*
* @param errorStream
* @throws Exception
*/
void error(InputStream errorStream) throws Exception;
}

View File

@ -59,7 +59,65 @@ public class MediaServiceImpl extends DefaultBaseService implements IMediaServic
@Override
public String getContentType(String fileType) {
return null;
String contentType;
switch (fileType) {
case "png":
contentType = "image/png";
break;
case "blob":
case "jpg":
case "jpeg":
contentType = "image/jpeg";
break;
case "gif":
contentType = "image/gif";
break;
case "mp4":
contentType = "video/mp4";
break;
case "rmvb":
contentType = "application/vnd.rn-realmedia-vbr";
break;
case "mp3":
contentType = "audio/mp3";
break;
case "wmv":
contentType = "video/x-ms-wmv";
break;
case "wav":
contentType = "audio/wav";
break;
case "doc":
contentType = "application/msword";
break;
case "docx":
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
break;
case "xls":
contentType = "application/vnd.ms-excel";
break;
case "xlsx":
contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
break;
case "ppt":
contentType = "application/vnd.ms-powerpoint";
break;
case "pptx":
contentType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
break;
case "txt":
contentType = "text/plain";
break;
case "apk":
contentType = "application/vnd.android.package-archive";
break;
case "pdf":
contentType = "application/pdf";
break;
default:
contentType = "application/octet-stream";
}
return contentType;
}
@Override

View File

@ -9,7 +9,10 @@ import ink.wgink.pojo.result.SuccessResultList;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@ -137,4 +140,13 @@ public interface IVideoService {
*/
SuccessResultList<List<VideoDTO>> listPage(ListPage page);
/**
* 文件下载
*
* @param request
* @param response
* @param isOpen
* @param fileId
*/
void downLoad(HttpServletRequest request, HttpServletResponse response, Boolean isOpen, String fileId);
}

View File

@ -18,18 +18,32 @@ import ink.wgink.module.file.media.service.video.IVideoService;
import ink.wgink.module.file.media.task.transcoding.VideoConvertManager;
import ink.wgink.module.file.media.task.transcoding.runnable.VideoConvertRunnable;
import ink.wgink.pojo.ListPage;
import ink.wgink.pojo.pos.FilePO;
import ink.wgink.pojo.result.SuccessResultList;
import ink.wgink.properties.media.MediaProperties;
import ink.wgink.util.UUIDUtil;
import ink.wgink.util.date.DateUtil;
import ink.wgink.util.map.HashMapUtil;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URLEncoder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -126,6 +140,7 @@ public class VideoServiceImpl extends DefaultBaseService implements IVideoServic
params.put("fileFullPath", convertFile.getAbsolutePath());
VideoMetaInfo videoMetaInfo = MediaManager.getInstance().getVideoMetaInfo(convertFile);
System.out.println(videoMetaInfo);
params.put("videoDuration", videoMetaInfo.getDuration());
params.put("videoWidth", videoMetaInfo.getWidth());
params.put("videoHeight", videoMetaInfo.getHeight());
@ -173,7 +188,7 @@ public class VideoServiceImpl extends DefaultBaseService implements IVideoServic
// 构建视频内容
VideoVO videoVO = new VideoVO();
videoVO.setFileName(fileName);
videoVO.setFileName(fileName.split("\\.")[0]);
videoVO.setFileFullPath(fileFullPath);
videoVO.setFilePath(filePath);
videoVO.setFileSize(fileSize);
@ -204,18 +219,17 @@ public class VideoServiceImpl extends DefaultBaseService implements IVideoServic
@Override
public SseEmitter getConvertProgress() {
String threadId = String.valueOf(Thread.currentThread().getId());
// 30分钟超时
SseEmitter sseEmitter = new SseEmitter(1800000L);
final String sseEmitterId = UUIDUtil.getUUID();
SseEmitter sseEmitter = new SseEmitter(0L);
sseEmitter.onTimeout(() -> {
VideoConvertManager.getInstance().removeConvertProgressSseEmitter(threadId);
VideoConvertManager.getInstance().removeConvertProgressSseEmitter(sseEmitterId);
LOG.debug("连接超时,移出队列");
});
sseEmitter.onCompletion(() -> {
VideoConvertManager.getInstance().removeConvertProgressSseEmitter(sseEmitterId);
LOG.debug("连接结束,移出队列");
});
sseEmitter.onError(throwable -> {
throwable.printStackTrace();
});
VideoConvertManager.getInstance().addConvertProgressSseEmitter(threadId, sseEmitter);
VideoConvertManager.getInstance().addConvertProgressSseEmitter(sseEmitterId, sseEmitter);
return sseEmitter;
}
@ -251,6 +265,79 @@ public class VideoServiceImpl extends DefaultBaseService implements IVideoServic
return new SuccessResultList<>(videoDTOs, pageInfo.getPageNum(), pageInfo.getTotal());
}
@Override
public void downLoad(HttpServletRequest request, HttpServletResponse response, Boolean isOpen, String fileId) {
VideoDTO videoDTO = get(fileId);
if (videoDTO == null) {
response.setStatus(HttpStatus.MOVED_PERMANENTLY.value());
try {
response.sendRedirect("404.html");
} catch (IOException e) {
throw new FileException("文件输出异常", e);
}
return;
}
try (
RandomAccessFile randomAccessFile = new RandomAccessFile(videoDTO.getFileFullPath(), "r");
FileChannel fileChannel = randomAccessFile.getChannel();
OutputStream outputStream = response.getOutputStream();
WritableByteChannel writableByteChannel = Channels.newChannel(outputStream);
) {
response.setHeader("Content-Length", String.valueOf(videoDTO.getFileSize()));
response.setContentType(mediaService.getContentType(videoDTO.getFileType()));
if (!isOpen) {
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(videoDTO.getFileName(), "UTF-8"));
} else {
response.setHeader("Content-Disposition", "inline;fileName=" + URLEncoder.encode(videoDTO.getFileName(), "UTF-8"));
}
String rangeString = request.getHeader("Range");
LOG.debug("range: {}", rangeString);
long contentLength = Long.valueOf(videoDTO.getFileSize());
long startRange = 0;
long endRange = contentLength - 1;
if (!StringUtils.isBlank(rangeString)) {
if (!isOpen) {
response.setContentType("multipart/byteranges");
}
String[] rangeArray = rangeString.substring(rangeString.indexOf("=") + 1).split("-");
startRange = Long.valueOf(rangeArray[0]);
if (rangeArray.length > 1) {
endRange = Long.valueOf(rangeArray[1]);
}
setRangeHeader(startRange, endRange, response, videoDTO.getFileId(), contentLength);
randomAccessFile.seek(startRange);
}
LOG.debug("startRange: {}, endRange: {}", startRange, endRange);
long totalOutputLength = endRange - startRange + 1;
fileChannel.transferTo(startRange, totalOutputLength, writableByteChannel);
outputStream.flush();
} catch (Exception e) {
if (e instanceof ClientAbortException) {
LOG.debug("客户端断开连接");
} else {
throw new FileException("文件输出异常", e);
}
}
}
/**
* 处理视频流问题
*
* @param startRange
* @param endRange
* @param response
* @param fileId
* @param contentLength
*/
private void setRangeHeader(long startRange, long endRange, HttpServletResponse response, String fileId, long contentLength) {
// 这里不设置会出现第一次加载很慢的情况
response.setHeader("Content-Length", String.valueOf(endRange - startRange + 1));
response.setHeader("Content-Range", String.format("bytes %d-%d/%d", startRange, endRange, contentLength));
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("Etag", fileId);
response.setStatus(206);
}
/**
* 检查转换中状态
*/

View File

@ -2,8 +2,11 @@ package ink.wgink.module.file.media.task.transcoding;
import ink.wgink.module.file.media.pojo.dtos.ConvertProgressDTO;
import ink.wgink.module.file.media.task.transcoding.runnable.VideoConvertRunnable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.*;
@ -18,7 +21,7 @@ import java.util.concurrent.*;
* @Version: 1.0
*/
public class VideoConvertManager {
private static final Logger LOG = LoggerFactory.getLogger(VideoConvertManager.class);
private static VideoConvertManager VIDEO_TRANS_CODING_MANAGER = VideoTransCodingManagerBuilder.videoConvertManager;
private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10, 30L, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
private Map<String, SseEmitter> convertProgressSseEmitterMap = new ConcurrentHashMap<>();
@ -34,24 +37,34 @@ public class VideoConvertManager {
threadPoolExecutor.execute(videoConvertRunnable);
}
public void addConvertProgressSseEmitter(String threadId, SseEmitter sseEmitter) {
convertProgressSseEmitterMap.put(threadId, sseEmitter);
public void addConvertProgressSseEmitter(String sseEmitterId, SseEmitter sseEmitter) {
convertProgressSseEmitterMap.put(sseEmitterId, sseEmitter);
// 发送空对象
ConvertProgressDTO convertProgressDTO = new ConvertProgressDTO();
convertProgressDTO.setFileId("");
convertProgressDTO.setPercent(0D);
try {
sseEmitter.send(convertProgressDTO);
} catch (Throwable e) {
e.printStackTrace();
}
}
public void removeConvertProgressSseEmitter(String threadId) {
convertProgressSseEmitterMap.remove(threadId);
public void removeConvertProgressSseEmitter(String sseEmitterId) {
convertProgressSseEmitterMap.remove(sseEmitterId);
}
public void convertProgressSseEmitterNotice(String fileId, Double percent) {
ConvertProgressDTO convertProgressDTO = new ConvertProgressDTO();
convertProgressDTO.setFileId(fileId);
convertProgressDTO.setPercent(percent);
try {
for (Map.Entry<String, SseEmitter> kv : convertProgressSseEmitterMap.entrySet()) {
LOG.debug("视频ID {} 转码进度:{}%", fileId, percent);
for (Map.Entry<String, SseEmitter> kv : convertProgressSseEmitterMap.entrySet()) {
try {
kv.getValue().send(convertProgressDTO);
} catch (Throwable e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}

View File

@ -85,30 +85,17 @@ public class VideoConvertRunnable implements Runnable {
if (fullTime > 0L) {
convertPercent = new BigDecimal((currentTime / (fullTime * 1D) * 100D)).setScale(2, RoundingMode.HALF_UP).doubleValue();
}
if (convertPercent > 0D && convertPercent < 100D && (int) convertPercent % 5 == 0) {
if (convertPercent > 0D && convertPercent < 100D) {
VideoConvertManager.getInstance().convertProgressSseEmitterNotice(fileId, convertPercent);
}
if (convertPercent >= 100D) {
updateConvertStatus();
}
}
VideoConvertManager.getInstance().convertProgressSseEmitterNotice(fileId, convertPercent);
LOG.debug("转码结束,更新状态");
videoService.updateConvert(fileId, outFile);
}
});
}
/**
* 更新转换状态
*/
private void updateConvertStatus() {
if (isUpdateConvertStatus) {
return;
}
VideoConvertManager.getInstance().convertProgressSseEmitterNotice(fileId, convertPercent);
isUpdateConvertStatus = true;
LOG.debug("转码结束,更新状态");
videoService.updateConvert(fileId, outFile);
}
/**
* 视频时长
*

View File

@ -236,11 +236,14 @@
FROM
media_video
<where>
is_delete = 0
<if test="fileId != null and fileId != ''">
AND
file_id = #{fileId}
</if>
<if test="fileMd5 != null and fileMd5 != ''">
AND file_md5 = #{fileMd5}
AND
file_md5 = #{fileMd5}
</if>
</where>
</select>

View File

@ -9,6 +9,10 @@
<link rel="stylesheet" href="assets/fonts/font-awesome/css/font-awesome.css"/>
<link rel="stylesheet" href="assets/layuiadmin/layui/css/layui.css" media="all">
<link rel="stylesheet" href="assets/layuiadmin/style/admin.css" media="all">
<style>
.layui-table-progress {height: 28px !important; line-height: 28px !important; border-radius: 0px !important;}
.layui-table-progress-bar {height: 28px !important; line-height: 28px !important; border-radius: 0px !important;}
</style>
</head>
<body>
<div class="layui-fluid layui-anim layui-anim-fadein">
@ -41,45 +45,29 @@
<i class="fa fa-lg fa-trash"></i> 删除
</button>
</div>
<span style="margin-left: 10px;"><i class="fa fa-warning"></i> 视频在线播放只支持MP4格式格式不符点击"转码状态"列的"转码"按钮进行转码</span>
<span style="margin-left: 10px;"><i class="fa fa-warning"></i> 视频在线播放只支持MP4格式格式不符合或不能播放,请点击"转码状态"列的"开始转码"按钮进行转码</span>
</script>
</div>
</div>
</div>
</div>
<div id="videoBoxBackGround" style="position: fixed;z-index: 1000;top: 0;left: 0;width: 100%;height: 100%;background-color: rgba(0,0,0,0.6); display: none;">
<div role="button" class="viewer-button viewer-close video-close-btn"></div>
<div id="videoBox" style="width: 600px; height: 400px; position: absolute; left: 50%; top: 50%; margin-left: -300px; margin-top: -200px;">
<div id="video" style="width: 100%; height: 100%"></div>
</div>
</div>
</div>
<script src="assets/js/vendor/ckplayer/ckplayer/ckplayer.js"></script>
<script src="assets/layuiadmin/layui/layui.js"></script>
<script type="text/javascript">
layui.config({
base: 'assets/layuiadmin/'
}).extend({
index: 'lib/index'
}).use(['index', 'table', 'laydate', 'common'], function() {
}).use(['index', 'table', 'laydate', 'element', 'common'], function() {
var $ = layui.$;
var $win = $(window);
var table = layui.table;
var admin = layui.admin;
var laydate = layui.laydate;
var element = layui.element;
var common = layui.common;
var resizeTimeout = null;
var tableUrl = 'api/file/media/video/listpage';
var ckPlayerFileId;
var ckPlayer = new ckplayer({
container: '#video',
variable: 'player',
flashplayer: false,
video: {
file: '',
type: 'video/mp4'
},
});
// 初始化表格
function initTable() {
@ -183,7 +171,7 @@
return rowData;
}
},
{field: 'convertProgress', width: 100, title: '转换进度', align:'center', fixed: 'right',
{field: 'convertProgress', width: 120, title: '转换进度', align:'center', fixed: 'right',
templet: function(row) {
if(row.convertStatus === 'UN_CONVERTED') {
return '0%';
@ -191,23 +179,27 @@
if(row.convertStatus === 'CONVERTED') {
return '100%';
}
return '<span id="progress_'+ row.fileId +'">0%</span>';
return '<div class="layui-progress layui-progress-big layui-table-progress" lay-showPercent="true" lay-filter="progress_'+ row.fileId +'">\n' +
' <div class="layui-progress-bar layui-table-progress-bar layui-bg-blue">' +
' <span class="layui-progress-text">0%</span>' +
' </div>\n' +
'</div>';
}
},
{field: 'convertStatus', width: 100, title: '转码状态', align:'center', fixed: 'right',
{field: 'convertStatus', width: 120, title: '转码状态', align:'center', fixed: 'right',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
if(rowData === 'UN_CONVERTED') {
return '<button type="button" class="layui-btn layui-btn-xs" lay-event="convertEvent">转码</button>';
return '<button type="button" class="layui-btn layui-btn-xs" lay-event="convertEvent">开始转码</button>';
}
if(rowData === 'CONVERTING') {
return '转码中';
return '排队转码中...';
}
if(rowData === 'CONVERTED') {
return '转码';
return '转码完成';
}
return rowData;
}
@ -253,46 +245,33 @@
});
}
// 删除
function removeFile(isRemoveSource, ids) {
var layIndex;
top.restAjax.delete(top.restAjax.path('api/file/media/video/remove/{isRemoveSource}/{ids}', [isRemoveSource, ids]), {}, null, function (code, data) {
top.dialog.msg(top.dataMessage.deleteSuccess, {time: 1000});
reloadTable();
}, function (code, data) {
top.dialog.msg(data.msg);
}, function () {
layIndex = top.dialog.msg(top.dataMessage.deleting, {icon: 16, time: 0, shade: 0.3});
}, function () {
top.dialog.close(layIndex);
});
}
function removeData(ids) {
top.dialog.msg(top.dataMessage.delete, {
time: 0,
btn: [top.dataMessage.button.yes, '同时删除文件(不可逆)', top.dataMessage.button.no],
shade: 0.3,
btn1: function (index) {
top.dialog.close(index);
removeFile(0, ids);
},
btn2: function(index) {
top.dialog.close(index);
removeFile(1, ids);
},
btn3: function(index) {
top.dialog.close(index);
}
top.dialog.confirm('确定删除(同时删除源文件)吗?', function(index) {
top.dialog.close(index);
var layIndex;
top.restAjax.delete(top.restAjax.path('api/file/media/video/delete/{ids}', [ids]), {}, null, function (code, data) {
top.dialog.msg(top.dataMessage.deleteSuccess, {time: 1000});
reloadTable();
}, function (code, data) {
top.dialog.msg(data.msg);
}, function () {
layIndex = top.dialog.msg(top.dataMessage.deleting, {icon: 16, time: 0, shade: 0.3});
}, function () {
top.dialog.close(layIndex);
});
});
}
function sseConvertProgress() {
var source = new EventSource(top.restAjax.path('api/file/media/video/get-convert-progress', []));
source.addEventListener('message', function(e) {
var data = JSON.parse(e.data);
$('#progress_'+ data.fileId).text(parseInt(data.percent) + '%');
if(data >= 100) {
var fileId = data.fileId;
var percent = data.percent;
element.progress('progress_'+ fileId, percent +'%');
if(percent >= 100) {
setTimeout(function() {
reloadTable();
}, 2000);
}, 5000);
}
});
source.addEventListener('open', function(e) {}, false);
@ -356,34 +335,60 @@
var data = obj.data;
var event = obj.event;
if(event === 'searchFileEvent') {
$('#videoBoxBackGround').show();
if(ckPlayerFileId == data.fileId) {
if(data.fileType != 'mp4') {
top.dialog.msg('在线播放只支持MP4格式请转码');
return;
}
ckPlayer.newVideo({
video: 'route/file/media/video/download/true/'+ data.fileId
var maxWidth = parseInt($win.width() * 0.8);
var maxHeight = parseInt($win.height() * 0.8);
var videoWidth = data.videoWidth;
var videoHeight = data.videoHeight;
var widthHeightProportion = videoWidth / videoHeight;
var openWidth = videoWidth;
var openHeight = videoHeight;
if(videoWidth > videoHeight) {
if(videoWidth > maxWidth) {
openWidth = maxWidth;
openHeight = maxWidth / widthHeightProportion;
}
if(videoHeight > maxHeight) {
openHeight = maxHeight;
openWidth = widthHeightProportion * openHeight;
}
} else {
if(videoHeight > maxHeight) {
openHeight = maxHeight;
openWidth = widthHeightProportion * openHeight;
}
if(videoWidth > maxWidth) {
openWidth = maxWidth;
openHeight = maxWidth / widthHeightProportion;
}
}
top.dialog.open({
url: top.restAjax.path('route/file/media/video/preview?fileId={fileId}', [data.fileId]),
title: false,
width: parseInt(openWidth) +'px',
height: parseInt(openHeight) +'px',
onClose: function() {}
});
ckPlayerFileId = data.fileId;
} else if(event === 'convertEvent') {
top.dialog.confirm('转码成功后,源文件将删除,确定转码吗?', function(index) {
top.dialog.close(index);
top.restAjax.put(top.restAjax.path('api/file/media/video/convert/{fileId}', [data.fileId]), {}, null, function (code, data) {
top.dialog.msg('正在转码...');
top.dialog.msg('正在转码...', {time: 1000});
reloadTable();
}, function (code, data) {
top.dialog.msg(data.msg);
}, function () {
layIndex = top.dialog.msg(top.dataMessage.deleting, {icon: 16, time: 0, shade: 0.3});
layIndex = top.dialog.msg('正在提交...', {icon: 16, time: 0, shade: 0.3});
}, function () {
top.dialog.close(layIndex);
});
});
}
});
$('.video-close-btn').on('click', function() {
ckPlayer.videoPause();
$('#videoBoxBackGround').hide();
});
});
</script>
</body>

View File

@ -0,0 +1,29 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="${#request.getContextPath() + '/'} ">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8"/>
<style>
html, body{margin: 0px; height: 0px; background-color: #000;}
body { overflow: hidden;}
</style>
</head>
<body>
<div id="video"></div>
<script src="assets/js/vendor/ckplayer/ckplayer/ckplayer.js"></script>
<script type="text/javascript">
var fileId = top.restAjax.params(window.location.href).fileId;
var ckPlayer = new ckplayer({
container: '#video',
variable: 'ckPlayer',
flashplayer: false,
video: {
file: top.restAjax.path('route/file/media/video/download/true/{fileId}', [fileId]),
type: 'video/mp4'
},
autoplay: true
});
</script>
</body>
</html>

View File

@ -57,7 +57,7 @@
var id = data.response;
uploadFileArray.push(id);
top.dialog.dialogData.uploadFileArray = uploadFileArray;
if(uploadFileArray.length >= maxFileCount) {
if(uploadFileArray.length >= $('#maxUploadCount').val()) {
$('.btn-file').hide();
}
}).on('filesuccessremove', function(event, previewId, index) {

View File

@ -54,9 +54,9 @@ public class MediaTest {
@Test
public void t3() {
MediaManager.getInstance().setFFmpegPath("D:\\ffmpeg-4.4-full_build\\ffmpeg-4.4-full_build\\bin\\ffmpeg.exe");
String sourcePath = "C:\\Users\\wenc0\\Desktop\\UploadFiles";
String sourceName = "超体.rmvb";
MediaManager.getInstance().setFFmpegPath("/Users/wanggeng/ffmpeg/ffmpeg");
String sourcePath = "/Users/wanggeng/Desktop/UploadFiles";
String sourceName = "720P_4000K_302139672.mp4";
File sourceFile = new File(sourcePath + File.separator + sourceName);
File outFile = new File(sourcePath + File.separator + sourceName + ".mp4");
@ -76,27 +76,29 @@ public class MediaTest {
public void error(InputStream errorStream) throws Exception {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(errorStream));
for (String line; (line = bufferedReader.readLine()) != null; ) {
System.out.println(line);
Matcher durationMatcher = durationPattern.matcher(line);
Matcher timeMatcher = timePattern.matcher(line);
if (durationMatcher.find()) {
String duration = durationMatcher.group();
System.out.println(duration);
// System.out.println(duration);
String durationTime = duration.replace("Duration: ", "");
fullTime = durationToLongTime(durationTime);
}
if (timeMatcher.find()) {
String time = timeMatcher.group();
System.out.println(time);
// System.out.println(time);
String timeTime = time.replace("time=", "");
currentTime = durationToLongTime(timeTime);
}
System.out.println(fullTime + "-" + currentTime);
if (fullTime > 0L) {
System.out.println((currentTime / (fullTime * 1D) * 100D) + "%");
}
// System.out.println(fullTime + "-" + currentTime);
// if (fullTime > 0L) {
// System.out.println((currentTime / (fullTime * 1D) * 100D) + "%");
// }
}
System.out.println("123123");
}
});
}