영상 스트리밍 #5. 썸네일 , 영상길이 추출

Bobby·2023년 3월 5일
1

streaming

목록 보기
5/9

업로드한 영상에서 썸네일과 영상길이를 추출하는 방법을 알아보자.

🎥 Jcodec 이용

의존성

implementation 'org.jcodec:jcodec-javase:0.2.5'

썸네일 추출

  • 썸네일 추출 클래스를 생성

ThumbnailExtractor

@Slf4j
public class ThumbnailExtractor {

    private static final String EXTENSION = "png";
    private static final String DEFAULT_IMAGE_PATH = "src/main/resources/static/images/default-thumbnail.png";

    public static String extract(File source) throws IOException {
    	// 썸네일 파일 생성
        File thumbnail = new File(source.getParent(), source.getName().split("\\.")[0] + "." + EXTENSION);

        try {
            FrameGrab frameGrab = FrameGrab.createFrameGrab(NIOUtils.readableChannel(source));
		
        	// 첫 프레임의 데이터
            frameGrab.seekToSecondPrecise(0);

            Picture picture = frameGrab.getNativeFrame();

			// 썸네일 파일에 복사
            BufferedImage bi = AWTUtil.toBufferedImage(picture);
            ImageIO.write(bi, EXTENSION, thumbnail);

        } catch (Exception e) {
        	// 실패했을 경우에 기본 이미지를 사용
            File defaultImage = new File(DEFAULT_IMAGE_PATH);

            try {
                FileUtils.copyFile(defaultImage, thumbnail);
            } catch (Exception ex) {
                log.info("Thumbnail Extract Failed => {}", source.getPath(), e);
            }
        }

        return thumbnail.getName();
    }
}

영상 길이 추출

  • 영상 길이 추출 클래스 생성

DurationExtractor

@Slf4j
public class DurationExtractor {

    public static double extract(File source) throws IOException {
        try {
            FrameGrab frameGrab = FrameGrab.createFrameGrab(NIOUtils.readableChannel(source));
            double durationInSeconds = frameGrab.getVideoTrack().getMeta().getTotalDuration();
            log.info("Video length: {} seconds", durationInSeconds);
            return durationInSeconds;
        } catch (Exception e) {
            log.warn("Duration extract failed", e);
        }

        return 0;
    }
}

사용

  • 업로드 한 파일을 저장할 때 썸네일, 영상길이 함께 추출

TusService

@Slf4j
@Service
@RequiredArgsConstructor
public class TusService {

    ...
    
    private void createFile(InputStream is, String filename) throws IOException {
        LocalDate today = LocalDate.now();

        String uploadedPath = savePath + "/" + today;

        String vodName = getVodName(filename);

        File file = new File(uploadedPath, vodName);

        FileUtils.copyInputStreamToFile(is, file);
		
        // 썸네일 추출
        ThumbnailExtractor.extract(file);
        
        // 영상 길이 추출
        DurationExtractor.extract(file);

    }
	
    ...
    
}

테스트

http://localhost:8080/tus

  • 영상 업로드 경로와 같은 곳에 같은 이름으로 저장했다.
  • 영상 길이 확인


🎥 FFmpeg 이용

ffmpeg 설치

ex) mac os에서 설치하는 방법

brew install ffmpeg

ffmpeg가 설치된 위치를 확인

which ffmpeg

의존성

  • 설치한 ffmpeg의 cli 명령을 java에서 쉽게 할 수 있는 wrapper 라이브러리
implementation 'net.bramp.ffmpeg:ffmpeg:0.7.0'

설정

  • ffmpeg 가 설치된 경로로 빈 등록

application.yml

ffmpeg:
  path: /usr/local/bin/ffmpeg
ffprobe:
  path: /usr/local/bin/ffprobe

FFmpegConfig

@Slf4j
@Configuration
public class FFmpegConfig {
    @Value("${ffmpeg.path}")
    private String ffmpegPath;

    @Value("${ffprobe.path}")
    private String ffprobePath;

    @Bean
    public FFmpeg ffMpeg() throws IOException {
        return new FFmpeg(ffmpegPath);
    }

    @Bean
    public FFprobe ffProbe() throws IOException {
        return new FFprobe(ffprobePath);
    }
}

썸네일 추출

FFmpegManager

@Slf4j
@Component
@RequiredArgsConstructor
public class FFmpegManager {

    private final FFmpeg ffmpeg;
    private final FFprobe ffprobe;

    private static final String THUMBNAIL_EXTENSION = ".png";
    private static final String DEFAULT_IMAGE_PATH = "src/main/resources/static/images/default-thumbnail.png";


    public void getThumbnail(String sourcePath) {
    	// 썸네일 저장할 경로
        final String outputPath = sourcePath.split("\\.")[0] + THUMBNAIL_EXTENSION;

        try {
        	// ffmpeg cli 명령어 생성
            FFmpegBuilder builder = new FFmpegBuilder()
                    .setInput(sourcePath)
                    .overrideOutputFiles(true)
                    .addOutput(outputPath)
                    .setFormat("image2")
                    .setFrames(1)
                    .setVideoFrameRate(1)
                    .done();
				
            // 명령어 실행
            ffmpeg.run(builder);
        } catch (Exception e) {
        	// 썸네일 추출 실패시 기본 이미지 썸네일로 사용
            File thumbnail = new File(outputPath);
            File defaultImage = new File(DEFAULT_IMAGE_PATH);

            try {
                FileUtils.copyFile(defaultImage, thumbnail);
            } catch (Exception ex) {
                log.error("Thumbnail Extract Failed => {}", sourcePath, e);
            }
        }
    }

}

영상 길이 추출

FFmpegManager

@Slf4j
@Component
@RequiredArgsConstructor
public class FFmpegManager {

 	...

    public void getDuration(String sourcePath) throws IOException {
    	// 영상 경로
        Path videoPath = Paths.get(sourcePath);

		// 영상 메타데이터 조회
        FFmpegProbeResult probeResult = ffprobe.probe(videoPath.toString());

		// 영상 길이 추출
        FFmpegStream videoStream = probeResult.getStreams().get(0);
        double durationInSeconds = videoStream.duration;

        System.out.println("Video length: " + durationInSeconds + " seconds");
    }
}

사용

  • 업로드 한 파일을 저장할 때 썸네일, 영상길이 함께 추출

TusService

@Slf4j
@Service
@RequiredArgsConstructor
public class TusService {

    ...
    
    private final FFmpegManager ffmpegManager;

 	...
 
    private void createFile(InputStream is, String filename) throws IOException {
        LocalDate today = LocalDate.now();

        String uploadedPath = savePath + "/" + today;

        String vodName = getVodName(filename);

        File file = new File(uploadedPath, vodName);

        FileUtils.copyInputStreamToFile(is, file);
		
        // 썸네일 추출
        ffmpegManager.getThumbnail(file.getAbsolutePath());
        
        // 영상 길이 추출
        ffmpegManager.getDuration(file.getAbsolutePath());

    }
	
    ...
    
}

테스트

http://localhost:8080/tus

  • 썸네일 확인
  • 영상 길이 확인

코드

profile
물흐르듯 개발하다 대박나기

0개의 댓글