Skip to content

Commit

Permalink
Merge pull request #45 from Team-J9/feature/ffmpeg
Browse files Browse the repository at this point in the history
Feature/ffmpeg
  • Loading branch information
SJ70 authored Oct 1, 2024
2 parents e9cb0e0 + 9cf7e32 commit a9ea37f
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/main/java/com/j9/bestmoments/BestMomentsApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class BestMomentsApplication {

public static void main(String[] args) {
Expand Down
79 changes: 79 additions & 0 deletions src/main/java/com/j9/bestmoments/service/FfmpegService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.j9.bestmoments.service;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.io.IOException;

@Slf4j
@Service
public class FfmpegService {

@Value("${ffmpeg.path}")
private String ffmpegPath;

@Async
public void encodeVideo(String inputFilePath, String outputFilePath, String resolution) {
try {
ProcessBuilder processBuilder = new ProcessBuilder(
ffmpegPath, "-i", inputFilePath, "-s", resolution, "-codec:v", "libx264", outputFilePath
);

Process process = processBuilder.start();

// 로그 스트림 처리
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line); // 로그를 출력하거나 저장할 수 있습니다.
}
}

int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException("비디오 인코딩 실패");
}

log.info("인코딩 완료 : {}", outputFilePath);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}

public String getVideoResolution(String inputFilePath) {
try {
ProcessBuilder processBuilder = new ProcessBuilder(
ffmpegPath, "-i", inputFilePath
);

Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line;
String resolution = null;

// FFmpeg의 출력에서 해상도를 파싱하여 추출
while ((line = reader.readLine()) != null) {
if (line.contains("Video:")) {
String[] parts = line.split(",");
for (String part : parts) {
if (part.trim().matches("\\d{2,}x\\d{2,}")) {
resolution = part.trim(); // 해상도 부분을 찾음
break;
}
}
}
}

process.waitFor();
return resolution;

} catch (Exception e) {
e.printStackTrace();
return "해상도 파싱 실패";
}
}

}
37 changes: 34 additions & 3 deletions src/main/java/com/j9/bestmoments/service/VideoService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import com.j9.bestmoments.service.storageService.LocalStorageService;
import com.j9.bestmoments.util.FileNameGenerator;
import jakarta.persistence.EntityNotFoundException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
Expand All @@ -24,6 +26,7 @@ public class VideoService {

private final VideoRepository videoRepository;
private final LocalStorageService storageService;
private final FfmpegService ffmpegService;

@Transactional
public Video upload(Member member, VideoCreateDto createDto) {
Expand All @@ -34,18 +37,46 @@ public Video upload(Member member, VideoCreateDto createDto) {
.description(createDto.description())
.build();

String videoName = FileNameGenerator.generateVideoFileName(video, createDto.video());
String videoUrl = storageService.uploadFile(createDto.video(), videoName);
video.setVideoUrl(videoUrl);
// 원본 영상
String originVideoName = FileNameGenerator.generateVideoFileName(video, createDto.video());
String originVideoUrl = storageService.uploadFile(createDto.video(), originVideoName);
video.setVideoUrl(originVideoUrl);

// 썸네일 이미지
String thumbnailName = FileNameGenerator.generateThumbnailImageFileName(video, createDto.thumbnail());
String thumbnailUrl = storageService.uploadFile(createDto.thumbnail(), thumbnailName);
video.setThumbnailUrl(thumbnailUrl);

// 원본 사이즈 인코딩
String resolution = ffmpegService.getVideoResolution(originVideoUrl);
String encodedVideoUrl = uploadEncodedVideo(originVideoUrl, resolution);

// 1/2 사이즈 인코딩
String halfResolution = Arrays.stream(resolution.split("x"))
.mapToInt(Integer::parseInt)
.map(value -> value / 2)
.mapToObj(String::valueOf)
.collect(Collectors.joining("x"));
String halfEncodedVideoUrl = uploadEncodedVideo(originVideoUrl, halfResolution);

// 1/4 사이즈 인코딩
String quarterResolution = Arrays.stream(resolution.split("x"))
.mapToInt(Integer::parseInt)
.map(value -> value / 4)
.mapToObj(String::valueOf)
.collect(Collectors.joining("x"));
String quarterEncodedVideoUrl = uploadEncodedVideo(originVideoUrl, quarterResolution);

videoRepository.save(video);
return video;
}

private String uploadEncodedVideo(String videoUrl, String resolution) {
String encodedVideoUrl = FileNameGenerator.generateEncodedVideoFileName(videoUrl, resolution);
ffmpegService.encodeVideo(videoUrl, encodedVideoUrl, resolution);
return encodedVideoUrl;
}

public Page<Video> findAll(Pageable pageable) {
return videoRepository.findAll(
PageRequest.of(
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/j9/bestmoments/util/FileNameGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ public static String generateVideoFileName(Video video, MultipartFile file) {
return String.format("video/%s/video-origin.%s", videoId, contentType);
}

public static String generateEncodedVideoFileName(String videoFileName, String resolution) {
String[] splitFileName = videoFileName.split("-origin.");
String originVideoFileName = splitFileName[0];
return String.format("%s-%s.mp4", originVideoFileName, resolution);
}

public static String generateThumbnailImageFileName(Video video, MultipartFile file) {
String videoId = video.getId().toString();
String dateString = generateDateString();
Expand Down

0 comments on commit a9ea37f

Please sign in to comment.