Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

인코딩된 영상 링크들을 제공하도록 수정 #46

Merged
merged 7 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.j9.bestmoments.converter;

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import java.util.Arrays;
import java.util.List;

@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {

private static final String SEPARATOR = ",";

@Override
public String convertToDatabaseColumn(List<String> attribute) {
return String.join(SEPARATOR, attribute);
}

@Override
public List<String> convertToEntityAttribute(String dbData) {
return Arrays.asList(dbData.split(SEPARATOR));
}

}
13 changes: 12 additions & 1 deletion src/main/java/com/j9/bestmoments/domain/Video.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.j9.bestmoments.domain;

import com.j9.bestmoments.converter.StringListConverter;
import jakarta.persistence.Convert;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
Expand All @@ -19,7 +21,6 @@
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.GenericGenerator;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

Expand All @@ -32,8 +33,13 @@ public class Video {

@Id
private UUID id;

private String videoUrl;
private String thumbnailUrl;
@Lob
@Convert(converter = StringListConverter.class)
private List<String> encodedVideoUrls;

private String title;
@Lob
private String description;
Expand All @@ -60,6 +66,7 @@ public Video(Member uploader, String title, String description, VideoStatus vide
this.description = description;
this.videoStatus = videoStatus;
this.isDeleted = false;
this.encodedVideoUrls = new ArrayList<>();
}

public void softDelete() {
Expand Down Expand Up @@ -90,6 +97,10 @@ public void setVideoUrl(String videoUrl) {
this.videoUrl = videoUrl;
}

public void addEncodedVideoUrl(String videoUrl) {
this.encodedVideoUrls.add(videoUrl);
}

public void setThumbnailUrl(String thumbnailUrl) {
this.thumbnailUrl = thumbnailUrl;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import com.j9.bestmoments.domain.Video;
import com.j9.bestmoments.domain.VideoStatus;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

public record VideoFindDto(
UUID id,
String videoUrl,
List<String> videoUrls,
String thumbnailUrl,
String title,
String description,
Expand All @@ -20,7 +21,7 @@ public record VideoFindDto(
public static VideoFindDto of (Video video) {
return new VideoFindDto(
video.getId(),
video.getVideoUrl(),
video.getEncodedVideoUrls(),
video.getThumbnailUrl(),
video.getTitle(),
video.getDescription(),
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/j9/bestmoments/service/FfmpegService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.j9.bestmoments.service;

import jakarta.persistence.criteria.CriteriaBuilder.In;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -18,6 +19,17 @@ public class FfmpegService {
@Async
public void encodeVideo(String inputFilePath, String outputFilePath, String resolution) {
try {
// 해상도가 홀수인 경우 조정
int width = Integer.parseInt(resolution.split("x")[0]);
int height = Integer.parseInt(resolution.split("x")[0]);
if (width % 2 == 1) {
width++;
}
if (height % 2 == 1) {
height++;
}
resolution = width + "x" + height;

ProcessBuilder processBuilder = new ProcessBuilder(
ffmpegPath, "-i", inputFilePath, "-s", resolution, "-codec:v", "libx264", outputFilePath
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.j9.bestmoments.util;
package com.j9.bestmoments.service;

import com.j9.bestmoments.domain.Member;
import com.j9.bestmoments.domain.Video;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.web.multipart.MultipartFile;

public final class FileNameGenerator {
public final class FileNameProvider {

public static String generateProfileImageFileName(Member member, MultipartFile file) {
String memberId = member.getId().toString();
Expand Down
4 changes: 1 addition & 3 deletions src/main/java/com/j9/bestmoments/service/MemberService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
import com.j9.bestmoments.domain.Member;
import com.j9.bestmoments.repository.MemberRepository;
import com.j9.bestmoments.service.storageService.StorageService;
import com.j9.bestmoments.util.FileNameGenerator;
import jakarta.persistence.EntityNotFoundException;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -68,7 +66,7 @@ public Member update(Member member, MemberUpdateDto memberUpdateDto) {
member.setDescription(memberUpdateDto.description());
}
if (memberUpdateDto.file() != null) {
String fileName = FileNameGenerator.generateProfileImageFileName(member, memberUpdateDto.file());
String fileName = FileNameProvider.generateProfileImageFileName(member, memberUpdateDto.file());
String profileImageUrl = googleCloudStorageService.uploadFile(memberUpdateDto.file(), fileName);
member.setProfileImageUrl(profileImageUrl);
}
Expand Down
49 changes: 26 additions & 23 deletions src/main/java/com/j9/bestmoments/service/VideoService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import com.j9.bestmoments.dto.request.VideoUpdateDto;
import com.j9.bestmoments.repository.VideoRepository;
import com.j9.bestmoments.service.storageService.LocalStorageService;
import com.j9.bestmoments.util.FileNameGenerator;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.criteria.CriteriaBuilder.In;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
Expand All @@ -28,6 +28,8 @@ public class VideoService {
private final LocalStorageService storageService;
private final FfmpegService ffmpegService;

private final static int[] STANDARD_RESOLUTION_HEIGHTS = {144, 240, 360, 480, 720, 1080, 1440, 2160};

@Transactional
public Video upload(Member member, VideoCreateDto createDto) {
Video video = Video.builder()
Expand All @@ -38,41 +40,42 @@ public Video upload(Member member, VideoCreateDto createDto) {
.build();

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

// 썸네일 이미지
String thumbnailName = FileNameGenerator.generateThumbnailImageFileName(video, createDto.thumbnail());
String thumbnailName = FileNameProvider.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);
String originalResolution = ffmpegService.getVideoResolution(originVideoUrl);
String originalSizeEncodedVideoUrl = uploadEncodedVideo(originVideoUrl, originalResolution);

int originalWidth = Integer.parseInt(originalResolution.split("x")[0]);
int originalHeight = Integer.parseInt(originalResolution.split("x")[1]);

for (int height : STANDARD_RESOLUTION_HEIGHTS) {
// 원본 화질보다 작은 화질로만 인코딩
if (height >= originalHeight) {
break;
}
int width = originalWidth * height / originalHeight;
String resolution = width + "x" + height;
String encodedVideoUrl = uploadEncodedVideo(originVideoUrl, resolution);
video.addEncodedVideoUrl(encodedVideoUrl);
}

// 가장 큰 원본 화질은 가장 마지막에 추가
video.addEncodedVideoUrl(originalSizeEncodedVideoUrl);

videoRepository.save(video);
return video;
}

private String uploadEncodedVideo(String videoUrl, String resolution) {
String encodedVideoUrl = FileNameGenerator.generateEncodedVideoFileName(videoUrl, resolution);
String encodedVideoUrl = FileNameProvider.generateEncodedVideoFileName(videoUrl, resolution);
ffmpegService.encodeVideo(videoUrl, encodedVideoUrl, resolution);
return encodedVideoUrl;
}
Expand Down Expand Up @@ -109,7 +112,7 @@ public Video findPublicById(UUID id) {
@Transactional
public Video update(Video video, VideoUpdateDto updateDto) {
if (updateDto.thumbnail() != null) {
String thumbnailName = FileNameGenerator.generateThumbnailImageFileName(video, updateDto.thumbnail());
String thumbnailName = FileNameProvider.generateThumbnailImageFileName(video, updateDto.thumbnail());
String thumbnailUrl = storageService.uploadFile(updateDto.thumbnail(), thumbnailName);
video.setThumbnailUrl(thumbnailUrl);
}
Expand Down