문제점
해결방안
구현
//동영상에서 특정 시각의 이미지를 잘라내어 특정 크기로 경로에 저장하는 쓰레드 작업
@Slf4j
public class ThreadStreamImage implements Runnable{
private File savePath;
private File source;
private int time;
private int width;
private int height;
private String format;
public ThreadStreamImage(int time,File savePath,File source,int width,int height,String format) {
this.time=time;
this.savePath=savePath;
this.source=source;
this.width=width;
this.height=height;
this.format=format;
}
@Override
public void run() {
log.debug("=============time: "+time+" processing Streaming thumbnail start");
FrameGrab grab;
try {
//동영상(source)를 읽기
grab = FrameGrab.createFrameGrab(NIOUtils.readableChannel(this.source));
//특정 시각(time)으로 이동
grab.seekToSecondPrecise(this.time);
//이동된 시각의 프레임 이미지 잘라내기
Picture thumbnail=grab.getNativeFrame();
//버퍼에 잘라낸 이미지 저장
BufferedImage bufferedImage = AWTUtil.toBufferedImage(thumbnail);
//특정 경로(savePath)에 가로(width),세로(height)형식으로 이미지 저장
Thumbnails.of(bufferedImage)
.size(this.width,this.height)
.outputFormat(this.format)
.toFile(savePath);
} catch (IOException | JCodecException e) {
e.printStackTrace();
}
log.debug("time: "+time+" processing Streaming thumbnail end===============");
}
}
public class FileUtilImpl implements FileUtil{
private void generateVideoStreamingThumbnail(File source,MediaDto media) throws Exception{
//동영상 소스 읽기
FrameGrab grab = FrameGrab.createFrameGrab(NIOUtils.readableChannel(source));
// 동영상의 재생시간 추출하기
int durationInSeconds = (int)grab.getVideoTrack().getMeta().getTotalDuration();
//동영상의 0초 이미지 잘라내기
Picture thumbnail=grab.getNativeFrame();
BufferedImage bufferedImage = AWTUtil.toBufferedImage(thumbnail);
//잘라낸 이미지의 해상도 추출하기(동영상의 해상도를 직접 알아낼 수 있는 방법이 없기에 사진을 잘라 해상도를 알아냄)
int width= (int) (bufferedImage.getWidth()/this.imageThumbRatio);
int height= (int) (bufferedImage.getHeight()/this.imageThumbRatio);
//저장하려는 경로가 없다면 생성
File folder=new File(streamingThumbnailPath+File.separator+media.getFileNameWithoutExtension());
if (!folder.exists())
folder.mkdirs();
//모든 쓰레드 작업들을 담을 배열 생성
ArrayList<Thread> threadList=new ArrayList<Thread>();
//초단위로 잘라내야하기 때문에 time을 1간격으로 동영상 재생시간만큼 반복
for(int time=0;time<=durationInSeconds;time++) {
File saveThumbnailPath=new File(folder,time+"."+this.thumbnailImageFormat);
Runnable r = new ThreadStreamImage(time,saveThumbnailPath,source,width,height,this.thumbnailImageFormat);
Thread thread = new Thread(r);
//쓰레드 작업을 배열에 추가 및 시작
threadList.add(thread);
thread.start();
}
//모든 작업이 끝날떄까지 대기
for(Thread thread:threadList) {
thread.join();
}
비교
public class FileUtilImpl implements FileUtil{
private void generateVideoStreamingThumbnail(File source,MediaDto media) throws Exception{
FrameGrab grab = FrameGrab.createFrameGrab(NIOUtils.readableChannel(source));
int durationInSeconds = (int)grab.getVideoTrack().getMeta().getTotalDuration();
Picture thumbnail=grab.getNativeFrame();
BufferedImage bufferedImage = AWTUtil.toBufferedImage(thumbnail);
int width= (int) (bufferedImage.getWidth()/this.imageThumbRatio);
int height= (int) (bufferedImage.getHeight()/this.imageThumbRatio);
File folder=new File(streamingThumbnailPath+File.separator+media.getFileNameWithoutExtension());
if (!folder.exists())
folder.mkdirs();
for(int time=0;time<=durationInSeconds;time++) {
grab.seekToSecondPrecise(time);
thumbnail=grab.getNativeFrame();
bufferedImage = AWTUtil.toBufferedImage(thumbnail);
Thumbnails.of(bufferedImage)
.size(width,height)
.outputFormat(thumbnailImageFormat)
.toFile(new File(folder,time+"."+this.thumbnailImageFormat));
}
}
}
성능
singleThread
multiThread
주의점