--- /dev/null
+package kaka.cakelight.mode;
+
+import kaka.cakelight.Configuration;
+import kaka.cakelight.FrameGrabber;
+import kaka.cakelight.VideoDeviceListener;
+import kaka.cakelight.VideoFrame;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+public class VideoMode extends Mode {
+ private Configuration config;
+ private Thread grabberThread;
+ private Consumer<VideoFrame> frameConsumer;
+ private VideoDeviceListener deviceListener;
+
+ public VideoMode() {
+ deviceListener = new VideoDeviceListener();
+ deviceListener.onVideoDeviceChange(this::onVideoDeviceChange);
+ }
+
+ @Override
+ public void enter(Configuration config) {
+ this.config = config;
+ deviceListener.startListening();
+ }
+
+ @Override
+ public void exit() {
+ grabberThread.interrupt();
+ deviceListener.stopListening();
+ }
+
+ private void startGrabberThread(File videoDevice) {
+ assert frameConsumer != null;
+ grabberThread = new Thread() {
+ public void run() {
+ try (FrameGrabber grabber = FrameGrabber.from(videoDevice, config)) {
+ while (!isInterrupted()) {
+ Optional<VideoFrame> frame = grabber.grabFrame();
+ if (frameConsumer != null) frame.ifPresent(frameConsumer);
+ frame.ifPresent(VideoMode.this::onVideoFrame);
+// timeIt("frame", grabber::grabFrame);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ };
+ grabberThread.start();
+ }
+
+ public void onVideoFrame(Consumer<VideoFrame> consumer) {
+ frameConsumer = consumer;
+ }
+
+ private void onVideoFrame(VideoFrame frame) {
+ updateWithFrame(frame.getLedFrame());
+ }
+
+ public void onVideoDeviceChange(Optional<File> videoDevice) {
+ // Should only happen when this mode is active!
+ if (grabberThread != null) {
+ grabberThread.interrupt();
+ }
+ videoDevice.ifPresent(this::startGrabberThread);
+ }
+}