mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
5450 save video thumbnails with time position
This commit is contained in:
parent
e60d96f341
commit
5ce2436c92
@ -23,7 +23,11 @@ import com.google.common.cache.CacheBuilder;
|
|||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.RenderedImage;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -37,10 +41,16 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.apache.tools.ant.util.StringUtils;
|
||||||
import org.netbeans.api.progress.ProgressHandle;
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
import org.opencv.core.Mat;
|
import org.opencv.core.Mat;
|
||||||
import org.opencv.highgui.VideoCapture;
|
import org.opencv.highgui.VideoCapture;
|
||||||
|
import org.openide.util.Exceptions;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
|
||||||
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
|
||||||
@ -72,6 +82,8 @@ class FileSearch {
|
|||||||
|
|
||||||
private final static Logger logger = Logger.getLogger(FileSearch.class.getName());
|
private final static Logger logger = Logger.getLogger(FileSearch.class.getName());
|
||||||
private static final int MAXIMUM_CACHE_SIZE = 10;
|
private static final int MAXIMUM_CACHE_SIZE = 10;
|
||||||
|
private static final String THUMBNAIL_FORMAT = "png"; //NON-NLS
|
||||||
|
private static final String VIDEO_THUMBNAIL_DIR = "video-thumbnails"; //NON-NLS
|
||||||
private static final Cache<SearchKey, Map<GroupKey, List<ResultFile>>> searchCache = CacheBuilder.newBuilder()
|
private static final Cache<SearchKey, Map<GroupKey, List<ResultFile>>> searchCache = CacheBuilder.newBuilder()
|
||||||
.maximumSize(MAXIMUM_CACHE_SIZE)
|
.maximumSize(MAXIMUM_CACHE_SIZE)
|
||||||
.build();
|
.build();
|
||||||
@ -369,25 +381,75 @@ class FileSearch {
|
|||||||
"FileSearch.genVideoThumb.progress.text=extracting temporary file {0}"})
|
"FileSearch.genVideoThumb.progress.text=extracting temporary file {0}"})
|
||||||
static void getVideoThumbnails(VideoThumbnailsWrapper thumbnailWrapper) {
|
static void getVideoThumbnails(VideoThumbnailsWrapper thumbnailWrapper) {
|
||||||
AbstractFile file = thumbnailWrapper.getResultFile().getFirstInstance();
|
AbstractFile file = thumbnailWrapper.getResultFile().getFirstInstance();
|
||||||
//Currently this method always creates the thumbnails
|
String cacheDirectory;
|
||||||
java.io.File tempFile;
|
|
||||||
try {
|
try {
|
||||||
tempFile = getVideoFileInTempDir(file);
|
cacheDirectory = Case.getCurrentCaseThrows().getCacheDirectory();
|
||||||
} catch (NoCurrentCaseException ex) {
|
} catch (NoCurrentCaseException ex) {
|
||||||
logger.log(Level.WARNING, "Exception while getting open case.", ex); //NON-NLS
|
cacheDirectory = null;
|
||||||
int[] framePositions = new int[]{
|
logger.log(Level.WARNING, "Unable to get cache directory, video thumbnails will not be saved", ex);
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0};
|
|
||||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
|
if (cacheDirectory == null || !Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile().exists()) {
|
||||||
ProgressHandle progress = ProgressHandle.createHandle(Bundle.FileSearch_genVideoThumb_progress_text(file.getName()));
|
//Currently this method always creates the thumbnails
|
||||||
progress.start(100);
|
java.io.File tempFile;
|
||||||
try {
|
try {
|
||||||
Files.createParentDirs(tempFile);
|
tempFile = getVideoFileInTempDir(file);
|
||||||
|
} catch (NoCurrentCaseException ex) {
|
||||||
|
logger.log(Level.WARNING, "Exception while getting open case.", ex); //NON-NLS
|
||||||
|
int[] framePositions = new int[]{
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0};
|
||||||
|
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
|
||||||
|
ProgressHandle progress = ProgressHandle.createHandle(Bundle.FileSearch_genVideoThumb_progress_text(file.getName()));
|
||||||
|
progress.start(100);
|
||||||
|
try {
|
||||||
|
Files.createParentDirs(tempFile);
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
int[] framePositions = new int[]{
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0};
|
||||||
|
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ContentUtils.writeToFile(file, tempFile, progress, null, true);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Error extracting temporary file for " + file.getParentPath() + "/" + file.getName(), ex); //NON-NLS
|
||||||
|
} finally {
|
||||||
|
progress.finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VideoCapture videoFile = new VideoCapture(); // will contain the video
|
||||||
|
BufferedImage bufferedImage = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!videoFile.open(tempFile.toString())) {
|
||||||
|
logger.log(Level.WARNING, "Error opening {0} for preview generation.", file.getParentPath() + "/" + file.getName()); //NON-NLS
|
||||||
|
int[] framePositions = new int[]{
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0};
|
||||||
|
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double fps = videoFile.get(5); // gets frame per second
|
||||||
|
double totalFrames = videoFile.get(7); // gets total frames
|
||||||
|
if (fps <= 0 || totalFrames <= 0) {
|
||||||
|
logger.log(Level.WARNING, "Error getting fps or total frames for {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
|
||||||
|
int[] framePositions = new int[]{
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0};
|
||||||
|
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
int[] framePositions = new int[]{
|
int[] framePositions = new int[]{
|
||||||
0,
|
0,
|
||||||
@ -397,112 +459,136 @@ class FileSearch {
|
|||||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ContentUtils.writeToFile(file, tempFile, progress, null, true);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, "Error extracting temporary file for " + file.getParentPath() + "/" + file.getName(), ex); //NON-NLS
|
|
||||||
} finally {
|
|
||||||
progress.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VideoCapture videoFile = new VideoCapture(); // will contain the video
|
|
||||||
BufferedImage bufferedImage = null;
|
|
||||||
|
|
||||||
try {
|
double duration = 1000 * (totalFrames / fps); //total milliseconds
|
||||||
if (!videoFile.open(tempFile.toString())) {
|
|
||||||
logger.log(Level.WARNING, "Error opening {0} for preview generation.", file.getParentPath() + "/" + file.getName()); //NON-NLS
|
|
||||||
int[] framePositions = new int[]{
|
int[] framePositions = new int[]{
|
||||||
0,
|
(int) (duration * .01),
|
||||||
0,
|
(int) (duration * .25),
|
||||||
0,
|
(int) (duration * .5),
|
||||||
0};
|
(int) (duration * .75),};
|
||||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
double fps = videoFile.get(5); // gets frame per second
|
|
||||||
double totalFrames = videoFile.get(7); // gets total frames
|
|
||||||
if (fps <= 0 || totalFrames <= 0) {
|
|
||||||
logger.log(Level.WARNING, "Error getting fps or total frames for {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
|
|
||||||
int[] framePositions = new int[]{
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0};
|
|
||||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
int[] framePositions = new int[]{
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0};
|
|
||||||
thumbnailWrapper.setThumbnails(createDefaultThumbnailList(), framePositions);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double duration = 1000 * (totalFrames / fps); //total milliseconds
|
Mat imageMatrix = new Mat();
|
||||||
|
List<Image> videoThumbnails = new ArrayList<>();
|
||||||
int[] framePositions = new int[]{
|
try {
|
||||||
(int) (duration * .01),
|
FileUtils.forceMkdir(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile());
|
||||||
(int) (duration * .25),
|
} catch (IOException ex) {
|
||||||
(int) (duration * .5),
|
cacheDirectory = null;
|
||||||
(int) (duration * .75),};
|
logger.log(Level.WARNING, "Unable to make video thumbnails directory, thumbnails will not be saved", ex);
|
||||||
|
|
||||||
Mat imageMatrix = new Mat();
|
|
||||||
List<Image> videoThumbnails = new ArrayList<>();
|
|
||||||
for (int i = 0; i < framePositions.length; i++) {
|
|
||||||
if (!videoFile.set(0, framePositions[i])) {
|
|
||||||
logger.log(Level.WARNING, "Error seeking to " + framePositions[i] + "ms in {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
|
|
||||||
// If we can't set the time, continue to the next frame position and try again.
|
|
||||||
|
|
||||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Read the frame into the image/matrix.
|
|
||||||
if (!videoFile.read(imageMatrix)) {
|
|
||||||
logger.log(Level.WARNING, "Error reading frame at " + framePositions[i] + "ms from {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
|
|
||||||
// If the image is bad for some reason, continue to the next frame position and try again.
|
|
||||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// If the image is empty, return since no buffered image can be created.
|
|
||||||
if (imageMatrix.empty()) {
|
|
||||||
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < framePositions.length; i++) {
|
||||||
|
if (!videoFile.set(0, framePositions[i])) {
|
||||||
|
logger.log(Level.WARNING, "Error seeking to " + framePositions[i] + "ms in {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
|
||||||
|
// If we can't set the time, continue to the next frame position and try again.
|
||||||
|
|
||||||
int matrixColumns = imageMatrix.cols();
|
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||||
int matrixRows = imageMatrix.rows();
|
if (cacheDirectory != null) {
|
||||||
|
try {
|
||||||
|
ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
|
||||||
|
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Read the frame into the image/matrix.
|
||||||
|
if (!videoFile.read(imageMatrix)) {
|
||||||
|
logger.log(Level.WARNING, "Error reading frame at " + framePositions[i] + "ms from {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS
|
||||||
|
// If the image is bad for some reason, continue to the next frame position and try again.
|
||||||
|
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||||
|
if (cacheDirectory != null) {
|
||||||
|
try {
|
||||||
|
ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
|
||||||
|
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Convert the matrix that contains the frame to a buffered image.
|
continue;
|
||||||
if (bufferedImage == null) {
|
}
|
||||||
bufferedImage = new BufferedImage(matrixColumns, matrixRows, BufferedImage.TYPE_3BYTE_BGR);
|
// If the image is empty, return since no buffered image can be created.
|
||||||
}
|
if (imageMatrix.empty()) {
|
||||||
|
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||||
|
if (cacheDirectory != null) {
|
||||||
|
try {
|
||||||
|
ImageIO.write((RenderedImage) ImageUtils.getDefaultThumbnail(), THUMBNAIL_FORMAT,
|
||||||
|
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Unable to save default video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
byte[] data = new byte[matrixRows * matrixColumns * (int) (imageMatrix.elemSize())];
|
int matrixColumns = imageMatrix.cols();
|
||||||
imageMatrix.get(0, 0, data); //copy the image to data
|
int matrixRows = imageMatrix.rows();
|
||||||
|
|
||||||
if (imageMatrix.channels() == 3) {
|
// Convert the matrix that contains the frame to a buffered image.
|
||||||
for (int k = 0; k < data.length; k += 3) {
|
if (bufferedImage == null) {
|
||||||
byte temp = data[k];
|
bufferedImage = new BufferedImage(matrixColumns, matrixRows, BufferedImage.TYPE_3BYTE_BGR);
|
||||||
data[k] = data[k + 2];
|
}
|
||||||
data[k + 2] = temp;
|
|
||||||
|
byte[] data = new byte[matrixRows * matrixColumns * (int) (imageMatrix.elemSize())];
|
||||||
|
imageMatrix.get(0, 0, data); //copy the image to data
|
||||||
|
|
||||||
|
if (imageMatrix.channels() == 3) {
|
||||||
|
for (int k = 0; k < data.length; k += 3) {
|
||||||
|
byte temp = data[k];
|
||||||
|
data[k] = data[k + 2];
|
||||||
|
data[k + 2] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferedImage.getRaster().setDataElements(0, 0, matrixColumns, matrixRows, data);
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
|
||||||
|
try {
|
||||||
|
FileUtils.forceDelete(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile());
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Unable to delete directory for cancelled video thumbnail process", ex);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BufferedImage thumbnail = ScalrWrapper.resizeFast(bufferedImage, ImageUtils.ICON_SIZE_LARGE);
|
||||||
|
videoThumbnails.add(thumbnail);
|
||||||
|
try {
|
||||||
|
ImageIO.write(thumbnail, THUMBNAIL_FORMAT,
|
||||||
|
Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash(), i + "-" + framePositions[i] + "." + THUMBNAIL_FORMAT).toFile()); //NON-NLS)
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Unable to save video thumbnail for " + file.getMd5Hash() + " at frame position " + framePositions[i], ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
|
||||||
bufferedImage.getRaster().setDataElements(0, 0, matrixColumns, matrixRows, data);
|
} finally {
|
||||||
if (Thread.interrupted()) {
|
videoFile.release(); // close the file}
|
||||||
thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
videoThumbnails.add(ScalrWrapper.resizeFast(bufferedImage, ImageUtils.ICON_SIZE_LARGE));
|
|
||||||
}
|
}
|
||||||
thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
|
} else {
|
||||||
} finally {
|
loadSavedThumbnails(cacheDirectory, thumbnailWrapper);
|
||||||
videoFile.release(); // close the file}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void loadSavedThumbnails(String cacheDirectory, VideoThumbnailsWrapper thumbnailWrapper) {
|
||||||
|
int[] framePositions = new int[4];
|
||||||
|
List<Image> videoThumbnails = new ArrayList<>();
|
||||||
|
int thumbnailNumber = 0;
|
||||||
|
String md5 = thumbnailWrapper.getResultFile().getFirstInstance().getMd5Hash();
|
||||||
|
for (String fileName : Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5).toFile().list()) {
|
||||||
|
try {
|
||||||
|
videoThumbnails.add(ImageIO.read(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, md5, fileName).toFile()));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
videoThumbnails.add(ImageUtils.getDefaultThumbnail());
|
||||||
|
logger.log(Level.WARNING, "Unable to read saved video thumbnail " + fileName + " for " + md5, ex);
|
||||||
|
}
|
||||||
|
int framePos = Integer.valueOf(FilenameUtils.getBaseName(fileName).substring(2));
|
||||||
|
framePositions[thumbnailNumber] = framePos;
|
||||||
|
thumbnailNumber++;
|
||||||
|
|
||||||
|
}
|
||||||
|
thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private helper method for creating video thumbnails, for use when no
|
* Private helper method for creating video thumbnails, for use when no
|
||||||
* thumbnails are created.
|
* thumbnails are created.
|
||||||
@ -623,8 +709,7 @@ class FileSearch {
|
|||||||
FileSizeGroupKey(ResultFile file) {
|
FileSizeGroupKey(ResultFile file) {
|
||||||
if (file.getFileType() == FileType.VIDEO) {
|
if (file.getFileType() == FileType.VIDEO) {
|
||||||
fileSize = FileSize.fromVideoSize(file.getFirstInstance().getSize());
|
fileSize = FileSize.fromVideoSize(file.getFirstInstance().getSize());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
fileSize = FileSize.fromImageSize(file.getFirstInstance().getSize());
|
fileSize = FileSize.fromImageSize(file.getFirstInstance().getSize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user