From fa89c66260c2cf3ef2063a355929022e7781fa8d Mon Sep 17 00:00:00 2001 From: ThisBirchWood Date: Tue, 8 Jul 2025 18:26:47 +0200 Subject: [PATCH] REFACTOR clipService and ADD ffmpegService --- frontend/src/pages/MyClips.tsx | 0 .../ddf/vodsystem/services/ClipService.java | 97 ++----------------- .../ddf/vodsystem/services/FfmpegService.java | 91 +++++++++++++++++ .../vodsystem/services/MetadataService.java | 10 ++ 4 files changed, 107 insertions(+), 91 deletions(-) create mode 100644 frontend/src/pages/MyClips.tsx create mode 100644 src/main/java/com/ddf/vodsystem/services/FfmpegService.java diff --git a/frontend/src/pages/MyClips.tsx b/frontend/src/pages/MyClips.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/com/ddf/vodsystem/services/ClipService.java b/src/main/java/com/ddf/vodsystem/services/ClipService.java index b1bd7f4..e7e209d 100644 --- a/src/main/java/com/ddf/vodsystem/services/ClipService.java +++ b/src/main/java/com/ddf/vodsystem/services/ClipService.java @@ -7,8 +7,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -25,21 +23,20 @@ import org.springframework.stereotype.Service; public class ClipService { private static final Logger logger = LoggerFactory.getLogger(ClipService.class); - private static final float AUDIO_RATIO = 0.15f; - private static final float MAX_AUDIO_BITRATE = 128f; - private static final float BITRATE_MULTIPLIER = 0.9f; - private final ClipRepository clipRepository; private final MetadataService metadataService; private final DirectoryService directoryService; + private final FfmpegService ffmpegService; private final Pattern timePattern = Pattern.compile("out_time_ms=(\\d+)"); public ClipService(ClipRepository clipRepository, MetadataService metadataService, - DirectoryService directoryService) { + DirectoryService directoryService, + FfmpegService ffmpegService) { this.clipRepository = clipRepository; this.metadataService = metadataService; this.directoryService = directoryService; + this.ffmpegService = ffmpegService; } /** @@ -55,10 +52,9 @@ public class ClipService { */ public void run(Job job) throws IOException, InterruptedException { logger.info("FFMPEG starting..."); + metadataService.normalizeVideoMetadata(job.getInputVideoMetadata(), job.getOutputVideoMetadata()); - validateVideoMetadata(job.getInputVideoMetadata(), job.getOutputVideoMetadata()); - - ProcessBuilder pb = buildCommand(job.getInputFile(), job.getOutputFile(), job.getOutputVideoMetadata()); + ProcessBuilder pb = ffmpegService.buildCommand(job.getInputFile(), job.getOutputFile(), job.getOutputVideoMetadata()); Process process = pb.start(); job.setStatus(JobStatus.RUNNING); @@ -105,87 +101,6 @@ public class ClipService { return null; } - private void validateVideoMetadata(VideoMetadata inputFileMetadata, VideoMetadata outputFileMetadata) { - if (outputFileMetadata.getStartPoint() == null) { - outputFileMetadata.setStartPoint(0f); - } - - if (outputFileMetadata.getEndPoint() == null) { - outputFileMetadata.setEndPoint(inputFileMetadata.getEndPoint()); - } - } - - private void buildFilters(ArrayList command, Float fps, Integer width, Integer height) { - List filters = new ArrayList<>(); - - if (fps != null) { - filters.add("fps=" + fps); - } - - if (!(width == null && height == null)) { - String w = (width != null) ? width.toString() : "-1"; - String h = (height != null) ? height.toString() : "-1"; - filters.add("scale=" + w + ":" + h); - } - - if (!filters.isEmpty()) { - command.add("-vf"); - command.add(String.join(",", filters)); - } - } - - private void buildBitrate(ArrayList command, Float length, Float fileSize) { - float bitrate = ((fileSize * 8) / length) * BITRATE_MULTIPLIER; - - float audioBitrate = bitrate * AUDIO_RATIO; - float videoBitrate; - - if (audioBitrate > MAX_AUDIO_BITRATE) { - audioBitrate = MAX_AUDIO_BITRATE; - videoBitrate = bitrate - MAX_AUDIO_BITRATE; - } else { - videoBitrate = bitrate * (1 - AUDIO_RATIO); - } - - command.add("-b:v"); - command.add(videoBitrate + "k"); - command.add("-b:a"); - command.add(audioBitrate + "k"); - } - - private void buildInputs(ArrayList command, File inputFile, Float startPoint, Float length) { - command.add("-ss"); - command.add(startPoint.toString()); - - command.add("-i"); - command.add(inputFile.getAbsolutePath()); - - command.add("-t"); - command.add(Float.toString(length)); - } - - private ProcessBuilder buildCommand(File inputFile, File outputFile, VideoMetadata videoMetadata) { - ArrayList command = new ArrayList<>(); - command.add("ffmpeg"); - command.add("-progress"); - command.add("pipe:1"); - command.add("-y"); - - Float length = videoMetadata.getEndPoint() - videoMetadata.getStartPoint(); - buildInputs(command, inputFile, videoMetadata.getStartPoint(), length); - buildFilters(command, videoMetadata.getFps(), videoMetadata.getWidth(), videoMetadata.getHeight()); - - if (videoMetadata.getFileSize() != null) { - buildBitrate(command, length, videoMetadata.getFileSize()); - } - - // Output file - command.add(outputFile.getAbsolutePath()); - - logger.info("Running command: {}", command); - return new ProcessBuilder(command); - } - private void persistClip(VideoMetadata videoMetadata, User user, Job job) { // Move clip from temp to output directory String fileExtension = directoryService.getFileExtension(job.getOutputFile().getAbsolutePath()); diff --git a/src/main/java/com/ddf/vodsystem/services/FfmpegService.java b/src/main/java/com/ddf/vodsystem/services/FfmpegService.java new file mode 100644 index 0000000..dd8d495 --- /dev/null +++ b/src/main/java/com/ddf/vodsystem/services/FfmpegService.java @@ -0,0 +1,91 @@ +package com.ddf.vodsystem.services; + +import com.ddf.vodsystem.entities.VideoMetadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +@Service +public class FfmpegService { + private static final Logger logger = LoggerFactory.getLogger(FfmpegService.class); + + private static final float AUDIO_RATIO = 0.15f; + private static final float MAX_AUDIO_BITRATE = 128f; + private static final float BITRATE_MULTIPLIER = 0.9f; + + private void buildFilters(ArrayList command, Float fps, Integer width, Integer height) { + List filters = new ArrayList<>(); + + if (fps != null) { + logger.info("Frame rate set to {}", fps); + filters.add("fps=" + fps); + } + + if (!(width == null && height == null)) { + logger.info("Scaling video to width: {}, height: {}", width, height); + String w = (width != null) ? width.toString() : "-1"; + String h = (height != null) ? height.toString() : "-1"; + filters.add("scale=" + w + ":" + h); + } + + if (!filters.isEmpty()) { + logger.info("Adding video filters"); + command.add("-vf"); + command.add(String.join(",", filters)); + } + } + + private void buildBitrate(ArrayList command, Float length, Float fileSize) { + float bitrate = ((fileSize * 8) / length) * BITRATE_MULTIPLIER; + + float audioBitrate = bitrate * AUDIO_RATIO; + float videoBitrate; + + if (audioBitrate > MAX_AUDIO_BITRATE) { + audioBitrate = MAX_AUDIO_BITRATE; + videoBitrate = bitrate - MAX_AUDIO_BITRATE; + } else { + videoBitrate = bitrate * (1 - AUDIO_RATIO); + } + + command.add("-b:v"); + command.add(videoBitrate + "k"); + command.add("-b:a"); + command.add(audioBitrate + "k"); + } + + private void buildInputs(ArrayList command, File inputFile, Float startPoint, Float length) { + command.add("-ss"); + command.add(startPoint.toString()); + + command.add("-i"); + command.add(inputFile.getAbsolutePath()); + + command.add("-t"); + command.add(Float.toString(length)); + } + + public ProcessBuilder buildCommand(File inputFile, File outputFile, VideoMetadata videoMetadata) { + ArrayList command = new ArrayList<>(); + command.add("ffmpeg"); + command.add("-progress"); + command.add("pipe:1"); + command.add("-y"); + + Float length = videoMetadata.getEndPoint() - videoMetadata.getStartPoint(); + buildInputs(command, inputFile, videoMetadata.getStartPoint(), length); + buildFilters(command, videoMetadata.getFps(), videoMetadata.getWidth(), videoMetadata.getHeight()); + + if (videoMetadata.getFileSize() != null) { + buildBitrate(command, length, videoMetadata.getFileSize()); + } + + // Output file + command.add(outputFile.getAbsolutePath()); + return new ProcessBuilder(command); + } +} diff --git a/src/main/java/com/ddf/vodsystem/services/MetadataService.java b/src/main/java/com/ddf/vodsystem/services/MetadataService.java index 3c19a06..080dd16 100644 --- a/src/main/java/com/ddf/vodsystem/services/MetadataService.java +++ b/src/main/java/com/ddf/vodsystem/services/MetadataService.java @@ -63,6 +63,16 @@ public class MetadataService { return metadata.getEndPoint(); } + public void normalizeVideoMetadata(VideoMetadata inputFileMetadata, VideoMetadata outputFileMetadata) { + if (outputFileMetadata.getStartPoint() == null) { + outputFileMetadata.setStartPoint(0f); + } + + if (outputFileMetadata.getEndPoint() == null) { + outputFileMetadata.setEndPoint(inputFileMetadata.getEndPoint()); + } + } + private JsonNode readStandardOutput(Process process) throws IOException{ // Read the standard output (JSON metadata) BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));