diff --git a/pom.xml b/pom.xml
index f1e30c5..df253f0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,7 +35,6 @@
com.vaadin
vaadin-spring-boot-starter
-
org.springframework.boot
spring-boot-devtools
diff --git a/src/main/java/com/ddf/vodsystem/controllers/EditController.java b/src/main/java/com/ddf/vodsystem/controllers/EditController.java
new file mode 100644
index 0000000..4497be5
--- /dev/null
+++ b/src/main/java/com/ddf/vodsystem/controllers/EditController.java
@@ -0,0 +1,31 @@
+package com.ddf.vodsystem.controllers;
+
+import com.ddf.vodsystem.entities.EditDTO;
+import com.ddf.vodsystem.services.EditService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+public class EditController {
+ private final EditService editService;
+
+ @Autowired
+ public EditController(EditService editService) {
+ this.editService = editService;
+ }
+
+ @PostMapping("edit/{uuid}")
+ public ResponseEntity edit(@PathVariable("uuid") String uuid, @ModelAttribute EditDTO editDTO) {
+ editService.edit(uuid, editDTO);
+ return new ResponseEntity<>(uuid, HttpStatus.OK);
+ }
+
+ @GetMapping("/convert/{uuid}")
+ public ResponseEntity convert(@PathVariable("uuid") String uuid) {
+ editService.jobReady(uuid);
+ return new ResponseEntity<>(uuid, HttpStatus.OK);
+ }
+
+}
diff --git a/src/main/java/com/ddf/vodsystem/entities/EditDTO.java b/src/main/java/com/ddf/vodsystem/entities/EditDTO.java
new file mode 100644
index 0000000..7b644e8
--- /dev/null
+++ b/src/main/java/com/ddf/vodsystem/entities/EditDTO.java
@@ -0,0 +1,13 @@
+package com.ddf.vodsystem.entities;
+
+import lombok.Data;
+
+@Data
+public class EditDTO {
+ private Float startPoint;
+ private Float endPoint;
+ private Float fps;
+ private Integer width;
+ private Integer height;
+ private Float fileSize;
+}
diff --git a/src/main/java/com/ddf/vodsystem/entities/Job.java b/src/main/java/com/ddf/vodsystem/entities/Job.java
index b8476e0..c21bd4d 100644
--- a/src/main/java/com/ddf/vodsystem/entities/Job.java
+++ b/src/main/java/com/ddf/vodsystem/entities/Job.java
@@ -1,7 +1,9 @@
package com.ddf.vodsystem.entities;
+import com.ddf.vodsystem.services.FfmpegService;
import lombok.Data;
import java.io.File;
+import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -14,15 +16,16 @@ public class Job implements Runnable {
private File file;
// configs
- private float startPoint;
- private float endPoint;
- private float fps;
- private int width;
- private int height;
- private float fileSize;
+ private Float startPoint;
+ private Float endPoint;
+ private Float fps;
+ private Integer width;
+ private Integer height;
+ private Float fileSize;
// job status
private JobStatus status = JobStatus.PENDING;
+ private Float progress = 0.0f;
public Job(String uuid, File file) {
this.uuid = uuid;
@@ -31,10 +34,26 @@ public class Job implements Runnable {
@Override
public void run() {
- logger.info("Job started");
+ logger.info("Job {} started", uuid);
this.status = JobStatus.RUNNING;
+ FfmpegService f = new FfmpegService(file, new File("output.mp4"));
+ f.setStartPoint(startPoint);
+ f.setEndPoint(endPoint);
+ f.setFps(fps);
+ f.setWidth(width);
+ f.setHeight(height);
+ f.setFileSize(fileSize);
+
+ try {
+ f.run();
+ } catch (IOException | InterruptedException e) {
+ logger.error(e.getMessage());
+ }
+
+
this.status = JobStatus.FINISHED;
+ logger.info("Job {} finished", uuid);
}
}
diff --git a/src/main/java/com/ddf/vodsystem/services/EditService.java b/src/main/java/com/ddf/vodsystem/services/EditService.java
new file mode 100644
index 0000000..5ec23c5
--- /dev/null
+++ b/src/main/java/com/ddf/vodsystem/services/EditService.java
@@ -0,0 +1,54 @@
+package com.ddf.vodsystem.services;
+
+import com.ddf.vodsystem.entities.EditDTO;
+import com.ddf.vodsystem.entities.Job;
+import org.springframework.stereotype.Service;
+
+@Service
+public class EditService {
+ private final JobService jobService;
+
+ public EditService(JobService jobService) {
+ this.jobService = jobService;
+ }
+
+ public void edit(String uuid, EditDTO editDTO) {
+ Job job = jobService.get(uuid);
+
+ if (editDTO.getStartPoint() != null) {
+ if (editDTO.getStartPoint() < 0) {
+ throw new IllegalArgumentException("Start point cannot be negative");
+ }
+
+ job.setStartPoint(editDTO.getStartPoint());
+ }
+
+ if (editDTO.getEndPoint() != null) {
+ job.setEndPoint(editDTO.getEndPoint());
+ }
+
+ if (editDTO.getFps() != null) {
+ job.setFps(editDTO.getFps());
+ }
+
+ if (editDTO.getWidth() != null) {
+ job.setWidth(editDTO.getWidth());
+ }
+
+ if (editDTO.getHeight() != null) {
+ job.setHeight(editDTO.getHeight());
+ }
+
+ if (editDTO.getFileSize() != null) {
+ if (editDTO.getFileSize() < 0) {
+ throw new IllegalArgumentException("File size cannot be negative");
+ }
+
+ job.setFileSize(editDTO.getFileSize());
+ }
+ }
+
+ public void jobReady(String uuid) {
+ jobService.jobReady(uuid);
+ }
+}
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..0f412e3
--- /dev/null
+++ b/src/main/java/com/ddf/vodsystem/services/FfmpegService.java
@@ -0,0 +1,140 @@
+package com.ddf.vodsystem.services;
+
+import lombok.Data;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.lang.Long.parseLong;
+
+@Data
+public class FfmpegService {
+ private static final Logger logger = LoggerFactory.getLogger(FfmpegService.class);
+
+ private List command;
+
+ private File inputFile;
+ private File outputFile;
+ private Float startPoint;
+ private Float endPoint;
+ private Integer width;
+ private Integer height;
+ private Float fps;
+ private Float fileSize;
+
+ private static final float AUDIO_RATIO = 0.2f;
+ private static final float BITRATE_MULTIPLIER = 0.9f;
+
+ private Pattern timePattern = Pattern.compile("time=([\\d:.]+)");
+ private long out_time_ms;
+
+ public FfmpegService(File file, File output) {
+ command = new ArrayList<>();
+ command.add("ffmpeg");
+ command.add("-progress");
+ command.add("pipe:1");
+ command.add("-y");
+
+ this.inputFile = file;
+ this.outputFile = output;
+
+ }
+
+ public void setResolution(Integer width, Integer height) {
+ this.width = width;
+ this.height = height;
+ }
+
+
+ private void buildFilters() {
+ List filters = new ArrayList<>();
+ System.out.println(fps);
+ if (fps != null) {
+ filters.add("fps=" + fps);
+ }
+
+ if ((width != null && height == null) || (height != null && width == 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() {
+ float length = endPoint - startPoint;
+ float bitrate = (fileSize / length) * BITRATE_MULTIPLIER;
+
+ float video_bitrate = bitrate * (1 - AUDIO_RATIO);
+ float audio_bitrate = bitrate * AUDIO_RATIO;
+
+ command.add("-b:v");
+ command.add(video_bitrate + "k");
+ command.add("-b:a");
+ command.add(audio_bitrate + "k");
+ }
+
+ private void buildInputs(){
+ if (startPoint != null) {
+ command.add("-ss");
+ command.add(startPoint.toString());
+ }
+
+ command.add("-i");
+ command.add(inputFile.getAbsolutePath());
+
+ if (endPoint != null) {
+ command.add("-t");
+
+ Float duration = endPoint - startPoint;
+ command.add(duration.toString());
+ }
+ }
+
+ private ProcessBuilder buildCommand() {
+ buildInputs();
+ buildFilters();
+
+ if (fileSize != null) {
+ buildBitrate();
+ }
+
+ // Output file
+ command.add(outputFile.getAbsolutePath());
+
+ logger.info("Running command: {}", String.join(" ", command));
+ return new ProcessBuilder(command);
+ }
+
+ public void run() throws IOException, InterruptedException {
+ logger.info("FFMPEG starting...");
+ ProcessBuilder pb = buildCommand();
+ pb.redirectErrorStream(true);
+ Process process = pb.start();
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+
+ String line;
+ while ((line = reader.readLine()) != null) {
+
+ if (line.startsWith("out_time_ms=")) {
+ out_time_ms = parseLong(line.substring("out_time_ms=".length()));
+ }
+ }
+
+ logger.info("FFMPEG finished");
+ }
+
+}
diff --git a/src/main/java/com/ddf/vodsystem/services/JobService.java b/src/main/java/com/ddf/vodsystem/services/JobService.java
index 3ae9235..443e308 100644
--- a/src/main/java/com/ddf/vodsystem/services/JobService.java
+++ b/src/main/java/com/ddf/vodsystem/services/JobService.java
@@ -43,7 +43,9 @@ public class JobService {
Thread thread = new Thread(() -> {
while (true) {
if (!jobQueue.isEmpty()) {
- Runnable task = jobQueue.poll();
+ Job task = jobQueue.poll();
+
+ logger.info("Starting job {}", task.getUuid());
task.run(); // Execute the task
}
diff --git a/src/main/java/com/ddf/vodsystem/services/UploadService.java b/src/main/java/com/ddf/vodsystem/services/UploadService.java
index 390647b..8110699 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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@@ -14,11 +15,16 @@ import java.nio.file.StandardCopyOption;
import java.util.Base64;
import java.util.UUID;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
@Service
public class UploadService {
+ private static final Logger logger = LoggerFactory.getLogger(UploadService.class);
private static final String UPLOAD_DIR = "videos/";
private final JobService jobService;
+ @Autowired
public UploadService(JobService jobService) {
this.jobService = jobService;
}
@@ -41,7 +47,7 @@ public class UploadService {
Path filePath = Paths.get(outputFile.getAbsolutePath());
Files.copy(inputFile.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
- e.printStackTrace();
+ logger.error(e.getMessage());
}
}