This commit is contained in:
jonathan millman 2013-09-05 09:57:42 -04:00
commit d88dfeb2e5
14 changed files with 2488 additions and 1909 deletions

View File

@ -28,7 +28,6 @@ import org.openide.modules.ModuleInstall;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
* Wrapper over Installers in packages in Core module This is the main
@ -40,56 +39,6 @@ public class Installer extends ModuleInstall {
private static final Logger logger = Logger.getLogger(Installer.class.getName());
private volatile boolean javaFxInit = true;
static {
loadDynLibraries();
}
private static void loadDynLibraries() {
if (PlatformUtil.isWindowsOS()) {
try {
//on windows force loading ms crt dependencies first
//in case linker can't find them on some systems
//Note: if shipping with a different CRT version, this will only print a warning
//and try to use linker mechanism to find the correct versions of libs.
//We should update this if we officially switch to a new version of CRT/compiler
System.loadLibrary("msvcr100");
System.loadLibrary("msvcp100");
logger.log(Level.INFO, "MS CRT libraries loaded");
} catch (UnsatisfiedLinkError e) {
logger.log(Level.SEVERE, "Error loading ms crt libraries, ", e);
}
}
try {
System.loadLibrary("zlib");
logger.log(Level.INFO, "ZLIB library loaded loaded");
} catch (UnsatisfiedLinkError e) {
logger.log(Level.SEVERE, "Error loading ZLIB library, ", e);
}
try {
System.loadLibrary("libewf");
logger.log(Level.INFO, "EWF library loaded");
} catch (UnsatisfiedLinkError e) {
logger.log(Level.SEVERE, "Error loading EWF library, ", e);
}
/* We should rename the Windows dll, to remove the lib prefix.
*/
try {
String tskLibName = null;
if (PlatformUtil.isWindowsOS()) {
tskLibName = "libtsk_jni";
} else {
tskLibName = "tsk_jni";
}
System.loadLibrary(tskLibName);
logger.log(Level.INFO, "TSK_JNI library loaded");
} catch (UnsatisfiedLinkError e) {
logger.log(Level.SEVERE, "Error loading tsk_jni library", e);
}
}
public Installer() {
javaFxInit = true;
packageInstallers = new ArrayList<ModuleInstall>();

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Copyright 2011-2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -34,7 +34,7 @@ import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.JMenuItem;
import javax.swing.JTextPane;
import javax.swing.text.Document;
import javax.swing.SwingWorker;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import org.openide.nodes.Node;
@ -56,7 +56,8 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private static int currentPage = 1;
private List<BlackboardArtifact> artifacts;
private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName());
private final static String WAIT_TEXT = "Preparing display...";
/** Creates new form DataContentViewerArtifact */
public DataContentViewerArtifact() {
initComponents();
@ -204,12 +205,12 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed
currentPage = currentPage+1;
setDataView(artifacts, currentPage);
new DisplayTask(currentPage).execute();
}//GEN-LAST:event_nextPageButtonActionPerformed
private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed
currentPage = currentPage-1;
setDataView(artifacts, currentPage);
new DisplayTask(currentPage).execute();
}//GEN-LAST:event_prevPageButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
@ -239,20 +240,30 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
Content content = lookup.lookup(Content.class);
if (content == null) {
resetComponent();
return;
return;
}
try {
this.setDataView(content.getAllArtifacts(), 1);
} catch (TskException ex) {
logger.log(Level.WARNING, "Couldn't get artifacts: ", ex);
artifacts = content.getAllArtifacts();
}
catch (TskException ex) {
logger.log(Level.WARNING, "Couldn't get artifacts", ex);
resetComponent();
return;
}
// focus on a specific artifact if it is in the node
int index = 0;
BlackboardArtifact artifact = lookup.lookup(BlackboardArtifact.class);
if (artifact != null) {
this.setSelectedArtifact(artifact);
}
index = artifacts.indexOf(artifact);
if (index == -1) {
index = 0;
}
}
// A little bit of cleverness here - add one since setDataView() is also passed page numbers.
new DisplayTask(index + 1).execute();
}
@Override
@ -279,7 +290,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
public void resetComponent() {
// clear / reset the fields
currentPage = 1;
this.artifacts = new ArrayList<BlackboardArtifact>();
this.artifacts = new ArrayList<>();
currentPageLabel.setText("");
totalPageLabel.setText("");
outputViewPane.setText("");
@ -311,12 +322,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
return false;
}
ArtifactStringContent artifact = node.getLookup().lookup(ArtifactStringContent.class);
Content content = node.getLookup().lookup(Content.class);
if(artifact != null) {
return true;
}
Content content = node.getLookup().lookup(Content.class);
if(content != null) {
try {
long size = content.getAllArtifactsCount();
@ -380,15 +386,14 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
* @param artifacts List of artifacts that could be displayed
* @param offset Index into the list for the artifact to display
*/
private void setDataView(List<BlackboardArtifact> artifacts, int offset) {
// change the cursor to "waiting cursor" for this operation
private void setDataView(int offset) {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
outputViewPane.setText(WAIT_TEXT);
if(artifacts.isEmpty()){
resetComponent();
return;
}
this.artifacts = artifacts;
StringContent artifactString = new ArtifactStringContent(artifacts.get(offset-1));
String text = artifactString.getString();
@ -406,16 +411,19 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat
setComponentsVisibility(true);
outputViewPane.moveCaretPosition(0);
this.setCursor(null);
}
}
/**
* Set the displayed artifact to the specified one.
* @param artifact Artifact to display
*/
private void setSelectedArtifact(BlackboardArtifact artifact) {
if(artifacts.contains(artifact)) {
int index = artifacts.indexOf(artifact);
setDataView(artifacts, index+1);
private class DisplayTask extends SwingWorker<Integer, Void> {
final int pageIndex;
DisplayTask(final int pageIndex) {
this.pageIndex = pageIndex;
}
}
@Override
public Integer doInBackground() {
setDataView(pageIndex);
return 0;
}
}
}

View File

@ -16,6 +16,7 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>

View File

@ -43,7 +43,7 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer {
private String[] IMAGES; // use javafx supported
private static final String[] VIDEOS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"};
private static final String[] VIDEOS = new String[]{".swf", ".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"};
private static final String[] AUDIOS = new String[]{".mp3", ".wav", ".wma"};
private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName());
@ -65,7 +65,9 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
initComponents();
videoPanel = new MediaViewVideoPanel();
// get the right panel for our platform
videoPanel = MediaViewVideoPanel.createVideoPanel();
imagePanel = new MediaViewImagePanel();
videoPanelInited = videoPanel.isInited();
imagePanelInited = imagePanel.isInited();
@ -75,14 +77,14 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
}
private void customizeComponents() {
logger.log(Level.INFO, "Supported image formats by javafx image viewer: ");
//initialize supported image types
//TODO use mime-types instead once we have support
String[] fxSupportedImagesSuffixes = ImageIO.getReaderFileSuffixes();
IMAGES = new String[fxSupportedImagesSuffixes.length];
//logger.log(Level.INFO, "Supported image formats by javafx image viewer: ");
for (int i = 0; i < fxSupportedImagesSuffixes.length; ++i) {
String suffix = fxSupportedImagesSuffixes[i];
logger.log(Level.INFO, "suffix: " + suffix);
//logger.log(Level.INFO, "suffix: " + suffix);
IMAGES[i] = "." + suffix;
}
@ -110,35 +112,38 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
@Override
public void setNode(Node selectedNode) {
if (selectedNode == null) {
resetComponent();
return;
}
try {
if (selectedNode == null) {
resetComponent();
return;
}
AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class);
if (file == null) {
resetComponent();
return;
}
AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class);
if (file == null) {
resetComponent();
return;
}
if (file.equals(lastFile)) {
return; //prevent from loading twice if setNode() called mult. times
} else {
if (file.equals(lastFile)) {
return; //prevent from loading twice if setNode() called mult. times
}
resetComponent();
final Dimension dims = DataContentViewerMedia.this.getSize();
if (imagePanelInited && containsExt(file.getName(), IMAGES)) {
imagePanel.showImageFx(file, dims);
this.switchPanels(false);
} else if (videoPanelInited
&& (containsExt(file.getName(), VIDEOS) || containsExt(file.getName(), AUDIOS))) {
videoPanel.setupVideo(file, dims);
switchPanels(true);
}
lastFile = file;
}
videoPanel.reset();
final Dimension dims = DataContentViewerMedia.this.getSize();
if (imagePanelInited && containsExt(file.getName(), IMAGES)) {
imagePanel.showImageFx(file, dims);
this.switchPanels(false);
} else if (videoPanelInited
&& (containsExt(file.getName(), VIDEOS) || containsExt(file.getName(), AUDIOS))) {
videoPanel.setupVideo(file, dims);
switchPanels(true);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception while setting node", e);
}
}
/**
@ -177,12 +182,11 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
@Override
public void resetComponent() {
lastFile = null;
videoPanel.reset();
// @@@ Seems like we should also reset the image viewer...
lastFile = null;
}
@Override
public boolean isSupported(Node node) {
if (node == null) {

View File

@ -0,0 +1,642 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 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.corecomponents;
import com.sun.javafx.application.PlatformImpl;
import java.awt.Component;
import java.awt.Dimension;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.embed.swing.JFXPanel;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaException;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaPlayer.Status;
import static javafx.scene.media.MediaPlayer.Status.READY;
import javafx.scene.media.MediaView;
import javafx.util.Duration;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.modules.ModuleInstall;
import org.openide.util.Cancellable;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Video viewer part of the Media View layered pane.
*/
@ServiceProviders(value = {
@ServiceProvider(service = FrameCapture.class)
})
public class FXVideoPanel extends MediaViewVideoPanel {
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
private boolean fxInited = false;
// FX Components
private MediaPlayer fxMediaPlayer;
private MediaPane mediaPane;
// Current media content representations
private AbstractFile currentFile;
// FX UI Components
private JFXPanel videoComponent;
/**
* Creates new form MediaViewVideoPanel
*/
public FXVideoPanel() {
org.sleuthkit.autopsy.core.Installer coreInstaller =
ModuleInstall.findObject(org.sleuthkit.autopsy.core.Installer.class, false);
if (coreInstaller != null) {
fxInited = coreInstaller.isJavaFxInited();
}
initComponents();
customizeComponents();
}
public JPanel getVideoPanel() {
return videoPanel;
}
public Component getVideoComponent() {
return videoComponent;
}
private void customizeComponents() {
setupFx();
}
@Override
synchronized void setupVideo(final AbstractFile file, final Dimension dims) {
currentFile = file;
final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
if (deleted) {
mediaPane.setInfoLabelText("Playback of deleted videos is not supported, use an external player.");
videoPanel.removeAll();
return;
}
String path = "";
try {
path = file.getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Cannot get unique path of video file");
}
mediaPane.setInfoLabelText(path);
mediaPane.setInfoLabelToolTipText(path);
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
em.execute();
}
synchronized void setupFx() {
if(!fxInited) {
return;
}
logger.log(Level.INFO, "In Setup FX");
PlatformImpl.runLater(new Runnable() {
@Override
public void run() {
mediaPane = new MediaPane();
logger.log(Level.INFO, "Created MediaPane");
Scene fxScene = new Scene(mediaPane);
videoComponent = new JFXPanel();
videoComponent.setScene(fxScene);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Configure VideoPanel
videoPanel.removeAll();
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
videoPanel.add(videoComponent);
videoPanel.setVisible(true);
}
});
}
});
}
@Override
void reset() {
PlatformImpl.runLater(new Runnable() {
@Override
public void run() {
if (fxMediaPlayer != null) {
if (fxMediaPlayer.getStatus() == MediaPlayer.Status.PLAYING ) {
fxMediaPlayer.stop();
}
fxMediaPlayer = null;
}
if (videoComponent != null) {
videoComponent = null;
}
}
});
currentFile = null;
}
private java.io.File getJFile(AbstractFile file) {
// Get the temp folder path of the case
String tempPath = Case.getCurrentCase().getTempDirectory();
String name = file.getName();
int extStart = name.lastIndexOf(".");
String ext = "";
if (extStart != -1) {
ext = name.substring(extStart, name.length()).toLowerCase();
}
tempPath = tempPath + java.io.File.separator + file.getId() + ext;
java.io.File tempFile = new java.io.File(tempPath);
return tempFile;
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
videoPanel = new javax.swing.JPanel();
javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel);
videoPanel.setLayout(videoPanelLayout);
videoPanelLayout.setHorizontalGroup(
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 448, Short.MAX_VALUE)
);
videoPanelLayout.setVerticalGroup(
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 248, Short.MAX_VALUE)
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(videoPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
}// </editor-fold>
// Variables declaration - do not modify
private javax.swing.JPanel videoPanel;
// End of variables declaration
@Override
public boolean isInited() {
return fxInited;
}
/**
* Thread that extracts Media from a Sleuthkit file representation to a
* Java file representation that the Media Player can take as input.
*/
private class ExtractMedia extends SwingWorker<Object, Void> {
private ProgressHandle progress;
boolean success = false;
private AbstractFile sFile;
private java.io.File jFile;
private long extractedBytes;
ExtractMedia(org.sleuthkit.datamodel.AbstractFile sFile, java.io.File jFile) {
this.sFile = sFile;
this.jFile = jFile;
}
public long getExtractedBytes() {
return extractedBytes;
}
public Media getMedia() {
return new Media(Paths.get(jFile.getAbsolutePath()).toUri().toString());
}
@Override
protected Object doInBackground() throws Exception {
success = false;
progress = ProgressHandleFactory.createHandle("Buffering " + sFile.getName(), new Cancellable() {
@Override
public boolean cancel() {
return ExtractMedia.this.cancel(true);
}
});
mediaPane.setProgressLabelText("Buffering... ");
progress.start();
progress.switchToDeterminate(100);
try {
extractedBytes = ContentUtils.writeToFile(sFile, jFile, progress, this, true);
} catch (IOException ex) {
logger.log(Level.WARNING, "Error buffering file", ex);
}
logger.log(Level.INFO, "Done buffering: " + jFile.getName());
success = true;
return null;
}
/* clean up or start the worker threads */
@Override
protected void done() {
try {
super.get(); //block and get all exceptions thrown while doInBackground()
} catch (CancellationException ex) {
logger.log(Level.INFO, "Media buffering was canceled.");
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Media buffering was interrupted.");
} catch (Exception ex) {
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex);
} finally {
progress.finish();
if (!this.isCancelled()) {
logger.log(Level.INFO, "ExtractMedia in done: " + jFile.getName());
try {
PlatformImpl.runLater(new Runnable() {
@Override
public void run() {
fxMediaPlayer = new MediaPlayer(getMedia());
logger.log(Level.INFO, "Fx Media Player null? " + (fxMediaPlayer == null));
logger.log(Level.INFO, "Media Tools null? " + (mediaPane == null));
mediaPane.setMediaPlayer(fxMediaPlayer);
}
});
} catch(MediaException e) {
logger.log(Level.WARNING, "something went wrong with javafx", e);
reset();
mediaPane.setInfoLabelText(e.getMessage());
return;
}
}
}
}
}
private class MediaPane extends BorderPane {
private MediaPlayer mediaPlayer;
private MediaView mediaView;
private Duration duration;
private HBox mediaTools;
private HBox mediaViewPane;
private Slider progressSlider;
private Button pauseButton;
private Label progressLabel;
private Label infoLabel;
private int totalHours;
private int totalMinutes;
private int totalSeconds;
private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d ";
public MediaPane() {
// Video Display
mediaViewPane = new HBox();
mediaViewPane.setStyle("-fx-background-color: black");
mediaViewPane.setAlignment(Pos.CENTER);
mediaView = new MediaView();
mediaViewPane.getChildren().add(mediaView);
setAlignment(mediaViewPane, Pos.CENTER);
setCenter(mediaViewPane);
// Media Controls
VBox controlPanel = new VBox();
mediaTools = new HBox();
mediaTools.setAlignment(Pos.CENTER);
mediaTools.setPadding(new Insets(5, 10, 5, 10));
pauseButton = new Button("");
mediaTools.getChildren().add(pauseButton);
mediaTools.getChildren().add(new Label(" "));
progressSlider = new Slider();
HBox.setHgrow(progressSlider,Priority.ALWAYS);
progressSlider.setMinWidth(50);
progressSlider.setMaxWidth(Double.MAX_VALUE);
mediaTools.getChildren().add(progressSlider);
progressLabel = new Label();
progressLabel.setPrefWidth(130);
progressLabel.setMinWidth(50);
mediaTools.getChildren().add(progressLabel);
controlPanel.getChildren().add(mediaTools);
controlPanel.setStyle("-fx-background-color: white");
infoLabel = new Label("");
controlPanel.getChildren().add(infoLabel);
setBottom(controlPanel);
setProgressActionListeners();
}
public void setInfoLabelText(final String text) {
PlatformImpl.runLater(new Runnable() {
@Override
public void run() {
infoLabel.setText(text);
}
});
}
public void setMediaPlayer(MediaPlayer mp) {
pauseButton.setDisable(true);
mediaPlayer = mp;
mediaView.setMediaPlayer(mp);
pauseButton.setDisable(false);
setMediaActionListeners();
}
private void setMediaActionListeners() {
mediaPlayer.setOnReady(new Runnable() {
@Override
public void run() {
duration = mediaPlayer.getMedia().getDuration();
long durationInMillis = (long) fxMediaPlayer.getMedia().getDuration().toMillis();
// pick out the total hours, minutes, seconds
long durationSeconds = (int) durationInMillis / 1000;
totalHours = (int) durationSeconds / 3600;
durationSeconds -= totalHours * 3600;
totalMinutes = (int) durationSeconds / 60;
durationSeconds -= totalMinutes * 60;
totalSeconds = (int) durationSeconds;
updateProgress();
}
});
mediaPlayer.setOnEndOfMedia(new Runnable() {
@Override
public void run() {
Duration beginning = mediaPlayer.getStartTime();
mediaPlayer.stop();
mediaPlayer.pause();
pauseButton.setText("");
updateSlider(beginning);
updateTime(beginning);
}
});
mediaPlayer.currentTimeProperty().addListener(new ChangeListener<Duration>() {
@Override
public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
updateSlider(newValue);
updateTime(newValue);
}
});
}
private void setProgressActionListeners() {
pauseButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
Status status = mediaPlayer.getStatus();
switch (status) {
// If playing, pause
case PLAYING:
pauseButton.setText("");
mediaPlayer.pause();
break;
// If ready, paused or stopped, continue playing
case READY:
case PAUSED:
case STOPPED:
pauseButton.setText("||");
mediaPlayer.play();
break;
default:
break;
}
}
});
progressSlider.valueProperty().addListener(new InvalidationListener() {
@Override
public void invalidated(Observable o) {
if (progressSlider.isValueChanging()) {
mediaPlayer.seek(duration.multiply(progressSlider.getValue() / 100.0));
}
}
});
}
private void updateProgress() {
Duration currentTime = mediaPlayer.getCurrentTime();
updateSlider(currentTime);
updateTime(currentTime);
}
private void updateSlider(Duration currentTime) {
if (progressSlider != null) {
progressSlider.setDisable(duration.isUnknown());
if (!progressSlider.isDisabled() && duration.greaterThan(Duration.ZERO)
&& !progressSlider.isValueChanging()) {
progressSlider.setValue(currentTime.divide(duration.toMillis()).toMillis() * 100.0);
}
}
}
private void updateTime(Duration currentTime) {
long millisElapsed = (long) currentTime.toMillis();
long elapsedHours, elapsedMinutes, elapsedSeconds;
// pick out the elapsed hours, minutes, seconds
long secondsElapsed = millisElapsed / 1000;
elapsedHours = (int) secondsElapsed / 3600;
secondsElapsed -= elapsedHours * 3600;
elapsedMinutes = (int) secondsElapsed / 60;
secondsElapsed -= elapsedMinutes * 60;
elapsedSeconds = (int) secondsElapsed;
String durationStr = String.format(durationFormat,
elapsedHours, elapsedMinutes, elapsedSeconds,
totalHours, totalMinutes, totalSeconds);
progressLabel.setText(durationStr);
}
private void setProgressLabelText(final String text) {
PlatformImpl.runLater(new Runnable() {
@Override
public void run() {
progressLabel.setText(text);
}
});
}
private void setInfoLabelToolTipText(final String text) {
PlatformImpl.runLater(new Runnable() {
@Override
public void run() {
infoLabel.setTooltip(new Tooltip(text));
}
});
}
}
/**
* @param file a video file from which to capture frames
* @param numFrames the number of frames to capture. These frames will be
* captured at successive intervals given by durationOfVideo/numFrames. If
* this frame interval is less than MIN_FRAME_INTERVAL_MILLIS, then only one
* frame will be captured and returned.
* @return a List of VideoFrames representing the captured frames.
*/
@Override
public List<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception {
//
// try {
// List<VideoFrame> frames = new ArrayList<>();
//
// FrameCapturer fc = new FrameCapturer(file);
// logger.log(Level.INFO, "Fc is null? " + (fc == null));
// frames = fc.getFrames(numFrames);
//
// return frames;
// }
// catch (NullPointerException e) {
// e.printStackTrace();
// return null;
// }
return null;
}
// private class FrameCapturer {
//
// private MediaPlayer mediaPlayer;
// private JFXPanel panel;
// private boolean isReady = false;
//
// FrameCapturer(java.io.File file) {
// initFx(file);
// }
//
// boolean isReady() {
// return isReady;
// }
//
// private void initFx(final java.io.File file) {
// PlatformImpl.runAndWait(new Runnable() {
// @Override
// public void run() {
// logger.log(Level.INFO, "In initFX.");
// // Create Media Player with no video output
// Media media = new Media(Paths.get(file.getAbsolutePath()).toUri().toString());
// mediaPlayer = new MediaPlayer(media);
// MediaView mediaView = new MediaView(mediaPlayer);
// mediaView.setStyle("-fx-background-color: black");
// Pane mediaViewPane = new Pane();
// mediaViewPane.getChildren().add(mediaView);
// Scene scene = new Scene(mediaViewPane);
// panel = new JFXPanel();
// panel.setScene(scene);
// isReady = true;
// }
// });
// }
//
// List<VideoFrame> getFrames(int numFrames) {
// logger.log(Level.INFO, "in get frames");
// List<VideoFrame> frames = new ArrayList<VideoFrame>(0);
//
// if (mediaPlayer.getStatus() != Status.READY) {
// try {
// Thread.sleep(500);
// } catch (InterruptedException e) {
// return frames;
// }
// }
//
// // get the duration of the video
// long myDurationMillis = (long) mediaPlayer.getMedia().getDuration().toMillis();
// if (myDurationMillis <= 0) {
// return frames;
// }
//
// // calculate the frame interval
// int numFramesToGet = numFrames;
// long frameInterval = (myDurationMillis - INTER_FRAME_PERIOD_MS) / numFrames;
// if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) {
// numFramesToGet = 1;
// }
//
// final Object frameLock = new Object();
// BufferedImage frame;
// final int width = (int) panel.getSize().getWidth();
// final int height = (int) panel.getSize().getHeight();
// // for each timeStamp, grap a frame
// for (int i = 0; i < numFramesToGet; ++i) {
// frame = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// logger.log(Level.INFO, "Grabbing a frame...");
// final long timeStamp = i * frameInterval + INTER_FRAME_PERIOD_MS;
//
// // PlatformImpl.runLater(new Runnable() {
// // @Override
// // public void run() {
// // synchronized (frameLock) {
// logger.log(Level.INFO, "seeking.");
// mediaPlayer.seek(new Duration(timeStamp));
// // }
// // }
// // });
//
// synchronized (frameLock) {
// panel.paint(frame.createGraphics());
// logger.log(Level.INFO, "Adding image to frames");
// }
// frames.add(new VideoFrame(frame, timeStamp));
// }
// return frames;
// }
// }
}

View File

@ -0,0 +1,770 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 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.corecomponents;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.gstreamer.ClockTime;
import org.gstreamer.Gst;
import org.gstreamer.GstException;
import org.gstreamer.State;
import org.gstreamer.StateChangeReturn;
import org.gstreamer.elements.PlayBin2;
import org.gstreamer.elements.RGBDataSink;
import org.gstreamer.swing.VideoComponent;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.Cancellable;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@ServiceProviders(value = {
@ServiceProvider(service = FrameCapture.class)
})
public class GstVideoPanel extends MediaViewVideoPanel {
private static final Logger logger = Logger.getLogger(GstVideoPanel.class.getName());
private boolean gstInited;
private static final long MIN_FRAME_INTERVAL_MILLIS = 500;
private static final long FRAME_CAPTURE_TIMEOUT_MILLIS = 1000;
private static final String MEDIA_PLAYER_ERROR_STRING = "The media player cannot process this file.";
//playback
private long durationMillis = 0;
private VideoProgressWorker videoProgressWorker;
private int totalHours, totalMinutes, totalSeconds;
private volatile PlayBin2 gstPlaybin2;
private VideoComponent gstVideoComponent;
private boolean autoTracking = false; // true if the slider is moving automatically
private final Object playbinLock = new Object(); // lock for synchronization of gstPlaybin2 player
private AbstractFile currentFile;
private Set<String> badVideoFiles = Collections.synchronizedSet(new HashSet<String>());
/**
* Creates new form MediaViewVideoPanel
*/
public GstVideoPanel() {
initComponents();
customizeComponents();
}
public JButton getPauseButton() {
return pauseButton;
}
public JLabel getProgressLabel() {
return progressLabel;
}
public JSlider getProgressSlider() {
return progressSlider;
}
public JPanel getVideoPanel() {
return videoPanel;
}
public VideoComponent getVideoComponent() {
return gstVideoComponent;
}
@Override
public boolean isInited() {
return gstInited;
}
private void customizeComponents() {
if (!initGst()) {
return;
}
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
progressSlider.setValue(0);
progressSlider.addChangeListener(new ChangeListener() {
/**
* Should always try to synchronize any call to
* progressSlider.setValue() to avoid a different thread
* changing playbin while stateChanged() is processing
*/
@Override
public void stateChanged(ChangeEvent e) {
int time = progressSlider.getValue();
synchronized (playbinLock) {
if (gstPlaybin2 != null && !autoTracking) {
State orig = gstPlaybin2.getState();
if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
if (gstPlaybin2.seek(ClockTime.fromMillis(time)) == false) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.seek() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
gstPlaybin2.setState(orig);
}
}
}
});
}
private boolean initGst() {
try {
logger.log(Level.INFO, "Initializing gstreamer for video/audio viewing");
Gst.init();
gstInited = true;
} catch (GstException e) {
gstInited = false;
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and frame extraction capabilities", e);
MessageNotifyUtil.Notify.error("Error initializing gstreamer for audio/video viewing and frame extraction capabilities. "
+ " Video and audio viewing will be disabled. ",
e.getMessage());
return false;
} catch (UnsatisfiedLinkError | NoClassDefFoundError | Exception e) {
gstInited = false;
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and extraction capabilities", e);
MessageNotifyUtil.Notify.error("Error initializing gstreamer for audio/video viewing frame extraction capabilities. "
+ " Video and audio viewing will be disabled. ",
e.getMessage());
return false;
}
return true;
}
@Override
void setupVideo(final AbstractFile file, final Dimension dims) {
infoLabel.setText("");
currentFile = file;
final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
if (deleted) {
infoLabel.setText("Playback of deleted videos is not supported, use an external player.");
videoPanel.removeAll();
pauseButton.setEnabled(false);
progressSlider.setEnabled(false);
return;
}
String path = "";
try {
path = file.getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Cannot get unique path of video file");
}
infoLabel.setText(path);
infoLabel.setToolTipText(path);
pauseButton.setEnabled(true);
progressSlider.setEnabled(true);
java.io.File ioFile = getJFile(file);
gstVideoComponent = new VideoComponent();
synchronized (playbinLock) {
if (gstPlaybin2 != null) {
gstPlaybin2.dispose();
}
gstPlaybin2 = new PlayBin2("VideoPlayer");
gstPlaybin2.setVideoSink(gstVideoComponent.getElement());
videoPanel.removeAll();
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
videoPanel.add(gstVideoComponent);
videoPanel.setVisible(true);
gstPlaybin2.setInputFile(ioFile);
if (gstPlaybin2.setState(State.READY) == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.READY) failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
}
}
@Override
void reset() {
// reset the progress label text on the event dispatch thread
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
progressLabel.setText("");
}
});
if (!isInited()) {
return;
}
synchronized (playbinLock) {
if (gstPlaybin2 != null) {
if (gstPlaybin2.isPlaying()) {
if (gstPlaybin2.stop() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.stop() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
}
if (gstPlaybin2.setState(State.NULL) == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.NULL) failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
if (gstPlaybin2.getState().equals(State.NULL)) {
gstPlaybin2.dispose();
}
gstPlaybin2 = null;
}
gstVideoComponent = null;
}
// get rid of any existing videoProgressWorker thread
if (videoProgressWorker != null) {
videoProgressWorker.cancel(true);
videoProgressWorker = null;
}
currentFile = null;
}
private java.io.File getJFile(AbstractFile file) {
// Get the temp folder path of the case
String tempPath = Case.getCurrentCase().getTempDirectory();
String name = file.getName();
int extStart = name.lastIndexOf(".");
String ext = "";
if (extStart != -1) {
ext = name.substring(extStart, name.length()).toLowerCase();
}
tempPath = tempPath + java.io.File.separator + file.getId() + ext;
java.io.File tempFile = new java.io.File(tempPath);
return tempFile;
}
/**
* @param file a video file from which to capture frames
* @param numFrames the number of frames to capture. These frames will be
* captured at successive intervals given by durationOfVideo/numFrames. If
* this frame interval is less than MIN_FRAME_INTERVAL_MILLIS, then only one
* frame will be captured and returned.
* @return a List of VideoFrames representing the captured frames.
*/
@Override
public List<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception {
List<VideoFrame> frames = new ArrayList<>();
Object lock = new Object();
FrameCaptureRGBListener rgbListener = new FrameCaptureRGBListener(lock);
if (!isInited()) {
return frames;
}
// throw exception if this file is known to be problematic
if (badVideoFiles.contains(file.getName())) {
throw new Exception("Cannot capture frames from this file (" + file.getName() + ").");
}
// set up a PlayBin2 object
RGBDataSink videoSink = new RGBDataSink("rgb", rgbListener);
PlayBin2 playbin = new PlayBin2("VideoFrameCapture");
playbin.setInputFile(file);
playbin.setVideoSink(videoSink);
// this is necessary to get a valid duration value
StateChangeReturn ret = playbin.play();
if (ret == StateChangeReturn.FAILURE) {
// add this file to the set of known bad ones
badVideoFiles.add(file.getName());
throw new Exception("Problem with video file; problem when attempting to play while obtaining duration.");
}
ret = playbin.pause();
if (ret == StateChangeReturn.FAILURE) {
// add this file to the set of known bad ones
badVideoFiles.add(file.getName());
throw new Exception("Problem with video file; problem when attempting to pause while obtaining duration.");
}
playbin.getState();
// get the duration of the video
TimeUnit unit = TimeUnit.MILLISECONDS;
long myDurationMillis = playbin.queryDuration(unit);
if (myDurationMillis <= 0) {
return frames;
}
// calculate the number of frames to capture
int numFramesToGet = numFrames;
long frameInterval = myDurationMillis / numFrames;
if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) {
numFramesToGet = 1;
}
// for each timeStamp, grap a frame
for (int i = 0; i < numFramesToGet; ++i) {
long timeStamp = i * frameInterval;
ret = playbin.pause();
if (ret == StateChangeReturn.FAILURE) {
// add this file to the set of known bad ones
badVideoFiles.add(file.getName());
throw new Exception("Problem with video file; problem when attempting to pause while capturing a frame.");
}
playbin.getState();
//System.out.println("Seeking to " + timeStamp + "milliseconds.");
if (!playbin.seek(timeStamp, unit)) {
logger.log(Level.INFO, "There was a problem seeking to " + timeStamp + " " + unit.name().toLowerCase());
}
ret = playbin.play();
if (ret == StateChangeReturn.FAILURE) {
// add this file to the set of known bad ones
badVideoFiles.add(file.getName());
throw new Exception("Problem with video file; problem when attempting to play while capturing a frame.");
}
// wait for FrameCaptureRGBListener to finish
synchronized(lock) {
try {
lock.wait(FRAME_CAPTURE_TIMEOUT_MILLIS);
} catch (InterruptedException e) {
logger.log(Level.INFO, "InterruptedException occurred while waiting for frame capture.", e);
}
}
Image image = rgbListener.getImage();
ret = playbin.stop();
if (ret == StateChangeReturn.FAILURE) {
// add this file to the set of known bad ones
badVideoFiles.add(file.getName());
throw new Exception("Problem with video file; problem when attempting to stop while capturing a frame.");
}
if (image == null) {
logger.log(Level.WARNING, "There was a problem while trying to capture a frame from file " + file.getName());
badVideoFiles.add(file.getName());
break;
}
frames.add(new VideoFrame(image, timeStamp));
}
return frames;
}
private class FrameCaptureRGBListener implements RGBDataSink.Listener {
public FrameCaptureRGBListener(Object waiter) {
this.waiter = waiter;
}
private BufferedImage bi;
private final Object waiter;
@Override
public void rgbFrame(boolean bln, int w, int h, IntBuffer rgbPixels) {
synchronized (waiter) {
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
bi.setRGB(0, 0, w, h, rgbPixels.array(), 0, w);
waiter.notify();
}
}
public Image getImage() {
synchronized (waiter) {
Image image = bi;
bi = null;
return image;
}
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
videoPanel = new javax.swing.JPanel();
controlPanel = new javax.swing.JPanel();
pauseButton = new javax.swing.JButton();
progressSlider = new javax.swing.JSlider();
progressLabel = new javax.swing.JLabel();
infoLabel = new javax.swing.JLabel();
javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel);
videoPanel.setLayout(videoPanelLayout);
videoPanelLayout.setHorizontalGroup(
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
videoPanelLayout.setVerticalGroup(
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 188, Short.MAX_VALUE)
);
org.openide.awt.Mnemonics.setLocalizedText(pauseButton, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.pauseButton.text")); // NOI18N
pauseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pauseButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N
javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel);
controlPanel.setLayout(controlPanelLayout);
controlPanelLayout.setHorizontalGroup(
controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(controlPanelLayout.createSequentialGroup()
.addComponent(pauseButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 357, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(progressLabel)
.addContainerGap())
.addGroup(controlPanelLayout.createSequentialGroup()
.addComponent(infoLabel)
.addGap(0, 0, Short.MAX_VALUE))
);
controlPanelLayout.setVerticalGroup(
controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(controlPanelLayout.createSequentialGroup()
.addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(pauseButton)
.addComponent(progressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(progressLabel, javax.swing.GroupLayout.Alignment.TRAILING))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(infoLabel))
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(videoPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
}// </editor-fold>
private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {
synchronized (playbinLock) {
if (gstPlaybin2 == null) {
infoLabel.setText("Error: Playbin is null");
return;
}
State state = gstPlaybin2.getState();
if (state.equals(State.PLAYING)) {
if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
pauseButton.setText("");
// Is this call necessary considering we just called gstPlaybin2.pause()?
if (gstPlaybin2.setState(State.PAUSED) == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.PAUSED) failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
} else if (state.equals(State.PAUSED)) {
if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
pauseButton.setText("||");
// Is this call necessary considering we just called gstPlaybin2.play()?
if (gstPlaybin2.setState(State.PLAYING) == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.PLAYING) failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
} else if (state.equals(State.READY)) {
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
em.execute();
em.getExtractedBytes();
}
}
}
// Variables declaration - do not modify
private javax.swing.JPanel controlPanel;
private javax.swing.JLabel infoLabel;
private javax.swing.JButton pauseButton;
private javax.swing.JLabel progressLabel;
private javax.swing.JSlider progressSlider;
private javax.swing.JPanel videoPanel;
// End of variables declaration
private class VideoProgressWorker extends SwingWorker<Object, Object> {
private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d ";
private long millisElapsed = 0;
private final long INTER_FRAME_PERIOD_MS = 20;
private final long END_TIME_MARGIN_MS = 50;
private boolean hadError = false;
private boolean isPlayBinReady() {
synchronized (playbinLock) {
return gstPlaybin2 != null && !gstPlaybin2.getState().equals(State.NULL);
}
}
private void resetVideo() throws Exception {
synchronized (playbinLock) {
if (gstPlaybin2 != null) {
if (gstPlaybin2.stop() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.stop() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
// ready to be played again
if (gstPlaybin2.setState(State.READY) == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.READY) failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
gstPlaybin2.getState(); //NEW
}
}
pauseButton.setText("");
progressSlider.setValue(0);
String durationStr = String.format(durationFormat, 0, 0, 0,
totalHours, totalMinutes, totalSeconds);
progressLabel.setText(durationStr);
}
/**
* @return true while millisElapsed is greater than END_TIME_MARGIN_MS
* from durationMillis. This is used to indicate when the video has
* ended because for some videos the time elapsed never becomes equal to
* the reported duration of the video.
*/
private boolean hasNotEnded() {
return (durationMillis - millisElapsed) > END_TIME_MARGIN_MS;
}
@Override
protected Object doInBackground() throws Exception {
// enable the slider
progressSlider.setEnabled(true);
int elapsedHours = -1, elapsedMinutes = -1, elapsedSeconds = -1;
ClockTime pos = null;
while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
synchronized (playbinLock) {
pos = gstPlaybin2.queryPosition();
}
millisElapsed = pos.toMillis();
// pick out the elapsed hours, minutes, seconds
long secondsElapsed = millisElapsed / 1000;
elapsedHours = (int) secondsElapsed / 3600;
secondsElapsed -= elapsedHours * 3600;
elapsedMinutes = (int) secondsElapsed / 60;
secondsElapsed -= elapsedMinutes * 60;
elapsedSeconds = (int) secondsElapsed;
String durationStr = String.format(durationFormat,
elapsedHours, elapsedMinutes, elapsedSeconds,
totalHours, totalMinutes, totalSeconds);
progressLabel.setText(durationStr);
autoTracking = true;
progressSlider.setValue((int) millisElapsed);
autoTracking = false;
try {
Thread.sleep(INTER_FRAME_PERIOD_MS);
} catch (InterruptedException ex) {
break;
}
}
// disable the slider
progressSlider.setEnabled(false);
resetVideo();
return null;
}
} //end class progress worker
/* Thread that extracts and plays a file */
private class ExtractMedia extends SwingWorker<Object, Void> {
private ProgressHandle progress;
boolean success = false;
private AbstractFile sFile;
private java.io.File jFile;
private String duration;
private String position;
private long extractedBytes;
ExtractMedia(org.sleuthkit.datamodel.AbstractFile sFile, java.io.File jFile) {
this.sFile = sFile;
this.jFile = jFile;
}
public long getExtractedBytes() {
return extractedBytes;
}
@Override
protected Object doInBackground() throws Exception {
success = false;
progress = ProgressHandleFactory.createHandle("Buffering " + sFile.getName(), new Cancellable() {
@Override
public boolean cancel() {
return ExtractMedia.this.cancel(true);
}
});
progressLabel.setText("Buffering... ");
progress.start();
progress.switchToDeterminate(100);
try {
extractedBytes = ContentUtils.writeToFile(sFile, jFile, progress, this, true);
} catch (IOException ex) {
logger.log(Level.WARNING, "Error buffering file", ex);
}
success = true;
return null;
}
/* clean up or start the worker threads */
@Override
protected void done() {
try {
super.get(); //block and get all exceptions thrown while doInBackground()
} catch (CancellationException ex) {
logger.log(Level.INFO, "Media buffering was canceled.");
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Media buffering was interrupted.");
} catch (Exception ex) {
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex);
} finally {
progress.finish();
if (!this.isCancelled()) {
playMedia();
}
}
}
void playMedia() {
if (jFile == null || !jFile.exists()) {
progressLabel.setText("Error buffering file");
return;
}
ClockTime dur = null;
synchronized (playbinLock) {
// must play, then pause and get state to get duration.
if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
gstPlaybin2.getState();
dur = gstPlaybin2.queryDuration();
}
duration = dur.toString();
durationMillis = dur.toMillis();
// pick out the total hours, minutes, seconds
long durationSeconds = (int) durationMillis / 1000;
totalHours = (int) durationSeconds / 3600;
durationSeconds -= totalHours * 3600;
totalMinutes = (int) durationSeconds / 60;
durationSeconds -= totalMinutes * 60;
totalSeconds = (int) durationSeconds;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
progressSlider.setMaximum((int) durationMillis);
progressSlider.setMinimum(0);
synchronized (playbinLock) {
if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
}
pauseButton.setText("||");
videoProgressWorker = new VideoProgressWorker();
videoProgressWorker.execute();
}
});
}
}
}

View File

@ -1,113 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="videoPanel" alignment="1" max="32767" attributes="0"/>
<Component id="controlPanel" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="videoPanel" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="controlPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="videoPanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="188" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
</Container>
<Container class="javax.swing.JPanel" name="controlPanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="pauseButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="progressSlider" pref="357" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="progressLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="infoLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="pauseButton" min="-2" max="-2" attributes="0"/>
<Component id="progressSlider" min="-2" max="-2" attributes="0"/>
<Component id="progressLabel" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="infoLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="pauseButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MediaViewVideoPanel.pauseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pauseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JSlider" name="progressSlider">
</Component>
<Component class="javax.swing.JLabel" name="progressLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MediaViewVideoPanel.progressLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="infoLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="MediaViewVideoPanel.infoLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -19,753 +19,100 @@
package org.sleuthkit.autopsy.corecomponents;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
import java.util.Arrays;
import java.util.logging.Level;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import java.util.logging.Logger;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.gstreamer.ClockTime;
import org.gstreamer.Gst;
import org.gstreamer.GstException;
import org.gstreamer.State;
import org.gstreamer.StateChangeReturn;
import org.gstreamer.elements.PlayBin2;
import org.gstreamer.elements.RGBDataSink;
import org.gstreamer.swing.VideoComponent;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.Cancellable;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* Video viewer part of the Media View layered pane
* Video viewer part of the Media View layered pane.
* Uses different engines depending on platform.
*/
@ServiceProviders(value = {
@ServiceProvider(service = FrameCapture.class)
})
public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapture {
public abstract class MediaViewVideoPanel extends JPanel implements FrameCapture {
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
private boolean gstInited;
private static final long MIN_FRAME_INTERVAL_MILLIS = 500;
private static final long FRAME_CAPTURE_TIMEOUT_MILLIS = 1000;
private static final String MEDIA_PLAYER_ERROR_STRING = "The media player cannot process this file.";
//playback
private long durationMillis = 0;
private VideoProgressWorker videoProgressWorker;
private int totalHours, totalMinutes, totalSeconds;
private volatile PlayBin2 gstPlaybin2;
private VideoComponent gstVideoComponent;
private boolean autoTracking = false; // true if the slider is moving automatically
private final Object playbinLock = new Object(); // lock for synchronization of gstPlaybin2 player
private AbstractFile currentFile;
private Set<String> badVideoFiles = Collections.synchronizedSet(new HashSet<String>());
// 64 bit architectures
private static final String[] ARCH64 = new String[]{"amd64", "x86_64"};
// 32 bit architectures
private static final String[] ARCH32 = new String[]{"x86"};
// A Gstreamer implementation of MediaViewVideoPanel
private static GstVideoPanel gstVideoPanel = null;
// A JavaFX implmentation of MediaViewVideoPanel
private static FXVideoPanel fxVideoPanel = null;
/**
* Factory Method to create a MediaViewVideoPanel.
*
* Implementation is dependent on the architecture of the JVM.
*
* @return a MediaViewVideoPanel instance.
*/
public static MediaViewVideoPanel createVideoPanel() {
if (is64BitJVM()) {
logger.log(Level.INFO, "64 bit JVM detected. Creating JavaFX Video Player.");
return getFXImpl();
} else {
logger.log(Level.INFO, "32 bit JVM detected. Creating GStreamer Video Player.");
return getGstImpl();
}
}
/**
* Is the JVM architecture 64 bit?
*
* @return
*/
private static boolean is64BitJVM() {
String arch = System.getProperty("os.arch");
return Arrays.asList(ARCH64).contains(arch);
}
/**
* Creates new form MediaViewVideoPanel
* Get a GStreamer video player implementation.
*
* @return a GstVideoPanel
*/
public MediaViewVideoPanel() {
initComponents();
customizeComponents();
}
public JButton getPauseButton() {
return pauseButton;
}
public JLabel getProgressLabel() {
return progressLabel;
}
public JSlider getProgressSlider() {
return progressSlider;
}
public JPanel getVideoPanel() {
return videoPanel;
}
public VideoComponent getVideoComponent() {
return gstVideoComponent;
}
public boolean isInited() {
return gstInited;
}
private void customizeComponents() {
if (!initGst()) {
return;
private static MediaViewVideoPanel getGstImpl() {
if (gstVideoPanel == null) {
gstVideoPanel = new GstVideoPanel();
}
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
progressSlider.setValue(0);
progressSlider.addChangeListener(new ChangeListener() {
/**
* Should always try to synchronize any call to
* progressSlider.setValue() to avoid a different thread
* changing playbin while stateChanged() is processing
*/
@Override
public void stateChanged(ChangeEvent e) {
int time = progressSlider.getValue();
synchronized (playbinLock) {
if (gstPlaybin2 != null && !autoTracking) {
State orig = gstPlaybin2.getState();
if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
if (gstPlaybin2.seek(ClockTime.fromMillis(time)) == false) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.seek() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
gstPlaybin2.setState(orig);
}
}
}
});
return gstVideoPanel;
}
private boolean initGst() {
try {
logger.log(Level.INFO, "Initializing gstreamer for video/audio viewing");
Gst.init();
gstInited = true;
} catch (GstException e) {
gstInited = false;
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and frame extraction capabilities", e);
MessageNotifyUtil.Notify.error("Error initializing gstreamer for audio/video viewing and frame extraction capabilities. "
+ " Video and audio viewing will be disabled. ",
e.getMessage());
return false;
} catch (UnsatisfiedLinkError | NoClassDefFoundError | Exception e) {
gstInited = false;
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and extraction capabilities", e);
MessageNotifyUtil.Notify.error("Error initializing gstreamer for audio/video viewing frame extraction capabilities. "
+ " Video and audio viewing will be disabled. ",
e.getMessage());
return false;
/**
* Get a JavaFX video player implementation.
*
* @return a FXVideoPanel
*/
private static MediaViewVideoPanel getFXImpl() {
if (fxVideoPanel == null) {
fxVideoPanel = new FXVideoPanel();
}
return true;
return fxVideoPanel;
}
/**
* Has this MediaViewVideoPanel been initialized correctly?
*
* @return
*/
public abstract boolean isInited();
/**
* Prepare this MediaViewVideoPanel to accept a different media file.
*/
abstract void reset();
/**
* Initialize all the necessary vars to play a video/audio file.
*
* @param file video file to play
* @param dims dimension of the parent window
*/
void setupVideo(final AbstractFile file, final Dimension dims) {
infoLabel.setText("");
currentFile = file;
final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
if (deleted) {
infoLabel.setText("Playback of deleted videos is not supported, use an external player.");
videoPanel.removeAll();
pauseButton.setEnabled(false);
progressSlider.setEnabled(false);
return;
}
String path = "";
try {
path = file.getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Cannot get unique path of video file");
}
infoLabel.setText(path);
infoLabel.setToolTipText(path);
pauseButton.setEnabled(true);
progressSlider.setEnabled(true);
java.io.File ioFile = getJFile(file);
gstVideoComponent = new VideoComponent();
synchronized (playbinLock) {
if (gstPlaybin2 != null) {
gstPlaybin2.dispose();
}
gstPlaybin2 = new PlayBin2("VideoPlayer");
gstPlaybin2.setVideoSink(gstVideoComponent.getElement());
videoPanel.removeAll();
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
videoPanel.add(gstVideoComponent);
videoPanel.setVisible(true);
gstPlaybin2.setInputFile(ioFile);
if (gstPlaybin2.setState(State.READY) == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.READY) failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
}
}
void reset() {
// reset the progress label text on the event dispatch thread
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
progressLabel.setText("");
}
});
if (!isInited()) {
return;
}
synchronized (playbinLock) {
if (gstPlaybin2 != null) {
if (gstPlaybin2.isPlaying()) {
if (gstPlaybin2.stop() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.stop() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
}
if (gstPlaybin2.setState(State.NULL) == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.NULL) failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
if (gstPlaybin2.getState().equals(State.NULL)) {
gstPlaybin2.dispose();
}
gstPlaybin2 = null;
}
gstVideoComponent = null;
}
// get rid of any existing videoProgressWorker thread
if (videoProgressWorker != null) {
videoProgressWorker.cancel(true);
videoProgressWorker = null;
}
currentFile = null;
}
private java.io.File getJFile(AbstractFile file) {
// Get the temp folder path of the case
String tempPath = Case.getCurrentCase().getTempDirectory();
String name = file.getName();
int extStart = name.lastIndexOf(".");
String ext = "";
if (extStart != -1) {
ext = name.substring(extStart, name.length()).toLowerCase();
}
tempPath = tempPath + java.io.File.separator + file.getId() + ext;
java.io.File tempFile = new java.io.File(tempPath);
return tempFile;
}
/**
* @param file a video file from which to capture frames
* @param numFrames the number of frames to capture. These frames will be
* captured at successive intervals given by durationOfVideo/numFrames. If
* this frame interval is less than MIN_FRAME_INTERVAL_MILLIS, then only one
* frame will be captured and returned.
* @return a List of VideoFrames representing the captured frames.
*/
@Override
public List<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception {
List<VideoFrame> frames = new ArrayList<>();
Object lock = new Object();
FrameCaptureRGBListener rgbListener = new FrameCaptureRGBListener(lock);
if (!isInited()) {
return frames;
}
// throw exception if this file is known to be problematic
if (badVideoFiles.contains(file.getName())) {
throw new Exception("Cannot capture frames from this file (" + file.getName() + ").");
}
// set up a PlayBin2 object
RGBDataSink videoSink = new RGBDataSink("rgb", rgbListener);
PlayBin2 playbin = new PlayBin2("VideoFrameCapture");
playbin.setInputFile(file);
playbin.setVideoSink(videoSink);
// this is necessary to get a valid duration value
StateChangeReturn ret = playbin.play();
if (ret == StateChangeReturn.FAILURE) {
// add this file to the set of known bad ones
badVideoFiles.add(file.getName());
throw new Exception("Problem with video file; problem when attempting to play while obtaining duration.");
}
ret = playbin.pause();
if (ret == StateChangeReturn.FAILURE) {
// add this file to the set of known bad ones
badVideoFiles.add(file.getName());
throw new Exception("Problem with video file; problem when attempting to pause while obtaining duration.");
}
playbin.getState();
// get the duration of the video
TimeUnit unit = TimeUnit.MILLISECONDS;
long myDurationMillis = playbin.queryDuration(unit);
if (myDurationMillis <= 0) {
return frames;
}
// calculate the number of frames to capture
int numFramesToGet = numFrames;
long frameInterval = myDurationMillis / numFrames;
if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) {
numFramesToGet = 1;
}
// for each timeStamp, grap a frame
for (int i = 0; i < numFramesToGet; ++i) {
long timeStamp = i * frameInterval;
ret = playbin.pause();
if (ret == StateChangeReturn.FAILURE) {
// add this file to the set of known bad ones
badVideoFiles.add(file.getName());
throw new Exception("Problem with video file; problem when attempting to pause while capturing a frame.");
}
playbin.getState();
//System.out.println("Seeking to " + timeStamp + "milliseconds.");
if (!playbin.seek(timeStamp, unit)) {
logger.log(Level.INFO, "There was a problem seeking to " + timeStamp + " " + unit.name().toLowerCase());
}
ret = playbin.play();
if (ret == StateChangeReturn.FAILURE) {
// add this file to the set of known bad ones
badVideoFiles.add(file.getName());
throw new Exception("Problem with video file; problem when attempting to play while capturing a frame.");
}
// wait for FrameCaptureRGBListener to finish
synchronized(lock) {
try {
lock.wait(FRAME_CAPTURE_TIMEOUT_MILLIS);
} catch (InterruptedException e) {
logger.log(Level.INFO, "InterruptedException occurred while waiting for frame capture.", e);
}
}
Image image = rgbListener.getImage();
ret = playbin.stop();
if (ret == StateChangeReturn.FAILURE) {
// add this file to the set of known bad ones
badVideoFiles.add(file.getName());
throw new Exception("Problem with video file; problem when attempting to stop while capturing a frame.");
}
if (image == null) {
logger.log(Level.WARNING, "There was a problem while trying to capture a frame from file " + file.getName());
badVideoFiles.add(file.getName());
break;
}
frames.add(new VideoFrame(image, timeStamp));
}
return frames;
}
private class FrameCaptureRGBListener implements RGBDataSink.Listener {
public FrameCaptureRGBListener(Object waiter) {
this.waiter = waiter;
}
private BufferedImage bi;
private final Object waiter;
@Override
public void rgbFrame(boolean bln, int w, int h, IntBuffer rgbPixels) {
synchronized (waiter) {
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
bi.setRGB(0, 0, w, h, rgbPixels.array(), 0, w);
waiter.notify();
}
}
public Image getImage() {
synchronized (waiter) {
Image image = bi;
bi = null;
return image;
}
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
videoPanel = new javax.swing.JPanel();
controlPanel = new javax.swing.JPanel();
pauseButton = new javax.swing.JButton();
progressSlider = new javax.swing.JSlider();
progressLabel = new javax.swing.JLabel();
infoLabel = new javax.swing.JLabel();
javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel);
videoPanel.setLayout(videoPanelLayout);
videoPanelLayout.setHorizontalGroup(
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
);
videoPanelLayout.setVerticalGroup(
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 188, Short.MAX_VALUE)
);
org.openide.awt.Mnemonics.setLocalizedText(pauseButton, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.pauseButton.text")); // NOI18N
pauseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pauseButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N
javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel);
controlPanel.setLayout(controlPanelLayout);
controlPanelLayout.setHorizontalGroup(
controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(controlPanelLayout.createSequentialGroup()
.addComponent(pauseButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 357, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(progressLabel)
.addContainerGap())
.addGroup(controlPanelLayout.createSequentialGroup()
.addComponent(infoLabel)
.addGap(0, 0, Short.MAX_VALUE))
);
controlPanelLayout.setVerticalGroup(
controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(controlPanelLayout.createSequentialGroup()
.addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(pauseButton)
.addComponent(progressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(progressLabel, javax.swing.GroupLayout.Alignment.TRAILING))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(infoLabel))
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(videoPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
synchronized (playbinLock) {
State state = gstPlaybin2.getState();
if (state.equals(State.PLAYING)) {
if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
pauseButton.setText("");
// Is this call necessary considering we just called gstPlaybin2.pause()?
if (gstPlaybin2.setState(State.PAUSED) == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.PAUSED) failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
} else if (state.equals(State.PAUSED)) {
if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
pauseButton.setText("||");
// Is this call necessary considering we just called gstPlaybin2.play()?
if (gstPlaybin2.setState(State.PLAYING) == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.PLAYING) failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
} else if (state.equals(State.READY)) {
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
em.execute();
em.getExtractedBytes();
}
}
}//GEN-LAST:event_pauseButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel controlPanel;
private javax.swing.JLabel infoLabel;
private javax.swing.JButton pauseButton;
private javax.swing.JLabel progressLabel;
private javax.swing.JSlider progressSlider;
private javax.swing.JPanel videoPanel;
// End of variables declaration//GEN-END:variables
private class VideoProgressWorker extends SwingWorker<Object, Object> {
private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d ";
private long millisElapsed = 0;
private final long INTER_FRAME_PERIOD_MS = 20;
private final long END_TIME_MARGIN_MS = 50;
private boolean hadError = false;
private boolean isPlayBinReady() {
synchronized (playbinLock) {
return gstPlaybin2 != null && !gstPlaybin2.getState().equals(State.NULL);
}
}
private void resetVideo() throws Exception {
synchronized (playbinLock) {
if (gstPlaybin2 != null) {
if (gstPlaybin2.stop() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.stop() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
// ready to be played again
if (gstPlaybin2.setState(State.READY) == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.READY) failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
gstPlaybin2.getState(); //NEW
}
}
pauseButton.setText("");
progressSlider.setValue(0);
String durationStr = String.format(durationFormat, 0, 0, 0,
totalHours, totalMinutes, totalSeconds);
progressLabel.setText(durationStr);
}
/**
* @return true while millisElapsed is greater than END_TIME_MARGIN_MS
* from durationMillis. This is used to indicate when the video has
* ended because for some videos the time elapsed never becomes equal to
* the reported duration of the video.
*/
private boolean hasNotEnded() {
return (durationMillis - millisElapsed) > END_TIME_MARGIN_MS;
}
@Override
protected Object doInBackground() throws Exception {
// enable the slider
progressSlider.setEnabled(true);
int elapsedHours = -1, elapsedMinutes = -1, elapsedSeconds = -1;
ClockTime pos = null;
while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
synchronized (playbinLock) {
pos = gstPlaybin2.queryPosition();
}
millisElapsed = pos.toMillis();
// pick out the elapsed hours, minutes, seconds
long secondsElapsed = millisElapsed / 1000;
elapsedHours = (int) secondsElapsed / 3600;
secondsElapsed -= elapsedHours * 3600;
elapsedMinutes = (int) secondsElapsed / 60;
secondsElapsed -= elapsedMinutes * 60;
elapsedSeconds = (int) secondsElapsed;
String durationStr = String.format(durationFormat,
elapsedHours, elapsedMinutes, elapsedSeconds,
totalHours, totalMinutes, totalSeconds);
progressLabel.setText(durationStr);
autoTracking = true;
progressSlider.setValue((int) millisElapsed);
autoTracking = false;
try {
Thread.sleep(INTER_FRAME_PERIOD_MS);
} catch (InterruptedException ex) {
break;
}
}
// disable the slider
progressSlider.setEnabled(false);
resetVideo();
return null;
}
} //end class progress worker
/* Thread that extracts and plays a file */
private class ExtractMedia extends SwingWorker<Object, Void> {
private ProgressHandle progress;
boolean success = false;
private AbstractFile sFile;
private java.io.File jFile;
private String duration;
private String position;
private long extractedBytes;
ExtractMedia(org.sleuthkit.datamodel.AbstractFile sFile, java.io.File jFile) {
this.sFile = sFile;
this.jFile = jFile;
}
public long getExtractedBytes() {
return extractedBytes;
}
@Override
protected Object doInBackground() throws Exception {
success = false;
progress = ProgressHandleFactory.createHandle("Buffering " + sFile.getName(), new Cancellable() {
@Override
public boolean cancel() {
return ExtractMedia.this.cancel(true);
}
});
progressLabel.setText("Buffering... ");
progress.start();
progress.switchToDeterminate(100);
try {
extractedBytes = ContentUtils.writeToFile(sFile, jFile, progress, this, true);
} catch (IOException ex) {
logger.log(Level.WARNING, "Error buffering file", ex);
}
success = true;
return null;
}
/* clean up or start the worker threads */
@Override
protected void done() {
try {
super.get(); //block and get all exceptions thrown while doInBackground()
} catch (CancellationException ex) {
logger.log(Level.INFO, "Media buffering was canceled.");
} catch (InterruptedException ex) {
logger.log(Level.INFO, "Media buffering was interrupted.");
} catch (Exception ex) {
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex);
} finally {
progress.finish();
if (!this.isCancelled()) {
playMedia();
}
}
}
void playMedia() {
if (jFile == null || !jFile.exists()) {
progressLabel.setText("Error buffering file");
return;
}
ClockTime dur = null;
synchronized (playbinLock) {
// must play, then pause and get state to get duration.
if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
gstPlaybin2.getState();
dur = gstPlaybin2.queryDuration();
}
duration = dur.toString();
durationMillis = dur.toMillis();
// pick out the total hours, minutes, seconds
long durationSeconds = (int) durationMillis / 1000;
totalHours = (int) durationSeconds / 3600;
durationSeconds -= totalHours * 3600;
totalMinutes = (int) durationSeconds / 60;
durationSeconds -= totalMinutes * 60;
totalSeconds = (int) durationSeconds;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
progressSlider.setMaximum((int) durationMillis);
progressSlider.setMinimum(0);
synchronized (playbinLock) {
if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed.");
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
}
pauseButton.setText("||");
videoProgressWorker = new VideoProgressWorker();
videoProgressWorker.execute();
}
});
}
}
abstract void setupVideo(final AbstractFile file, final Dimension dims);
}

View File

@ -83,13 +83,22 @@ abstract class AbstractKeywordSearchPerformer extends javax.swing.JPanel impleme
@Override
public void search() {
boolean isRunning = IngestManager.getDefault().isModuleRunning(KeywordSearchIngestModule.getDefault());
if (filesIndexed == 0) {
KeywordSearchUtil.displayDialog("Keyword Search Error", "No files are indexed, please index an image before searching", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR);
if (isRunning) {
KeywordSearchUtil.displayDialog("Keyword Search Error", "<html>No files are in index yet. <br />"
+ "Try again later. Index is updated every " + KeywordSearchSettings.getUpdateFrequency().getTime() + " minutes.</html>", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR);
}
else {
KeywordSearchUtil.displayDialog("Keyword Search Error", "<html>No files were indexed.<br />"
+ "Re-ingest the image with the Keyword Search Module enabled. </html>", KeywordSearchUtil.DIALOG_MESSAGE_TYPE.ERROR);
}
return;
}
//check if keyword search module ingest is running (indexing, etc)
if (IngestManager.getDefault().isModuleRunning(KeywordSearchIngestModule.getDefault())) {
if (isRunning) {
if (KeywordSearchUtil.displayConfirmDialog("Keyword Search Ingest in Progress",
"<html>Keyword Search Ingest is currently running.<br />"
+ "Not all files have been indexed and this search might yield incomplete results.<br />"

View File

@ -43,7 +43,7 @@ import org.apache.solr.client.solrj.SolrServerException;
import org.sleuthkit.autopsy.casemodule.Case;
/**
* Keyword search toolbar which allows to search for single terms or phrases
* Keyword search toolbar (in upper right, by default) which allows to search for single terms or phrases
*
* The toolbar uses a different font from the rest of the application, Monospaced 14,
* due to the necessity to find a font that displays both Arabic and Asian fonts at an acceptable size.

1578
NEWS.txt

File diff suppressed because it is too large Load Diff

View File

@ -3,72 +3,51 @@
<!-- Need a way to specify TSK Debug versus Release -->
<property name="TSK_BUILD_TYPE">Release</property>
<target name="copyTSKLibsToRelease">
<property environment="env"/>
<condition property="ewfFound">
<isset property="env.LIBEWF_HOME"/>
</condition>
<fail unless="ewfFound" message="LIBEWF_HOME must be set as an environment variable."/>
<target name="build-installer-windows" depends="init-advanced-installer"
description="Makes an installer from the opened ZIP file">
<antcall target="run-advanced-installer" />
<!--<delete dir="${nbdist.dir}/${app.name}-installer"/>-->
<delete dir="${nbdist.dir}/installer_${app.name}-cache"/>
<move file="${nbdist.dir}/installer_${app.name}-SetupFiles/installer_${app.name}.msi" tofile="${nbdist.dir}/installer_${app.name}-${app.version}.msi" />
</target>
<copy file="${env.TSK_HOME}/win32/${TSK_BUILD_TYPE}/libtsk_jni.dll" tofile="${basedir}/Core/release/modules/lib/libtsk_jni.dll"/>
<copy file="${env.LIBEWF_HOME}/msvscpp/Release/libewf.dll" tofile="${basedir}/Core/release/modules/lib/libewf.dll"/>
<copy file="${env.LIBEWF_HOME}/msvscpp/Release/zlib.dll" tofile="${basedir}/Core/release/modules/lib/zlib.dll"/>
<target name="init-advanced-installer" depends="autoAIPath,inputAIPath"
description="Find AdvancedInstaller executable.">
<fail unless="ai-exe-path" message="Could not locate Advanced Installer."/>
<!-- Copy the template file to add details to -->
<copy file="${basedir}/installer_${app.name}/installer_${app.name}.aip" tofile="${nbdist.dir}/installer_${app.name}.aip" overwrite="true"/>
<property name="inst-path" value="${nbdist.dir}\${app.name}-installer"/>
<property name="aip-path" value="${nbdist.dir}\installer_${app.name}.aip"/>
<echo message="${ai-exe-path}" />
</target>
<target name="copyExternalLibsToZip">
<!-- Find CRT version we linked against from libtsk_jni manifest -->
<!-- disable auto-detection for CRT100
<property name="libtsk.manifest.path">${env.TSK_HOME}/win32/tsk_jni/${TSK_BUILD_TYPE}/libtsk_jni.dll.intermediate.manifest</property>
<loadfile property="libtsk.manifest" srcFile="${libtsk.manifest.path}" />
<propertyregex property="CRT.version"
input="${libtsk.manifest}"
regexp=".*Microsoft\.VC90.*?version\s*?=\s*?'(.*?)'"
select="\1"
casesensitive="false" />
<echo>Found CRT.version linked against: ${CRT.version}</echo>
-->
<!-- <property name="CRT.version">10.0.40219.1</property> -->
<!-- <property name="CRT.version">10.0.40219.325</property> -->
<property name="CRT.version">10.0.30319.1</property>
<!-- Get C++ Runtime dlls -->
<property environment="env"/>
<condition property="crtDetected">
<isset property="CRT.version"/>
</condition>
<fail unless="crtDetected" message="CRT version not detected, check libtsk_jni manifest."/>
<property name="CRT.path">${thirdparty.dir}/crt/x86-32/${CRT.version}/crt.zip</property>
<available file="${CRT.path}" property="crtFound"/>
<fail unless="crtFound" message="Detected CRT version ${CRT.version} not found in the thirdparty repo in path: ${CRT.path}"/>
<!-- unzip from thirdparty repo to modules/lib in staged dir -->
<!-- <unzip src="${CRT.path}" dest="${zip-tmp}/${app.name}/bin"/> -->
<unzip src="${CRT.path}" dest="${zip-tmp}/${app.name}/${app.name}/modules/lib"/>
<!-- delete 64 bit exe for now -->
<delete file="${zip-tmp}/${app.name}/bin/${app.name}64.exe" />
</target>
<target name="autoAIPath" >
<property name="AI.path">C:\Program Files (x86)\Caphyon\Advanced Installer 10.3\bin\x86\AdvancedInstaller.com</property>
<target name="autoAIPath" description="Attempt to find the AI path based on standard installation location">
<property name="AI.path" value="C:\Program Files (x86)\Caphyon\Advanced Installer 10.3\bin\x86\AdvancedInstaller.com" />
<available file="${AI.path}"
property="ai-exe-path"
value="${AI.path}"/>
<echo message="${ai-exe-path}" />
</target>
<target name="inputAIPath" unless="ai-exe-path">
<target name="inputAIPath" unless="ai-exe-path" description="Have the user input the path to the AI executable">
<input addProperty="ai-exe-path"
message="Enter the location of AdvancedInstaller.com"/>
</target>
<target name="run-advanced-installer" depends="autoAIPath,inputAIPath">
<fail unless="ai-exe-path" message="Could not locate Advanced Installer."/>
<!-- Copy the template file to add details to -->
<copy file="${basedir}/installer_${app.name}/installer_${app.name}.aip" tofile="${nbdist.dir}/installer_${app.name}.aip" overwrite="true"/>
<target name="run-advanced-installer" depends="add-ai-productinfo,add-ai-files,add-ai-shortcuts,add-ai-env">
<!-- Leaving this commented out bit for documentation purposes. Not sure what its supposed to do. -->
<!-- Need to find a way to deal with beta version -->
<!--<echo message="Setting ${app.name} version to ${app.version}..."/>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /SetVersion ${app.version}"/>
</exec>-->
<!--<delete file="${aip-path}"/>-->
</target>
<target name="add-ai-productinfo" description="Add product information to the aip file">
<scriptdef name="generateguid" language="javascript">
<attribute name="property" />
<![CDATA[
@ -78,8 +57,6 @@
]]>
</scriptdef>
<generateguid property="guid1" />
<property name="inst-path" value="${nbdist.dir}\${app.name}-installer"/>
<property name="aip-path" value="${nbdist.dir}\installer_${app.name}.aip"/>
<!-- automatically replace version name and productcode in the .aip file -->
<echo>Product Code: ${guid1}</echo>
<echo>Product Version: ${app.version}</echo>
@ -92,55 +69,36 @@
<replaceregexp file="${aip-path}"
match="ProductVersion&quot; Value=&quot;\d+\.{1}\d+\.{1}\d+"
replace="ProductVersion&quot; Value=&quot;${app.version}" />
<!-- Use Advanced Installer to configure files to add -->
<echo message="Adding files to installer..."/>
</target>
<target name="add-ai-files" description="Add the files in the installation path to the installer file">
<foreach target="add-file-or-dir" param="theFile" inheritall="true" inheritrefs="true">
<path>
<fileset dir="${inst-path}">
<include name="*" />
</fileset>
<dirset dir="${inst-path}">
<include name="*"/>
</dirset>
</path>
</foreach>
</target>
<target name="add-file-or-dir" depends="is-file-or-folder">
<echo message="${ai-exe-path}" />
<echo message="Adding ${file-or-folder} to installer: ${theFile}"/>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFolder APPDIR ${inst-path}\bin"/>
<arg line="/edit ${aip-path} /Add${file-or-folder} APPDIR ${theFile}" />
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFolder APPDIR ${inst-path}\etc"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFolder APPDIR ${inst-path}\gstreamer"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFolder APPDIR ${inst-path}\harness"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFolder APPDIR ${inst-path}\java"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFolder APPDIR ${inst-path}\jre"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFolder APPDIR ${inst-path}\platform"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFolder APPDIR ${inst-path}\${app.name}"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFile APPDIR ${inst-path}\icon.ico"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFile APPDIR ${inst-path}\KNOWN_ISSUES.txt"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFile APPDIR ${inst-path}\LICENSE-2.0.txt"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFile APPDIR ${inst-path}\NEWS.txt"/>
</exec>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /AddFile APPDIR ${inst-path}\README.txt"/>
</exec>
<!-- Need to find a way to deal with beta version -->
<!--<echo message="Setting ${app.name} version to ${app.version}..."/>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /SetVersion ${app.version}"/>
</exec>-->
</target>
<target name="is-file-or-folder">
<condition property="file-or-folder" value="File" else="Folder">
<available file="${theFile}" type="file" />
</condition>
</target>
<target name="add-ai-shortcuts" description="Add shortcuts to the aip file">
<echo message="Adding desktop/menu shortcuts..."/>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /NewShortcut -name ${app.title} -dir DesktopFolder -target APPDIR\bin\${app.name}.exe -icon ${inst-path}\icon.ico"/>
@ -148,7 +106,10 @@
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /NewShortcut -name ${app.title} -dir SHORTCUTDIR -target APPDIR\bin\${app.name}.exe -icon ${inst-path}\icon.ico"/>
</exec>
</target>
<!-- TODO: does this always need to be done, or just for 64 bit files? -->
<target name="add-ai-env" description="Add the enviornment variables to the aip file">
<echo message="Setting environment variables..."/>
<exec executable="${ai-exe-path}">
<arg line="/edit ${aip-path} /NewEnvironment -name GSTREAMER_PATH -value [APPDIR]gstreamer\bin -install_operation CreateUpdate -behavior Append -system_variable"/>
@ -162,14 +123,6 @@
<exec executable="${ai-exe-path}">
<arg line="/build ${aip-path}"/>
</exec>
<!--<delete file="${aip-path}"/>-->
</target>
<!-- Makes an installer from the opened ZIP file -->
<target name="build-installer-windows">
<antcall target="run-advanced-installer" />
<!--<delete dir="${nbdist.dir}/${app.name}-installer"/>-->
<delete dir="${nbdist.dir}/installer_${app.name}-cache"/>
<move file="${nbdist.dir}/installer_${app.name}-SetupFiles/installer_${app.name}.msi" tofile="${nbdist.dir}/installer_${app.name}-${app.version}.msi" />
</target>
</project>

View File

@ -54,10 +54,6 @@
</condition>
<fail unless="jreFound" message="JRE_HOME must be set as an environment variable."/>
</target>
<target name="getExternals" depends="findTSK">
<antcall target="copyTSKLibsToRelease" />
</target>
<target name="getJunit">
<property name="junit.dir" value="${thirdparty.dir}/junit/${netbeans-plat-version}" />
@ -87,9 +83,7 @@
<copy todir="${zip-tmp}/${app.name}/jre">
<fileset dir="${env.JRE_HOME}"/>
</copy>
<copy file="${basedir}/branding_${app.name}/icon.ico" tofile="${zip-tmp}/${app.name}/icon.ico" overwrite="true"/>
<antcall target="copyExternalLibsToZip"></antcall>
<property name="app.property.file" value="${zip-tmp}/${app.name}/etc/${app.name}.conf" />
<property name="jvm.options" value="&quot;--branding ${app.name} -J-Xms24m -J-Xmx512m -J-XX:MaxPermSize=128M -J-Xverify:none&quot;" />
@ -101,6 +95,18 @@
<!-- workaround for ant escaping : and = when setting properties -->
<replace file="${app.property.file}" token="@JVM_OPTIONS" value="${jvm.options}" />
<!-- We want to remove the dlls in autopsy/modules/lib because they will
shadow the files in the autopsy/modules/lib/ARCHITECTURE folder in the JAR.
These files are legacy from when we used to copy the dlls to this location.
This check should do away in the future. Added Sept '13-->
<delete failonerror="false">
<fileset dir="${zip-tmp}/${app.name}/autopsy/modules/lib">
<include name="libtsk_jni.dll" />
<include name="libewf.dll" />
<include name="zlib.dll" />
</fileset>
</delete>
<!-- step (4) zip again, but with the version numbers in the dir -->
<zip destfile="${nbdist.dir}/${app.name}-${app.version}.zip">
<zipfileset dir="${zip-tmp}/${app.name}"/>
@ -133,7 +139,7 @@
<property name="app.version" value="${DSTAMP}"/>
</target>
<target name="-init" depends="-taskdefs,-convert-old-project,getExternals,getProps,getJunit">
<target name="-init" depends="-taskdefs,-convert-old-project,getProps,getJunit">
<convertclusterpath from="${cluster.path.evaluated}" to="cluster.path.final" id="cluster.path.id"/>
<sortsuitemodules unsortedmodules="${modules}" sortedmodulesproperty="modules.sorted"/>
<property name="cluster" location="build/cluster"/>
@ -231,9 +237,8 @@
</exec>
</target>
<target name="versioning-script" depends="check-release"/> <!--, versioning-script-if-release, versioning-script-if-not-release"/>-->
<target name="versioning-script" depends="check-release, versioning-script-if-release, versioning-script-if-not-release"/>
<!-- opens the zip file into a folder in dist -->
<target name="build-installer-dir" depends="getProps, versioning-script, build-zip" description="Builds Autopsy with branding and all dependencies" >
<unzip src="${nbdist.dir}/${app.name}-${app.version}.zip" dest="${nbdist.dir}/${app.name}-installer"/>
</target>
@ -241,7 +246,6 @@
<target name="build-installer" depends="build-installer-dir" description="Builds Autopsy installer.">
<antcall target="build-installer-${os.family}" />
</target>
<target name="test-download-imgs" description="Get test images and store them in the path represented by the test-input variable.">
<available file="${test-input}/nps-2008-jean.E01" property="img-present-1"/>
<available file="${test-input}/nps-2008-jean.E02" property="img-present-2"/>

View File

@ -23,9 +23,9 @@
#
# http://wiki.sleuthkit.org/index.php?title=Autopsy_3_Module_Versions
#
# The basic idea is that this script uses javadoc/jdiff to
# compare the current state of the source code to the last
# tag and identifies if APIs were removed, added, etc.
# The basic idea is that this script uses javadoc/jdiff to
# compare the current state of the source code to the last
# tag and identifies if APIs were removed, added, etc.
#
# When run from the Autopsy build script, this script will:
# - Clone Autopsy and checkout to the previous release tag
@ -61,6 +61,12 @@ from shutil import move
from tempfile import mkstemp
from xml.dom.minidom import parse, parseString
# Jdiff return codes. Described in more detail further on
NO_CHANGES = 100
COMPATIBLE = 101
NON_COMPATIBLE = 102
ERROR = 1
# An Autopsy module object
class Module:
# Initialize it with a name, return code, and version numbers
@ -218,11 +224,11 @@ def compare_xml(module, apiname_tag, apiname_cur):
log.close()
code = jdiff.returncode
print("Compared XML for " + module.name)
if code == 100:
if code == NO_CHANGES:
print(" No API changes")
elif code == 101:
elif code == COMPATIBLE:
print(" API Changes are backwards compatible")
elif code == 102:
elif code == NON_COMPATIBLE:
print(" API Changes are not backwards compatible")
else:
print(" *Error in XML, most likely an empty module")
@ -564,18 +570,18 @@ def update_versions(modules, source):
if manifest == None or project == None:
print(" Error finding manifeset and project properties files")
return
if module.ret == 101:
if module.ret == COMPATIBLE:
versions = [versions[0].set(versions[0].increment()), versions[1] + 1, versions[2]]
set_specification(project, manifest, versions[0])
set_implementation(manifest, versions[1])
module.set_versions(versions)
elif module.ret == 102:
elif module.ret == NON_COMPATIBLE:
versions = [versions[0].set(versions[0].overflow()), versions[1] + 1, versions[2] + 1]
set_specification(project, manifest, versions[0])
set_implementation(manifest, versions[1])
set_release(manifest, versions[2])
module.set_versions(versions)
elif module.ret == 100:
elif module.ret == NO_CHANGES:
versions = [versions[0], versions[1] + 1, versions[2]]
set_implementation(manifest, versions[1])
module.set_versions(versions)
@ -624,48 +630,40 @@ def print_version_updates(modules):
f = open("gen_version.txt", "a")
for module in modules:
versions = module.versions
if module.ret == 101:
if module.ret == COMPATIBLE:
output = (module.name + ":\n")
output += (" Current Specification version:\t" + str(versions[0]) + "\n")
output += (" Updated Specification version:\t" + str(versions[0].increment()) + "\n")
output += ("\n")
output += (" Current Implementation version:\t" + str(versions[1]) + "\n")
output += (" Updated Implementation version:\t" + str(versions[1] + 1) + "\n")
output += ("\tSpecification:\t" + str(versions[0]) + "\t->\t" + str(versions[0].increment()) + "\n")
output += ("\tImplementation:\t" + str(versions[1]) + "\t->\t" + str(versions[1] + 1) + "\n")
output += ("\tRelease:\tNo Change.\n")
output += ("\n")
print(output)
sys.stdout.flush()
f.write(output)
elif module.ret == 102:
elif module.ret == NON_COMPATIBLE:
output = (module.name + ":\n")
output += (" Current Specification version:\t" + str(versions[0]) + "\n")
output += (" Updated Specification version:\t" + str(versions[0].overflow()) + "\n")
output += ("\n")
output += (" Current Implementation version:\t" + str(versions[1]) + "\n")
output += (" Updated Implementation version:\t" + str(versions[1] + 1) + "\n")
output += ("\n")
output += (" Current Release version:\t\t" + str(versions[2]) + "\n")
output += (" Updated Release version:\t\t" + str(versions[2] + 1) + "\n")
output += ("\tSpecification:\t" + str(versions[0]) + "\t->\t" + str(versions[0].overflow()) + "\n")
output += ("\tImplementation:\t" + str(versions[1]) + "\t->\t" + str(versions[1] + 1) + "\n")
output += ("\tRelease:\t" + str(versions[2]) + "\t->\t" + str(versions[2] + 1) + "\n")
output += ("\n")
print(output)
sys.stdout.flush()
f.write(output)
elif module.ret == 1:
elif module.ret == ERROR:
output = (module.name + ":\n")
output += (" *Unable to detect necessary changes\n")
output += (" Current Specification version:\t" + str(versions[0]) + "\n")
output += (" Current Implementation version:\t" + str(versions[1]) + "\n")
output += (" Current Release version:\t\t" + str(versions[2]) + "\n")
output += ("\t*Unable to detect necessary changes\n")
output += ("\tSpecification:\t" + str(versions[0]) + "\n")
output += ("\tImplementation:\t" + str(versions[1]) + "\n")
output += ("\tRelease:\t\t" + str(versions[2]) + "\n")
output += ("\n")
print(output)
f.write(output)
sys.stdout.flush()
elif module.ret == 100:
elif module.ret == NO_CHANGES:
output = (module.name + ":\n")
if versions[1] is None:
output += (" No Implementation version.\n")
output += ("\tImplementation: None\n")
else:
output += (" Current Implementation version:\t" + str(versions[1]) + "\n")
output += (" Updated Implementation version:\t" + str(versions[1] + 1) + "\n")
output += ("\tImplementation:\t" + str(versions[1]) + "\t->\t" + str(versions[1] + 1) + "\n")
output += ("\n")
print(output)
sys.stdout.flush()
@ -673,16 +671,13 @@ def print_version_updates(modules):
elif module.ret is None:
output = ("Added " + module.name + ":\n")
if module.spec() != "1.0" and module.spec() != "0.0":
output += (" Current Specification version:\t" + str(module.spec()) + "\n")
output += (" Updated Specification version:\t1.0\n")
output += ("\tSpecification:\t" + str(module.spec()) + "\t->\t" + "1.0\n")
output += ("\n")
if module.impl() != 1:
output += (" Current Implementation version:\t" + str(module.impl()) + "\n")
output += (" Updated Implementation version:\t1\n")
output += ("\tImplementation:\t" + str(module.impl()) + "\t->\t" + "1\n")
output += ("\n")
if module.release() != 1 and module.release() != 0:
output += (" Current Release version:\t\t" + str(module.release()) + "\n")
output += (" Updated Release version:\t\t1\n")
output += ("Release:\t\t" + str(module.release()) + "\t->\t" + "1\n")
output += ("\n")
print(output)
sys.stdout.flush()