集成视频格式转换功能1.0(基于javacv)

dependabot/maven/office-plugin/commons-io-commons-io-2.7
zhangxiaoxiao9527 2021-04-18 12:40:59 +08:00 committed by kl
parent a3485dd9b7
commit bcdb5ce0e6
10 changed files with 333 additions and 3 deletions

View File

@ -192,6 +192,64 @@
<artifactId>galimatias</artifactId>
<version>0.2.1</version>
</dependency>
<!-- bytedeco opencv ffmpegjavacv -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.2</version>
</dependency>
<!-- linuxwindows -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.1.2-1.5.2</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv</artifactId>
<version>4.1.2-1.5.2</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.6-1.5.1</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>openblas</artifactId>
<version>0.3.6-1.5.1</version>
<classifier>windows-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>4.2.1-1.5.2</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>ffmpeg</artifactId>
<version>4.2.1-1.5.2</version>
<classifier>windows-x86_64</classifier>
</dependency>
</dependencies>
<build>

View File

@ -55,6 +55,11 @@ cache.enabled = ${KK_CACHE_ENABLED:true}
simText = ${KK_SIMTEXT:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}
#
media = ${KK_MEDIA:mp3,wav,mp4,flv}
#,avi,mov,wmv,3gp,rm
#线线,
media.convert.disable = ${KK_MEDIA_CONVERT_DISABLE:false}
#
convertMedias = ${KK_CONVERTMEDIAS:avi,mov,wmv,mkv,3gp,rm}
#office(word ppt)(image)pdf
office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:image}
#officefalsetrue

View File

@ -24,6 +24,8 @@ public class ConfigConstants {
private static Boolean cacheEnabled;
private static String[] simTexts = {};
private static String[] medias = {};
private static String[] convertMedias = {};
private static String mediaConvertDisable;
private static String officePreviewType;
private static String officePreviewSwitchDisabled;
private static String ftpUsername;
@ -89,6 +91,33 @@ public class ConfigConstants {
ConfigConstants.medias = Media;
}
public static String[] getConvertMedias() {
return convertMedias;
}
@Value("${convertMedias:avi,mov,wmv,mkv,3gp,rm}")
public void setConvertMedias(String convertMedia) {
String[] mediaArr = convertMedia.split(",");
setConvertMediaValue(mediaArr);
}
public static void setConvertMediaValue(String[] ConvertMedia) {
ConfigConstants.convertMedias = ConvertMedia;
}
public static String getMediaConvertDisable() {
return mediaConvertDisable;
}
@Value("${media.convert.disable:true}")
public void setMediaConvertDisable(String mediaConvertDisable) {
setMediaConvertDisableValue(mediaConvertDisable);
}
public static void setMediaConvertDisableValue(String mediaConvertDisable) {
ConfigConstants.mediaConvertDisable = mediaConvertDisable;
}
public static String getOfficePreviewType() {
return officePreviewType;
}

View File

@ -33,6 +33,7 @@ public enum FileType {
private static final String[] SSIM_TEXT_TYPES = ConfigConstants.getSimText();
private static final String[] CODES = {"java", "c", "php", "go", "python", "py", "js", "html", "ftl", "css", "lua", "sh", "rb", "yml", "json", "h", "cpp", "cs", "aspx", "jsp"};
private static final String[] MEDIA_TYPES = ConfigConstants.getMedia();
public static final String[] MEDIA_TYPES_CONVERT = ConfigConstants.getConvertMedias();
private static final Map<String, FileType> FILE_TYPE_MAPPER = new HashMap<>();
static {
@ -51,6 +52,9 @@ public enum FileType {
for (String media : MEDIA_TYPES) {
FILE_TYPE_MAPPER.put(media, FileType.MEDIA);
}
for (String media : MEDIA_TYPES_CONVERT) {
FILE_TYPE_MAPPER.put(media, FileType.MEDIA);
}
for (String tif : TIFF_TYPES) {
FILE_TYPE_MAPPER.put(tif, FileType.TIFF);
}

View File

@ -286,4 +286,27 @@ public class FileHandlerService {
}
return attribute;
}
/**
* @return ()
*/
public Map<String, String> listConvertedMedias() {
return cacheService.getMediaConvertCache();
}
/**
*
* @param fileName
* @param value
*/
public void addConvertedMedias(String fileName, String value) {
cacheService.putMediaConvertCache(fileName, value);
}
/**
* @return
*/
public String getConvertedMedias(String key) {
return cacheService.getMediaConvertCache(key);
}
}

View File

@ -12,15 +12,18 @@ public interface CacheService {
String FILE_PREVIEW_PDF_KEY = "converted-preview-pdf-file";
String FILE_PREVIEW_IMGS_KEY = "converted-preview-imgs-file";//压缩包内图片文件集合
String FILE_PREVIEW_PDF_IMGS_KEY = "converted-preview-pdfimgs-file";
String FILE_PREVIEW_MEDIA_CONVERT_KEY = "converted-preview-media-file";
String TASK_QUEUE_NAME = "convert-task";
Integer DEFAULT_PDF_CAPACITY = 500000;
Integer DEFAULT_IMG_CAPACITY = 500000;
Integer DEFAULT_PDFIMG_CAPACITY = 500000;
Integer DEFAULT_MEDIACONVERT_CAPACITY = 500000;
void initPDFCachePool(Integer capacity);
void initIMGCachePool(Integer capacity);
void initPdfImagesCachePool(Integer capacity);
void initMediaConvertCachePool(Integer capacity);
void putPDFCache(String key, String value);
void putImgCache(String key, List<String> value);
Map<String, String> getPDFCache();
@ -29,6 +32,9 @@ public interface CacheService {
List<String> getImgCache(String key);
Integer getPdfImageCache(String key);
void putPdfImageCache(String pdfFilePath, int num);
Map<String, String> getMediaConvertCache();
void putMediaConvertCache(String key, String value);
String getMediaConvertCache(String key);
void cleanCache();
void addQueueTask(String url);
String takeQueueTask() throws InterruptedException;

View File

@ -26,6 +26,7 @@ public class CacheServiceJDKImpl implements CacheService {
private Map<String, String> pdfCache;
private Map<String, List<String>> imgCache;
private Map<String, Integer> pdfImagesCache;
private Map<String, String> mediaConvertCache;
private static final int QUEUE_SIZE = 500000;
private final BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
@ -34,6 +35,7 @@ public class CacheServiceJDKImpl implements CacheService {
initPDFCachePool(CacheService.DEFAULT_PDF_CAPACITY);
initIMGCachePool(CacheService.DEFAULT_IMG_CAPACITY);
initPdfImagesCachePool(CacheService.DEFAULT_PDFIMG_CAPACITY);
initMediaConvertCachePool(CacheService.DEFAULT_MEDIACONVERT_CAPACITY);
}
@Override
@ -79,6 +81,21 @@ public class CacheServiceJDKImpl implements CacheService {
pdfImagesCache.put(pdfFilePath, num);
}
@Override
public Map<String, String> getMediaConvertCache() {
return mediaConvertCache;
}
@Override
public void putMediaConvertCache(String key, String value) {
mediaConvertCache.put(key, value);
}
@Override
public String getMediaConvertCache(String key) {
return mediaConvertCache.get(key);
}
@Override
public void cleanCache() {
initPDFCachePool(CacheService.DEFAULT_PDF_CAPACITY);
@ -116,4 +133,12 @@ public class CacheServiceJDKImpl implements CacheService {
.maximumWeightedCapacity(capacity).weigher(Weighers.singleton())
.build();
}
@Override
public void initMediaConvertCachePool(Integer capacity) {
mediaConvertCache = new ConcurrentLinkedHashMap.Builder<String, String>()
.maximumWeightedCapacity(capacity).weigher(Weighers.singleton())
.build();
}
}

View File

@ -34,6 +34,11 @@ public class CacheServiceRedisImpl implements CacheService {
@Override
public void initPdfImagesCachePool(Integer capacity) { }
@Override
public void initMediaConvertCachePool(Integer capacity) {
}
@Override
public void putPDFCache(String key, String value) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_PDF_KEY);
@ -80,6 +85,23 @@ public class CacheServiceRedisImpl implements CacheService {
convertedList.fastPut(pdfFilePath, num);
}
@Override
public Map<String, String> getMediaConvertCache() {
return redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
}
@Override
public void putMediaConvertCache(String key, String value) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
convertedList.fastPut(key, value);
}
@Override
public String getMediaConvertCache(String key) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
return convertedList.get(key);
}
@Override
public void cleanCache() {
cleanPdfCache();

View File

@ -73,6 +73,11 @@ public class CacheServiceRocksDBImpl implements CacheService {
}
@Override
public void initMediaConvertCachePool(Integer capacity) {
}
@Override
public void putPDFCache(String key, String value) {
try {
@ -171,6 +176,40 @@ public class CacheServiceRocksDBImpl implements CacheService {
}
}
@Override
public Map<String, String> getMediaConvertCache() {
Map<String, String> result = new HashMap<>();
try{
result = (Map<String, String>) toObject(db.get(FILE_PREVIEW_MEDIA_CONVERT_KEY.getBytes()));
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
public void putMediaConvertCache(String key, String value) {
try {
Map<String, String> mediaConvertCacheItem = getMediaConvertCache();
mediaConvertCacheItem.put(key, value);
db.put(FILE_PREVIEW_MEDIA_CONVERT_KEY.getBytes(), toByteArray(mediaConvertCacheItem));
} catch (RocksDBException | IOException e) {
LOGGER.error("Put into RocksDB Exception" + e);
}
}
@Override
public String getMediaConvertCache(String key) {
String result = "";
try{
Map<String, String> map = (Map<String, String>) toObject(db.get(FILE_PREVIEW_MEDIA_CONVERT_KEY.getBytes()));
result = map.get(key);
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
public void cleanCache() {
try {

View File

@ -1,13 +1,21 @@
package cn.keking.service.impl;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FileHandlerService;
import cn.keking.web.filter.BaseUrlFilter;
import org.artofsolving.jodconverter.util.ConfigUtils;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import java.io.File;
/**
* @author : kl
@ -21,6 +29,8 @@ public class MediaFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
private static Object LOCK=new Object();
public MediaFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
@ -34,14 +44,123 @@ public class MediaFilePreviewImpl implements FilePreview {
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
} else {
model.addAttribute("mediaUrl", BaseUrlFilter.getBaseUrl() + fileHandlerService.getRelativePath(response.getContent()));
url=BaseUrlFilter.getBaseUrl() + fileHandlerService.getRelativePath(response.getContent());
fileAttribute.setUrl(url);
}
} else {
model.addAttribute("mediaUrl", url);
}
if(checkNeedConvert(fileAttribute.getSuffix())){
url=convertUrl(fileAttribute);
}else{
//正常media类型
String[] medias = ConfigConstants.getMedia();
for(String media:medias){
if(media.equals(fileAttribute.getSuffix())){
model.addAttribute("mediaUrl", url);
return MEDIA_FILE_PREVIEW_PAGE;
}
}
return otherFilePreview.notSupportedFile(model, fileAttribute, "暂不支持");
}
model.addAttribute("mediaUrl", url);
return MEDIA_FILE_PREVIEW_PAGE;
}
/**
*
* url
* @return url
*/
private String convertUrl(FileAttribute fileAttribute) {
String url = fileAttribute.getUrl();
if(fileHandlerService.listConvertedMedias().containsKey(url)){
url= fileHandlerService.getConvertedMedias(url);
}else{
if(!fileHandlerService.listConvertedMedias().containsKey(url)){
synchronized(LOCK){
if(!fileHandlerService.listConvertedMedias().containsKey(url)){
String convertedUrl=convertToMp4(fileAttribute);
//加入缓存
fileHandlerService.addConvertedMedias(url,convertedUrl);
url=convertedUrl;
}
}
}
}
return url;
}
/**
*
* @return
*/
private boolean checkNeedConvert(String suffix) {
//1.检查开关是否开启
if("false".equals(ConfigConstants.getMediaConvertDisable())){
return false;
}
//2.检查当前文件是否需要转换
String[] mediaTypesConvert = FileType.MEDIA_TYPES_CONVERT;
String type = suffix;
for(String temp : mediaTypesConvert){
if(type.equals(temp)){
return true;
}
}
return false;
}
/**
* MP4
* @param fileAttribute
* @return
*/
private static String convertToMp4(FileAttribute fileAttribute) {
//说明:这里做临时处理,取上传文件的目录
String homePath = ConfigUtils.getHomePath();
String filePath = homePath+File.separator+"file"+File.separator+"demo"+File.separator+fileAttribute.getName();
String convertFileName=fileAttribute.getUrl().replace(fileAttribute.getSuffix(),"mp4");
File file=new File(filePath);
FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber(file);
String fileName = null;
Frame captured_frame = null;
FFmpegFrameRecorder recorder = null;
try {
fileName = file.getAbsolutePath().replace(fileAttribute.getSuffix(),"mp4");
File desFile=new File(fileName);
//判断一下防止穿透缓存
if(desFile.exists()){
return fileName;
}
frameGrabber.start();
recorder = new FFmpegFrameRecorder(fileName, frameGrabber.getImageWidth(), frameGrabber.getImageHeight(), frameGrabber.getAudioChannels());
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); //avcodec.AV_CODEC_ID_H264  //AV_CODEC_ID_MPEG4
recorder.setFormat("mp4");
recorder.setFrameRate(frameGrabber.getFrameRate());
//recorder.setSampleFormat(frameGrabber.getSampleFormat()); //
recorder.setSampleRate(frameGrabber.getSampleRate());
recorder.setAudioChannels(frameGrabber.getAudioChannels());
recorder.setFrameRate(frameGrabber.getFrameRate());
recorder.start();
while ((captured_frame = frameGrabber.grabFrame()) != null) {
try {
recorder.setTimestamp(frameGrabber.getTimestamp());
recorder.record(captured_frame);
} catch (Exception e) {
}
}
recorder.stop();
recorder.release();
frameGrabber.stop();
} catch (Exception e) {
e.printStackTrace();
}
//是否删除源文件
//file.delete();
return convertFileName;
}
}