What is ffmpeg java wrapper?
In this java application, I am trying to convert a video into small clips.
Here is the implementation class for the same
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 error Occurred = (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 (error Occurred || (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");
CompletableFuturecompletableFuture = 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;
}
}
The Entire Solution is available at Github. I am actually using CompletableFuture and running FFMPEG commands by creating Java Processes. The time it takes is too much. For a 40 minutes video, it takes more than 49 minutes, on a 64 CPU machine. I am trying to reduce the core size to 8 or something, as well improve its performance, as this kind of performance won't be acceptable for any kind of application.
22-jan-2017 update
One Update, I have changed the FFMPEG command to create clips and updated to FFMPEG 3, but there is no improvement.
ffmpeg -y -i INPUT_FILE_PATH -ss TIME_STAMP -t DURATION_TO_CLIP OUTPUT_FILE_PATH
There is a nice library called JavaCV that is a wrapper for multiple C and C++ libraries like FFmpeg Java.
This is a simple example of how to implement a Converter:
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();
}
}
This basic implementation shows how to convert a video using a FFmpeg packet or a JavaCV frame.