5450 save video thumbnails with time position

This commit is contained in:
William Schaefer 2019-11-27 18:05:09 -05:00
parent e60d96f341
commit 5ce2436c92

View File

@ -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,6 +381,14 @@ 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();
String cacheDirectory;
try {
cacheDirectory = Case.getCurrentCaseThrows().getCacheDirectory();
} catch (NoCurrentCaseException ex) {
cacheDirectory = null;
logger.log(Level.WARNING, "Unable to get cache directory, video thumbnails will not be saved", ex);
}
if (cacheDirectory == null || !Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile().exists()) {
//Currently this method always creates the thumbnails //Currently this method always creates the thumbnails
java.io.File tempFile; java.io.File tempFile;
try { try {
@ -450,12 +470,26 @@ class FileSearch {
Mat imageMatrix = new Mat(); Mat imageMatrix = new Mat();
List<Image> videoThumbnails = new ArrayList<>(); List<Image> videoThumbnails = new ArrayList<>();
try {
FileUtils.forceMkdir(Paths.get(cacheDirectory, VIDEO_THUMBNAIL_DIR, file.getMd5Hash()).toFile());
} catch (IOException ex) {
cacheDirectory = null;
logger.log(Level.WARNING, "Unable to make video thumbnails directory, thumbnails will not be saved", ex);
}
for (int i = 0; i < framePositions.length; i++) { for (int i = 0; i < framePositions.length; i++) {
if (!videoFile.set(0, framePositions[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 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. // If we can't set the time, continue to the next frame position and try again.
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); 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; continue;
} }
// Read the frame into the image/matrix. // Read the frame into the image/matrix.
@ -463,11 +497,28 @@ class FileSearch {
logger.log(Level.WARNING, "Error reading frame at " + framePositions[i] + "ms from {0}", file.getParentPath() + "/" + file.getName()); //NON-NLS 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. // If the image is bad for some reason, continue to the next frame position and try again.
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); 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; continue;
} }
// If the image is empty, return since no buffered image can be created. // If the image is empty, return since no buffered image can be created.
if (imageMatrix.empty()) { if (imageMatrix.empty()) {
videoThumbnails.add(ImageUtils.getDefaultThumbnail()); 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; continue;
} }
@ -493,14 +544,49 @@ class FileSearch {
bufferedImage.getRaster().setDataElements(0, 0, matrixColumns, matrixRows, data); bufferedImage.getRaster().setDataElements(0, 0, matrixColumns, matrixRows, data);
if (Thread.interrupted()) { if (Thread.interrupted()) {
thumbnailWrapper.setThumbnails(videoThumbnails, framePositions); 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; return;
} }
videoThumbnails.add(ScalrWrapper.resizeFast(bufferedImage, ImageUtils.ICON_SIZE_LARGE)); 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); thumbnailWrapper.setThumbnails(videoThumbnails, framePositions);
} finally { } finally {
videoFile.release(); // close the file} videoFile.release(); // close the file}
} }
} else {
loadSavedThumbnails(cacheDirectory, thumbnailWrapper);
}
}
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);
} }
/** /**
@ -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());
} }
} }