diff --git a/Core/build.xml b/Core/build.xml index deb035b6099ad3f12f05e50725a6f8b4ee05abe4..461de2ed41d43e983588f1cdc1ce65300841131e 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -116,6 +116,10 @@ <fileset dir="${thirdparty.dir}/yara/bin"/> </copy> <copy file="${thirdparty.dir}/yara/bin/YaraJNIWrapper.jar" todir="${ext.dir}" /> + <!--Copy ffmpeg to release--> + <copy todir="${basedir}/release/ffmpeg" > + <fileset dir="${thirdparty.dir}/ffmpeg"/> + </copy> </target> diff --git a/Core/src/org/sleuthkit/autopsy/report/video/VideoSnapShotWorker.java b/Core/src/org/sleuthkit/autopsy/report/video/VideoSnapShotWorker.java new file mode 100755 index 0000000000000000000000000000000000000000..462258d91d6c3fe348a925313e16b56a02c9e0ea --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/video/VideoSnapShotWorker.java @@ -0,0 +1,168 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2022 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.report.video; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import javax.swing.SwingWorker; +import org.opencv.core.Mat; +import org.opencv.core.Size; +import org.opencv.imgproc.Imgproc; +import org.opencv.videoio.VideoCapture; +import org.opencv.videoio.VideoWriter; +import org.opencv.videoio.Videoio; + +/** + * A SwingWorker that will create a video of screen captures. + * + * To have the the gui update with process add a PropertyChangeListener for + * the property 'progress'. + * + * To update the gui on completion subclass this class and implement + * the SwingWorker done method. + * + */ +public class VideoSnapShotWorker extends SwingWorker<Void, Void>{ + private static final long MIN_FRAME_INTERVAL_MILLIS = 500; + + private static final int DEFAULT_FRAMES_PER_SECOND = 1; + private static final int DEFAULT_TOTAL_FRAMES = 100; + private static final int DEFAULT_SCALE = 100; + + private final Path inputPath; + private final Path outputPath; + private final long numFrames; + private final double scale; + private final int framesPerSecond; + + /** + * Creates a new instance of the SwingWorker using the default parameters. + * + * @param inputPath The path to the existing video. + * @param outputPath The output path of the snapshot video. + */ + public VideoSnapShotWorker(Path inputPath, Path outputPath) { + this(inputPath, outputPath, DEFAULT_TOTAL_FRAMES, DEFAULT_SCALE, DEFAULT_FRAMES_PER_SECOND); + } + + /** + * Creates a new instance of the SwingWorker. + * + * @param inputPath The path to the existing video. + * @param outputPath The output path of the snapshot video. + * @param numFrames The number of screen captures to include in the video. + * @param scale % to scale from the original. Passing 0 or 100 will result in no change. + * @param framesPerSecond Effects how long each frame will appear in the video. + */ + public VideoSnapShotWorker(Path inputPath, Path outputPath, long numFrames, double scale, int framesPerSecond) { + this.inputPath = inputPath; + this.outputPath = outputPath; + this.scale = scale; + this.numFrames = numFrames; + this.framesPerSecond = framesPerSecond; + } + + @Override + protected Void doInBackground() throws Exception { + File input = inputPath.toFile(); + if (!input.exists() || !input.isFile() || !input.canRead()) { + throw new IOException(String.format("Unable to read input file %s", input.toString())); + } + + File outputFile = outputPath.toFile(); + outputFile.mkdirs(); + + String file_name = inputPath.toString();//OpenCV API requires string for file name + VideoCapture videoCapture = new VideoCapture(file_name); //VV will contain the videos + + if (!videoCapture.isOpened()) //checks if file is not open + { + // add this file to the set of known bad ones + throw new Exception("Problem with video file; problem when attempting to open file."); + } + VideoWriter writer = null; + try { + // get the duration of the video + double fps = framesPerSecond == 0 ? videoCapture.get(Videoio.CAP_PROP_FPS) : framesPerSecond; // gets frame per second + double total_frames = videoCapture.get(7); // gets total frames + double milliseconds = 1000 * (total_frames / fps); //convert to ms duration + long myDurationMillis = (long) milliseconds; + + if (myDurationMillis <= 0) { + throw new Exception(String.format("Failed to make snapshot video, original video has no duration. %s", inputPath.toAbsolutePath())); + } + + // calculate the number of frames to capture + int numFramesToGet = (int) numFrames; + long frameInterval = myDurationMillis / numFrames; + + if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) { + numFramesToGet = 1; + } + + // for each timeStamp, grab a frame + Mat mat = new Mat(); // holds image received from video in mat format (opencv format) + + Size s = new Size((int) videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH), (int) videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT)); + Size newSize = (scale == 0 || scale == 100) ? s : new Size((int) (videoCapture.get(Videoio.CAP_PROP_FRAME_WIDTH) * scale), (int) (videoCapture.get(Videoio.CAP_PROP_FRAME_HEIGHT) * scale)); + + writer = new VideoWriter(outputFile.toString(), VideoWriter.fourcc('M', 'J', 'P', 'G'), 1, newSize, true); + + if (!writer.isOpened()) { + throw new Exception(String.format("Problem with video file; problem when attempting to open output file. %s", outputFile.toString())); + } + + for (int frame = 0; frame < numFramesToGet; ++frame) { + + if(isCancelled()) { + break; + } + + long timeStamp = frame * frameInterval; + videoCapture.set(0, timeStamp); //set video in timeStamp ms + + if (!videoCapture.read(mat)) { // on Wav files, usually the last frame we try to read does not succeed. + continue; + } + + Mat resized = new Mat(); + Imgproc.resize(mat, resized, newSize, 0, 0, Imgproc.INTER_CUBIC); + writer.write(resized); + + setProgress(numFramesToGet/frame); + } + + } finally { + videoCapture.release(); + if (writer != null) { + writer.release(); + } + } + + if(isCancelled()) { + if(outputFile.exists()) { + outputFile.delete(); + } + } + + return null; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/report/video/VideoUtilis.java b/Core/src/org/sleuthkit/autopsy/report/video/VideoUtilis.java new file mode 100755 index 0000000000000000000000000000000000000000..0a185ffe09ab51e12126a8831b5db4f7437bb786 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/video/VideoUtilis.java @@ -0,0 +1,93 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2022 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.report.video; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.openide.modules.InstalledFileLocator; +import org.sleuthkit.autopsy.coreutils.ExecUtil; +import org.sleuthkit.autopsy.coreutils.ExecUtil.ProcessTerminator; + +public class VideoUtilis { + + private static final String FFMPEG = "ffmpeg"; + private static final String FFMPEG_EXE = "ffmpeg.exe"; + + private VideoUtilis() {} + + static void compressVideo(Path inputPath, Path outputPath, ProcessTerminator terminator) throws Exception { + Path executablePath = Paths.get(FFMPEG, FFMPEG_EXE); + File exeFile = InstalledFileLocator.getDefault().locate(executablePath.toString(), VideoUtilis.class.getPackage().getName(), true); + if(exeFile == null) { + throw new IOException("Unable to compress ffmpeg.exe was not found."); + } + + if(!exeFile.canExecute()) { + throw new IOException("Unable to compress ffmpeg.exe could not be execute"); + } + + + ProcessBuilder processBuilder = buildProcessWithRunAsInvoker( + "\"" + exeFile.getAbsolutePath() + "\"", + "-i", "\"" + inputPath.toAbsolutePath().toString() + "\"", + "-vf", "scale=1280:-1", + "-c:v", "libx264", + "-preset", "veryslow", + "-crf", "24", + "\"" + outputPath.toAbsolutePath().toString() + "\""); + + ExecUtil.execute(processBuilder, terminator); + } + + static void scaleVideo(Path inputPath, Path outputPath, int width, int height, ProcessTerminator terminator) throws Exception{ + Path executablePath = Paths.get(FFMPEG, FFMPEG_EXE); + File exeFile = InstalledFileLocator.getDefault().locate(executablePath.toString(), VideoUtilis.class.getPackage().getName(), true); + if(exeFile == null) { + throw new IOException("Unable to compress ffmpeg.exe was not found."); + } + + if(!exeFile.canExecute()) { + throw new IOException("Unable to compress ffmpeg.exe could not be execute"); + } + + String scaleParam = Integer.toString(width) + ":" + Integer.toString(height); + + ProcessBuilder processBuilder = buildProcessWithRunAsInvoker( + "\"" + exeFile.getAbsolutePath() + "\"", + "-i", "\"" + inputPath.toAbsolutePath().toString() + "\"", + "-s", scaleParam, + "-c:a", "copy", + "\"" + outputPath.toAbsolutePath().toString() + "\""); + + ExecUtil.execute(processBuilder, terminator); + + } + + static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) { + ProcessBuilder processBuilder = new ProcessBuilder(commandLine); + /* + * Add an environment variable to force aLeapp to run with the same + * permissions Autopsy uses. + */ + processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS + return processBuilder; + } +} diff --git a/thirdparty/ffmpeg/avcodec-59.dll b/thirdparty/ffmpeg/avcodec-59.dll new file mode 100755 index 0000000000000000000000000000000000000000..4241f36f291479f6f8917fca031f08e9e16c01ef Binary files /dev/null and b/thirdparty/ffmpeg/avcodec-59.dll differ diff --git a/thirdparty/ffmpeg/avdevice-59.dll b/thirdparty/ffmpeg/avdevice-59.dll new file mode 100755 index 0000000000000000000000000000000000000000..07f8b8226e5256c5bcaf0e6e3d4f00c498fbc911 Binary files /dev/null and b/thirdparty/ffmpeg/avdevice-59.dll differ diff --git a/thirdparty/ffmpeg/avfilter-8.dll b/thirdparty/ffmpeg/avfilter-8.dll new file mode 100755 index 0000000000000000000000000000000000000000..7aa9015c2067df40295bdfa1d813546acb9e10ae Binary files /dev/null and b/thirdparty/ffmpeg/avfilter-8.dll differ diff --git a/thirdparty/ffmpeg/avformat-59.dll b/thirdparty/ffmpeg/avformat-59.dll new file mode 100755 index 0000000000000000000000000000000000000000..c579d74098c0e63c6cc6b48298ac1ddd946b00b2 Binary files /dev/null and b/thirdparty/ffmpeg/avformat-59.dll differ diff --git a/thirdparty/ffmpeg/avutil-57.dll b/thirdparty/ffmpeg/avutil-57.dll new file mode 100755 index 0000000000000000000000000000000000000000..95fd4168d8a8ec51faa7c0ef3296e276e838f658 Binary files /dev/null and b/thirdparty/ffmpeg/avutil-57.dll differ diff --git a/thirdparty/ffmpeg/ffmpeg.exe b/thirdparty/ffmpeg/ffmpeg.exe new file mode 100755 index 0000000000000000000000000000000000000000..7112dfddde9d696d1343c7f8f1eeb17e04c2b85f Binary files /dev/null and b/thirdparty/ffmpeg/ffmpeg.exe differ diff --git a/thirdparty/ffmpeg/postproc-56.dll b/thirdparty/ffmpeg/postproc-56.dll new file mode 100755 index 0000000000000000000000000000000000000000..323746c09928bf6248c165c307f7de81e1e44969 Binary files /dev/null and b/thirdparty/ffmpeg/postproc-56.dll differ diff --git a/thirdparty/ffmpeg/swresample-4.dll b/thirdparty/ffmpeg/swresample-4.dll new file mode 100755 index 0000000000000000000000000000000000000000..d78700ed59794cdf641c61106d7e6dc61308ea0a Binary files /dev/null and b/thirdparty/ffmpeg/swresample-4.dll differ diff --git a/thirdparty/ffmpeg/swscale-6.dll b/thirdparty/ffmpeg/swscale-6.dll new file mode 100755 index 0000000000000000000000000000000000000000..71b4fd0522441fadd193cd686ea15b18f00c0470 Binary files /dev/null and b/thirdparty/ffmpeg/swscale-6.dll differ