Autopsy  4.21.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
JavaFxAppSink.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.contentviewers;
20 
21 import java.nio.ByteBuffer;
22 import java.nio.ByteOrder;
23 import javafx.application.Platform;
24 import javafx.embed.swing.JFXPanel;
25 import javafx.scene.Scene;
26 import javafx.scene.image.Image;
27 import javafx.scene.image.ImageView;
28 import javafx.scene.image.PixelFormat;
29 import javafx.scene.image.PixelWriter;
30 import javafx.scene.image.WritableImage;
31 import javafx.scene.layout.BorderPane;
32 import org.freedesktop.gstreamer.Buffer;
33 import org.freedesktop.gstreamer.Caps;
34 import org.freedesktop.gstreamer.FlowReturn;
35 import org.freedesktop.gstreamer.Sample;
36 import org.freedesktop.gstreamer.Structure;
37 import org.freedesktop.gstreamer.elements.AppSink;
38 
42 final class JavaFxAppSink extends AppSink {
43 
44  private static final String CAP_MIME_TYPE = "video/x-raw";
45  private static final String CAP_BYTE_ORDER = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? "format=BGRx" : "format=xRGB");
46  private static final int PROP_MAX_BUFFERS = 5000;
47 
48  private final JavaFxFrameUpdater updater;
49 
57  public JavaFxAppSink(String name, JFXPanel target) {
58  super(name);
59  set("emit-signals", true);
60  updater = new JavaFxFrameUpdater(target);
61  connect((AppSink.NEW_SAMPLE) updater);
62  connect((AppSink.NEW_PREROLL) updater);
63  setCaps(new Caps(
64  String.format("%s, %s", CAP_MIME_TYPE, CAP_BYTE_ORDER)));
65  set("max-buffers", PROP_MAX_BUFFERS);
66  set("drop", true);
67  }
68 
72  public void clear() {
73  disconnect((AppSink.NEW_SAMPLE) updater);
74  disconnect((AppSink.NEW_PREROLL) updater);
75  updater.clear();
76  }
77 
82  static class JavaFxFrameUpdater implements AppSink.NEW_SAMPLE, AppSink.NEW_PREROLL {
83  private final ImageView fxImageView;
84 
85  public JavaFxFrameUpdater(JFXPanel target) {
86  //We should probably pass an ImageView instead of a JFXPanel to make
87  //it more reuseable
88  fxImageView = new ImageView(); // Will hold the current video frame.
89  BorderPane borderpane = new BorderPane(fxImageView); // Center and size ImageView.
90  Scene scene = new Scene(borderpane); // Root of the JavaFX tree.
91  // Although the documentation for JFXPanel.setScene() claims that it
92  // can be called on either the EDT or the JavaFX thread, with JavaFX 11
93  // it doesn't work unless you call it on the JavaFX application thread.
94  Platform.runLater(() -> {
95  target.setScene(scene);
96  });
97 
98  // Bind size of image to that of scene, while keeping proportions
99  fxImageView.fitWidthProperty().bind(scene.widthProperty());
100  fxImageView.fitHeightProperty().bind(scene.heightProperty());
101  fxImageView.setPreserveRatio(true);
102  fxImageView.setSmooth(true);
103  fxImageView.setCache(true);
104  }
105 
112  @Override
113  public FlowReturn newSample(AppSink appSink) {
114  return setSample(appSink.pullSample());
115  }
116 
124  public FlowReturn setSample(Sample input) {
125  Buffer buffer = input.getBuffer();
126  ByteBuffer byteBuffer = buffer.map(false);
127  if (byteBuffer != null) {
128  Structure capsStruct = input.getCaps().getStructure(0);
129  int width = capsStruct.getInteger("width");
130  int height = capsStruct.getInteger("height");
131  byte[] byteArray = new byte[width * height * 4];
132  byteBuffer.get(byteArray);
133  Image videoFrame = convertBytesToImage(byteArray, width, height);
134  fxImageView.setImage(videoFrame);
135  buffer.unmap();
136  }
137  input.dispose();
138 
139  //Keep frames rolling
140  return FlowReturn.OK;
141  }
142 
154  @Override
155  public FlowReturn newPreroll(AppSink sink) {
156  //Grab the next frame without removing it from the pipeline
157  Sample sample = sink.pullPreroll();
158  return setSample(sample);
159  }
160 
170  private Image convertBytesToImage(byte[] pixels, int width, int height) {
171  WritableImage image = new WritableImage(width, height);
172  PixelWriter pixelWriter = image.getPixelWriter();
173  pixelWriter.setPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), pixels, 0, width * 4);
174  return image;
175  }
176 
180  void clear() {
181  fxImageView.setImage(null);
182  }
183  }
184 }

Copyright © 2012-2022 Basis Technology. Generated on: Tue Feb 6 2024
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.