From a639cfbb0eece993bdda681b7db4d10c07cb4e78 Mon Sep 17 00:00:00 2001 From: ThisBirchWood Date: Tue, 20 May 2025 13:56:00 +0200 Subject: [PATCH] ADD MetadataService to track inital metadata --- .../java/com/ddf/vodsystem/entities/Job.java | 6 +- .../services/CompressionService.java | 4 +- .../ddf/vodsystem/services/EditService.java | 2 +- .../vodsystem/services/MetadataService.java | 123 ++++++++++++++++++ .../ddf/vodsystem/services/UploadService.java | 8 +- 5 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/ddf/vodsystem/services/MetadataService.java diff --git a/src/main/java/com/ddf/vodsystem/entities/Job.java b/src/main/java/com/ddf/vodsystem/entities/Job.java index a0b4a50..fe327e3 100644 --- a/src/main/java/com/ddf/vodsystem/entities/Job.java +++ b/src/main/java/com/ddf/vodsystem/entities/Job.java @@ -15,15 +15,17 @@ public class Job { private File outputFile; // configs - private VideoMetadata videoMetadata; + private VideoMetadata inputVideoMetadata; + private VideoMetadata outputVideoMetadata; // job status private JobStatus status = JobStatus.NOT_READY; private Float progress = 0.0f; - public Job(String uuid, File inputFile, File outputFile) { + public Job(String uuid, File inputFile, File outputFile, VideoMetadata inputVideoMetadata) { this.uuid = uuid; this.inputFile = inputFile; this.outputFile = outputFile; + this.inputVideoMetadata = inputVideoMetadata; } } diff --git a/src/main/java/com/ddf/vodsystem/services/CompressionService.java b/src/main/java/com/ddf/vodsystem/services/CompressionService.java index e04783f..fb0ebad 100644 --- a/src/main/java/com/ddf/vodsystem/services/CompressionService.java +++ b/src/main/java/com/ddf/vodsystem/services/CompressionService.java @@ -109,12 +109,12 @@ public class CompressionService { public void run(Job job) throws IOException, InterruptedException { logger.info("FFMPEG starting..."); - ProcessBuilder pb = buildCommand(job.getInputFile(), job.getOutputFile(), job.getVideoMetadata()); + ProcessBuilder pb = buildCommand(job.getInputFile(), job.getOutputFile(), job.getOutputVideoMetadata()); Process process = pb.start(); job.setStatus(JobStatus.RUNNING); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - float length = job.getVideoMetadata().getEndPoint() - job.getVideoMetadata().getStartPoint(); + float length = job.getOutputVideoMetadata().getEndPoint() - job.getOutputVideoMetadata().getStartPoint(); String line; while ((line = reader.readLine()) != null) { diff --git a/src/main/java/com/ddf/vodsystem/services/EditService.java b/src/main/java/com/ddf/vodsystem/services/EditService.java index 4c08a59..d702463 100644 --- a/src/main/java/com/ddf/vodsystem/services/EditService.java +++ b/src/main/java/com/ddf/vodsystem/services/EditService.java @@ -16,7 +16,7 @@ public class EditService { public void edit(String uuid, VideoMetadata videoMetadata) { Job job = jobService.getJob(uuid); validateClipConfig(videoMetadata); - job.setVideoMetadata(videoMetadata); + job.setOutputVideoMetadata(videoMetadata); } public void process(String uuid) { diff --git a/src/main/java/com/ddf/vodsystem/services/MetadataService.java b/src/main/java/com/ddf/vodsystem/services/MetadataService.java new file mode 100644 index 0000000..51fe0a4 --- /dev/null +++ b/src/main/java/com/ddf/vodsystem/services/MetadataService.java @@ -0,0 +1,123 @@ +package com.ddf.vodsystem.services; + +import com.ddf.vodsystem.entities.VideoMetadata; +import com.ddf.vodsystem.exceptions.FFMPEGException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; + +@Service +public class MetadataService { + private static Logger logger = LoggerFactory.getLogger(MetadataService.class); + + public VideoMetadata getVideoMetadata(File file) { + ProcessBuilder pb = new ProcessBuilder("ffprobe", + "-v", "quiet", + "-print_format", "json", + "-show_format", "-select_streams", + "v:0", "-show_entries", "stream=duration,width,height,r_frame_rate:format=size,duration", + "-i", file.getAbsolutePath()); + + Process process; + + try { + process = pb.start(); + handleFfprobeError(process); + return parseVideoMetadata(readStandardOutput(process)); + } catch (IOException | InterruptedException e) { + Thread.currentThread().interrupt(); + throw new FFMPEGException(e.getMessage()); + } + + } + + private JsonNode readStandardOutput(Process process) throws IOException{ + // Read the standard output (JSON metadata) + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + StringBuilder jsonOutput = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + jsonOutput.append(line); + } + + // Parse the JSON output + ObjectMapper mapper = new ObjectMapper(); + return mapper.readTree(jsonOutput.toString()); + } + + private void handleFfprobeError(Process process) throws IOException, InterruptedException { + int exitCode = process.waitFor(); + if (exitCode != 0) { + BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); + StringBuilder errorOutput = new StringBuilder(); + String line; + while ((line = errorReader.readLine()) != null) { + errorOutput.append(line).append("\n"); + } + + throw new FFMPEGException("ffprobe exited with code " + exitCode + ". Error: " + errorOutput); + } + } + + private VideoMetadata parseVideoMetadata(JsonNode node) { + VideoMetadata metadata = new VideoMetadata(); + metadata.setStartPoint(0f); + + JsonNode streamNode = node.path("streams").get(0); + + // if stream doesn't exist + if (streamNode == null || streamNode.isMissingNode()) { + throw new FFMPEGException("ffprobe streams missing"); + } + + if (streamNode.has("duration")) { + metadata.setEndPoint(Float.valueOf(streamNode.get("duration").asText())); + } + + if (streamNode.has("width")) { + metadata.setWidth(streamNode.get("width").asInt()); + } + + if (streamNode.has("height")) { + metadata.setHeight(streamNode.get("height").asInt()); + } + + if (streamNode.has("r_frame_rate")) { + String fpsFraction = streamNode.get("r_frame_rate").asText(); + + if (fpsFraction.contains("/")) { + String[] parts = fpsFraction.split("/"); + double numerator = Float.parseFloat(parts[0]); + double denominator = Float.parseFloat(parts[1]); + if (denominator != 0) { + metadata.setFps((float) (numerator / denominator)); + } + } else { + metadata.setFps(Float.valueOf(fpsFraction)); // Handle cases like "25" directly + } + } + + // Extract from the 'format' section + JsonNode formatNode = node.path("format"); + if (formatNode != null && !formatNode.isMissingNode()) { + if (formatNode.has("size")) { + metadata.setFileSize(Float.parseFloat(formatNode.get("size").asText())); + } + + // Use format duration as a fallback or primary source if stream duration is absent/zero + if (formatNode.has("duration") && metadata.getEndPoint() == null) { + metadata.setEndPoint(Float.parseFloat(formatNode.get("duration").asText())); + } + } + + return metadata; + } + +} diff --git a/src/main/java/com/ddf/vodsystem/services/UploadService.java b/src/main/java/com/ddf/vodsystem/services/UploadService.java index 798efee..e19291c 100644 --- a/src/main/java/com/ddf/vodsystem/services/UploadService.java +++ b/src/main/java/com/ddf/vodsystem/services/UploadService.java @@ -1,6 +1,7 @@ package com.ddf.vodsystem.services; import com.ddf.vodsystem.entities.Job; +import com.ddf.vodsystem.entities.VideoMetadata; import com.vaadin.flow.server.auth.AnonymousAllowed; import com.vaadin.hilla.Endpoint; import jakarta.annotation.PostConstruct; @@ -34,10 +35,12 @@ public class UploadService { private String outputDir; private final JobService jobService; + private final MetadataService metadataService; @Autowired - public UploadService(JobService jobService) { + public UploadService(JobService jobService, MetadataService metadataService) { this.jobService = jobService; + this.metadataService = metadataService; } public String upload(MultipartFile file) { @@ -55,7 +58,8 @@ public class UploadService { moveToFile(file, inputFile); // add job - Job job = new Job(uuid, inputFile, outputFile); + VideoMetadata videoMetadata = metadataService.getVideoMetadata(inputFile); + Job job = new Job(uuid, inputFile, outputFile, videoMetadata); jobService.add(job); return uuid;