In dieser Java-Anwendung versuche ich, ein Video in kleine Clips zu konvertieren.
Hier ist die Implementierungsklasse für das gleiche
package ffmpeg.clip.process; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Paths; import java.time.Duration; import java.time.LocalTime; import java.time.temporal.ChronoUnit; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import ffmpeg.clip.utils.VideoConstant; import ffmpeg.clip.utils.VideoUtils; /* * @author Nitishkumar Singh * @Description: class will use ffmpeg to break an source video into clips */ public class VideoToClip { /* * Prevent from creating instance */ private VideoToClip() { } /** * Get Video Duration is milliseconds * * @Exception IOException - File does not exist VideoException- Video File have data issues */ static LocalTime getDuration(String sourceVideoFile) throws Exception { if (!Paths.get(sourceVideoFile).toFile().exists()) throw new Exception("File does not exist!!"); Process proc = new ProcessBuilder(VideoConstant.SHELL, VideoConstant.SHELL_COMMAND_STRING_ARGUMENT, String.format(VideoConstant.DURATION_COMMAND, sourceVideoFile)).start(); boolean errorOccured = (new BufferedReader(new InputStreamReader(proc.getErrorStream())).lines() .count() > VideoConstant.ZERO); String durationInSeconds = new BufferedReader(new InputStreamReader(proc.getInputStream())).lines() .collect(Collectors.joining(System.lineSeparator())); proc.destroy(); if (errorOccured || (durationInSeconds.length() == VideoConstant.ZERO)) throw new Exception("Video File have some issues!"); else return VideoUtils.parseHourMinuteSecondMillisecondFormat(durationInSeconds); } /** * Create Clips for Video Using Start and End Second * * @Exception IOException - Clip Creation Process Failed InterruptedException - Clip Creation task get"s failed */ static String toClipProcess(String sourceVideo, String outputDirectory, LocalTime start, LocalTime end, String fileExtension) throws IOException, InterruptedException, ExecutionException { String clipName = String.format(VideoConstant.CLIP_FILE_NAME, VideoUtils.getHourMinuteSecondMillisecondFormat(start), VideoUtils.getHourMinuteSecondMillisecondFormat(end), fileExtension); String command = String.format(VideoConstant.FFMPEG_OUTPUT_COMMAND, sourceVideo, VideoUtils.getHourMinuteSecondMillisecondFormat(start), VideoUtils.getHourMinuteSecondMillisecondFormat(end.minus(start.toNanoOfDay(), ChronoUnit.NANOS)), outputDirectory, clipName); LocalTime startTime = LocalTime.now(); System.out.println("Clip Name: " + clipName); System.out.println("FFMPEG Process Execution Started"); CompletableFuture<Process> completableFuture = CompletableFuture.supplyAsync(() -> { try { return executeProcess(command); } catch (InterruptedException | IOException ex) { throw new RuntimeException(ex); } }); completableFuture.get(); // remove LocalTime endTime = LocalTime.now(); System.out.println("Clip Name: " + clipName); System.out.println("FFMPEG Process Execution Finished"); System.out.println("Duration: " + Duration.between(startTime, endTime).toMillis() / 1000); return clipName; } /** * Create and Execute Process for each command */ static Process executeProcess(String command) throws InterruptedException, IOException { Process clipProcess = Runtime.getRuntime().exec(command); clipProcess.waitFor(); return clipProcess; } }
Die gesamte Lösung ist unter Github verfügbar. Ich verwende tatsächlich CompletableFuture
und führe den Befehl FFMPEG aus, indem ich Java Process erstelle. Die Zeit ist zu viel. Für ein 40-minütiges Video dauert es auf einem 64-CPU-Computer mehr als 49 Minuten. Ich versuche, die Kerngröße auf 8 oder so zu reduzieren und die Leistung zu verbessern, da diese Art von Leistung für keine Anwendung akzeptabel ist.
22.01.2017 update
Bei einem Update habe ich den Befehl FFMPEG geändert, um Clips zu erstellen, und auf FFMPEG 3 aktualisiert, aber es gibt keine Verbesserung.
ffmpeg -y -i INPUT_FILE_PATH -ss TIME_STAMP -t DURATION_TO_CLIP OUTPUT_FILE_PATH
Kommentare
- Ich habe einige Beispiele mit JavaCV gepostet. Ich habe ‚ nicht mit einem langen Video-Video versucht, sondern mit kurzen Videos (dh 5 Minuten) beträgt die Verarbeitungszeit ungefähr 1 Minute.
Antwort
Dies ist eine natürliche Einschränkung für Videos Codierung. Auf modernen Computern wird 1 Minute 720p-Video ungefähr in 1 Minute codiert.
Sie können viel Zeit sparen, wenn Sie keine Neucodierung benötigen (d.h. Ändern der Codec- oder Videogröße) mithilfe der Option -codec copy
ffmpeg.
Sie sagten auch, Sie haben 64 Kerne, aber Ihr Code verwendet nur 1 Thread zum Codieren. Verwenden Sie -threads 0
, damit ffmpeg selbst auswählen kann.
Wenn Sie dies in Java ausführen müssen, geben Sie Jaffree eine Chance (ich bin ein Autor).
Kommentare
- Wenn Sie Jaffree geschrieben haben, geben Sie dies bitte ausdrücklich an
- github.com/bramp/ffmpeg-cli-wrapper ist möglicherweise eine bessere Lösung für Sie.
- Ich habe Jaffree genau wegen mehrerer Probleme mit ffmpeg-cli-wrapper gestartet.
Antwort
Ich weiß, dass dies der Fall ist Eine alte Frage, aber ich denke, dies könnte für Java-Entwickler nützlich sein.
Es gibt eine nette Bibliothek namens JavaCV. Dieses Live ist ein Wrapper für mehrere C- und C ++ – Bibliotheken wie FFmpeg.
Dies ist ein einfaches Beispiel für die Implementierung eines Konverters:
import org.bytedeco.javacpp.avcodec; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public class PacketRecorderTest { private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd__hhmmSSS"); private static final int RECORD_LENGTH = 5000; private static final boolean AUDIO_ENABLED = false; public static void main(String[] args) throws FrameRecorder.Exception, FrameGrabber.Exception { String inputFile = "/home/usr/videos/VIDEO_FILE_NAME.mp4"; // Decodes-encodes String outputFile = "/tmp/" + DATE_FORMAT.format(new Date()) + "_frameRecord.mp4"; PacketRecorderTest.frameRecord(inputFile, outputFile); // copies codec (no need to re-encode) outputFile = "/tmp/" + DATE_FORMAT.format(new Date()) + "_packetRecord.mp4"; PacketRecorderTest.packetRecord(inputFile, outputFile); } public static void frameRecord(String inputFile, String outputFile) throws FrameGrabber.Exception, FrameRecorder.Exception { int audioChannel = AUDIO_ENABLED ? 1 : 0; FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile); FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel); grabber.start(); recorder.start(); Frame frame; long t1 = System.currentTimeMillis(); while ((frame = grabber.grabFrame(AUDIO_ENABLED, true, true, false)) != null) { recorder.record(frame); if ((System.currentTimeMillis() - t1) > RECORD_LENGTH) { break; } } recorder.stop(); grabber.stop(); } public static void packetRecord(String inputFile, String outputFile) throws FrameGrabber.Exception, FrameRecorder.Exception { int audioChannel = AUDIO_ENABLED ? 1 : 0; FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile); FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 1280, 720, audioChannel); grabber.start(); recorder.start(grabber.getFormatContext()); avcodec.AVPacket packet; long t1 = System.currentTimeMillis(); while ((packet = grabber.grabPacket()) != null) { recorder.recordPacket(packet); if ((System.currentTimeMillis() - t1) > RECORD_LENGTH) { break; } } recorder.stop(); grabber.stop(); } }
Diese grundlegende Implementierung zeigt, wie ein Video mit einer FFmpeg-Paket oder ein JavaCV-Frame .
Kommentare
- Willkommen bei Code Review. Sie haben eine alternative Lösung vorgestellt, aber ‚ hat den Code nicht überprüft. Bitte erläutern Sie Ihre Überlegungen (wie Ihre Lösung funktioniert und warum sie besser als das Original ist), damit der Autor und andere Leser aus Ihrem Denkprozess lernen können.