diff --git a/README.md b/README.md new file mode 100644 index 0000000..041977a --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +AdvancedCameraSettings_r3 +========================= + +This program is for webcams to persistent the config + +Usage +----- + +1. java -jar AdvancedCameraSettings.jar +2. Config all the webcams +3. Save the config, so that config.ini is created +4. during windows startup, run the loadconfig.bat (you can do a file shortcut at C:\Trakomatic\Cron\@reboot) + + +Credits +------- + +(Codes from https://obsproject.com/forum/threads/using-2-or-more-logitech-c920s-or-c930es-together-successfully.26707/ , +edited by Cxrus) \ No newline at end of file diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..8d770e1 --- /dev/null +++ b/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project AdvancedCameraSettings. + + + diff --git a/lib/dsj.dll b/lib/dsj.dll new file mode 100644 index 0000000..c0beae7 Binary files /dev/null and b/lib/dsj.dll differ diff --git a/lib/dsj.jar b/lib/dsj.jar new file mode 100644 index 0000000..e344114 Binary files /dev/null and b/lib/dsj.jar differ diff --git a/src/AdvancedCameraSettingsMain.java b/src/AdvancedCameraSettingsMain.java new file mode 100644 index 0000000..25e7ef9 --- /dev/null +++ b/src/AdvancedCameraSettingsMain.java @@ -0,0 +1,124 @@ +import com.digitalmodular.utilities.ConfigManager; +import de.humatic.dsj.CaptureDeviceControls; +import java.util.ArrayList; + +import util.CameraFormatSelectionDialog; +import util.CameraSelectionDialog; +import util.DirectShowCamera; +import de.humatic.dsj.DSFilterInfo; +import de.humatic.dsj.DSMediaType; +import java.util.Map; + +/** + * @author Mark Jeronimus + */ +// date 2014/08/05 +public class AdvancedCameraSettingsMain { + public static void main(String[] args) throws Exception { + if(args.length > 0 && args[0].equals("--load") ) { + loadCamerasSettings(); + } else { + DirectShowCamera[] cameras = queryCameras(); + + if (cameras.length == 0) + System.exit(0); + + new MultiCameraTestMain(cameras); + } + } + + private static DirectShowCamera[] queryCameras() { + DSFilterInfo[] cameraInfos = DirectShowCamera.getCameras(); + ArrayList camerasToExclude = new ArrayList(); + ArrayList cameras = new ArrayList(); + DSFilterInfo cameraInfo = null; + int i = 1; + + do { + CameraSelectionDialog cd = new CameraSelectionDialog("Select camera " + i, cameraInfos, camerasToExclude); + cameraInfo = cd.getSelectedCamera(); + if (cameraInfo == null) + break; + + CameraFormatSelectionDialog cfd = new CameraFormatSelectionDialog("Select format for camera " + i, + DirectShowCamera.getFormats(cameraInfo)); + DSMediaType format = cfd.getSelectedFormat(); + if (format == null) + break; + + camerasToExclude.add(cameraInfo); + + DirectShowCamera camera = new DirectShowCamera(cameraInfo); + camera.selectFormat(format); + cameras.add(camera); + i++; + } while (cameraInfo != null); + + return cameras.toArray(new DirectShowCamera[cameras.size()]); + } + + public AdvancedCameraSettingsMain() { + + } + + + private static final int[][] propertyKeys = { + {CaptureDeviceControls.EXPOSURE, CaptureDeviceControls.GAIN, CaptureDeviceControls.BRIGHTNESS, + CaptureDeviceControls.CONTRAST, CaptureDeviceControls.SATURATION, CaptureDeviceControls.HUE, + CaptureDeviceControls.COLORENABLE, CaptureDeviceControls.WHITEBALANCE, CaptureDeviceControls.GAMMA}, + {CaptureDeviceControls.BACKLIGHTCOMPENSATION, CaptureDeviceControls.SHARPNESS, CaptureDeviceControls.INPUT_LEVEL, + CaptureDeviceControls.INPUT_SELECT}, + {CaptureDeviceControls.FOCUS, CaptureDeviceControls.IRIS, CaptureDeviceControls.ZOOM, CaptureDeviceControls.PAN, + CaptureDeviceControls.TILT, CaptureDeviceControls.ROLL}, + {CaptureDeviceControls.MASTER_VOL, CaptureDeviceControls.MASTER_PAN, CaptureDeviceControls.TREBLE, + CaptureDeviceControls.BASS, CaptureDeviceControls.BALANCE}, + {CaptureDeviceControls.CAMCONTROL_ABSOLUTE, CaptureDeviceControls.CAMCONTROL_AUTO, + CaptureDeviceControls.CAMCONTROL_MANUAL, CaptureDeviceControls.CAMCONTROL_RELATIVE}, + {CaptureDeviceControls.VC_FLIP_HOR, CaptureDeviceControls.VC_FLIP_VER, CaptureDeviceControls.VC_TRIGGER, + CaptureDeviceControls.VC_TRIGGER_ENABLE}, + {CaptureDeviceControls.LT_DIGITAL_PAN, CaptureDeviceControls.LT_DIGITAL_PANTILTZOOM, + CaptureDeviceControls.LT_DIGITAL_TILT, CaptureDeviceControls.LT_DIGITAL_ZOOM, + CaptureDeviceControls.LT_EXPOSURE_TIME, CaptureDeviceControls.LT_FACE_TRACKING, CaptureDeviceControls.LT_FINDFACE, + CaptureDeviceControls.LT_LED} }; + + private static void loadCamerasSettings() { + DSFilterInfo[] cameraInfos = DirectShowCamera.getCameras(); + ConfigManager.revert(); + Map data = ConfigManager.getAllData(); + + for(DSFilterInfo cameraInfo : cameraInfos) { + DirectShowCamera camera = new DirectShowCamera(cameraInfo); + + try { + camera.start(); + for (int j = 0; j < propertyKeys.length; j++) { + for (int i = 0; i < propertyKeys[j].length; i++) { + + try { + String value = data.get(camera.getPath() + "?" + propertyKeys[j][i]); + if(value != null) { + System.out.println("Set "+camera.getName()+" value "+propertyKeys[j][i]+" to "+value); + camera.setParameterValue(propertyKeys[j][i], Integer.parseInt(value)); + } + } + catch(Exception e) {} + + try { + String value = data.get(camera.getPath() + "&" + propertyKeys[j][i]); + if(value != null) { + System.out.println("Set "+camera.getName()+" auto "+propertyKeys[j][i]+" to "+value); + camera.setAuto(propertyKeys[j][i], Boolean.parseBoolean(value)); + } + } + catch(Exception e) {} + } + } + camera.stop(); + } + catch(Exception e) { + System.out.println(e); + } + } + + } +} diff --git a/src/MultiCameraTestMain.java b/src/MultiCameraTestMain.java new file mode 100644 index 0000000..d3e80fc --- /dev/null +++ b/src/MultiCameraTestMain.java @@ -0,0 +1,97 @@ +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; + +import javax.swing.JFrame; +import javax.swing.Timer; + +import util.CameraSettingsPanel; +import util.DirectShowCamera; +import util.FrameListener; + +import com.digitalmodular.utilities.swing.ImagePanel; + +/** + * @author Mark Jeronimus + * @version 1.0 + * @since 1.0 + * @date 2012/04/03 + */ +public class MultiCameraTestMain extends JFrame implements FrameListener { + private final DirectShowCamera[] cameras; + private ImagePanel[] images; + + public MultiCameraTestMain(final DirectShowCamera[] cameras) throws Exception { + super("Dual camera test"); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + this.cameras = cameras; + images = new ImagePanel[cameras.length]; + + for (int i = 0; i < cameras.length; i++) { + DirectShowCamera camera = cameras[i]; + + images[i] = new ImagePanel(true); + + camera.addFrameListener(this); + camera.start(); + } + + { + Timer t = new Timer(1000, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + System.out.println("initializeComponents"); + initializeComponents(); + setSize(400 * cameras.length, 600); + setLocationRelativeTo(null); + setVisible(true); + } + }); + t.setRepeats(false); + t.start(); + } + } + + void initializeComponents() { + setLayout(new GridLayout(2, cameras.length)); + + for (DirectShowCamera camera : cameras) + add(new CameraSettingsPanel(camera)); + + for (int i = 0; i < cameras.length; i++) { + add(images[i]); + } + } + + @Override + public synchronized void newFrame(Object source, byte[] data) { + // Catch exceptions because somewhere up the call tree it is silently + // caught and not reported. + try { + int index = -1; + for (int i = 0; i < cameras.length; i++) { + if (cameras[i] == source) { + index = i; + break; + } + } + + int width = cameras[index].getFormat().getWidth(); + int height = cameras[index].getFormat().getHeight(); + + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); + byte[] array = ((DataBufferByte)image.getRaster().getDataBuffer()).getData(); + System.arraycopy(data, 0, array, 0, array.length); + + images[index].setImage(image); + + repaint(); + } + catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/com/digitalmodular/utilities/ConfigManager.java b/src/com/digitalmodular/utilities/ConfigManager.java new file mode 100644 index 0000000..bee622a --- /dev/null +++ b/src/com/digitalmodular/utilities/ConfigManager.java @@ -0,0 +1,163 @@ +// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. +// Jad home page: http://www.kpdus.com/jad.html +// Decompiler options: packimports(3) +// Source File Name: ConfigManager.java + +package com.digitalmodular.utilities; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +public class ConfigManager { + private static final String filename = "config.ini"; + private static TreeMap data = new TreeMap(); + + static { + revert(); + } + + public static void revert() { + data.clear(); + + try { + BufferedReader in = new BufferedReader(new FileReader(ConfigManager.filename)); + String s; + while ((s = in.readLine()) != null) { + int split = s.indexOf(' '); + String key = s.substring(0, split++); + String value = s.substring(split); + ConfigManager.data.put(key, value); + } + in.close(); + } + catch (FileNotFoundException filenotfoundexception) {} + catch (IOException e) { + e.printStackTrace(); + } + } + + public static void save() { + try { + BufferedWriter out = new BufferedWriter(new FileWriter(ConfigManager.filename)); + Set keys = ConfigManager.data.keySet(); + for (String key : keys) { + out.write(new StringBuilder(key).append(" ").append(ConfigManager.data.get(key)).append("\n").toString()); + } + out.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + public static void setValue(String key, String value) { + ConfigManager.data.put(key, value); + } + + public static void setIntValue(String key, int value) { + ConfigManager.data.put(key, Integer.toString(value)); + } + + public static void setLongValue(String key, long value) { + ConfigManager.data.put(key, Long.toString(value)); + } + + public static void setFloatValue(String key, float value) { + ConfigManager.data.put(key, Float.toString(value)); + } + + public static void setDoubleValue(String key, double value) { + ConfigManager.data.put(key, Double.toString(value)); + } + + public static void setBoolValue(String key, boolean value) { + ConfigManager.data.put(key, Boolean.toString(value)); + } + + public static String getValue(String key, String defaultValue) { + String value = ConfigManager.data.get(key); + if (value == null) { + ConfigManager.setValue(key, defaultValue); + return defaultValue; + } + return value; + } + + public static int getIntValue(String key, int defaultValue) { + String value = ConfigManager.data.get(key); + if (value == null) { + ConfigManager.setIntValue(key, defaultValue); + return defaultValue; + } + try { + return Integer.parseInt(value); + } + catch (NumberFormatException e) { + return defaultValue; + } + } + + public static long getLongValue(String key, long defaultValue) { + String value = ConfigManager.data.get(key); + if (value == null) { + ConfigManager.setLongValue(key, defaultValue); + return defaultValue; + } + try { + return Long.parseLong(value); + } + catch (NumberFormatException e) { + return defaultValue; + } + } + + public static float getFloatValue(String key, float defaultValue) { + String value = ConfigManager.data.get(key); + if (value == null) { + ConfigManager.setFloatValue(key, defaultValue); + return defaultValue; + } + try { + return Float.parseFloat(value); + } + catch (NumberFormatException e) { + return defaultValue; + } + } + + public static double getDoubleValue(String key, double defaultValue) { + String value = ConfigManager.data.get(key); + if (value == null) { + ConfigManager.setDoubleValue(key, defaultValue); + return defaultValue; + } + try { + return Double.parseDouble(value); + } + catch (NumberFormatException e) { + return defaultValue; + } + } + + public static boolean getBoolValue(String key, boolean defaultValue) { + String value = ConfigManager.data.get(key); + if (value == null) { + ConfigManager.setBoolValue(key, defaultValue); + return defaultValue; + } + return Boolean.parseBoolean(value); + } + + public static Map getAllData() { + Map allData = new TreeMap(); + allData.putAll(data); + return allData; + } +} diff --git a/src/com/digitalmodular/utilities/swing/ImagePanel.java b/src/com/digitalmodular/utilities/swing/ImagePanel.java new file mode 100644 index 0000000..b3ff17e --- /dev/null +++ b/src/com/digitalmodular/utilities/swing/ImagePanel.java @@ -0,0 +1,113 @@ +package com.digitalmodular.utilities.swing; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Insets; +import java.awt.image.BufferedImage; + +import javax.swing.JComponent; +import javax.swing.border.Border; + +/** + * @author Mark Jeronimus + */ +// date 2011-06-20 +public class ImagePanel extends JComponent +{ + public Image image; + public boolean stretch; + + public ImagePanel() + { + this(false); + } + + public ImagePanel(boolean stretch) + { + this.stretch = stretch; + } + + public ImagePanel(BufferedImage image) + { + setImage(image); + } + + public void setImage(Image im) + { + image = im; + sizeChanged(); + } + + public Image getImage() + { + return image; + } + + private void sizeChanged() + { + if (stretch) + return; + + Dimension size = new Dimension(); + + if (image != null) + { + size.width = image.getWidth(null); + size.height = image.getHeight(null); + } + + Border border = getBorder(); + if (border != null) + { + Insets insets = border.getBorderInsets(this); + + size.width += insets.left + insets.right; + size.height += insets.top + insets.bottom; + } + + setPreferredSize(size); + setSize(size); + } + + @Override + public void setBorder(Border border) + { + super.setBorder(border); + sizeChanged(); + } + + @Override + public void paintComponent(Graphics g) + { + // super.paintComponent(g); + + if (image != null) + { + int x = 0; + int y = 0; + int width = getWidth(); + int height = getHeight(); + + Border border = getBorder(); + if (border != null) + { + Insets insets = border.getBorderInsets(this); + + x = insets.left; + y = insets.top; + width -= insets.right + x; + height -= insets.right + x; + } + + if (stretch) + { + g.drawImage(image, x, y, width, height, this); + } + else + { + g.drawImage(image, x, y, null); + } + } + } +} diff --git a/src/com/digitalmodular/utilities/swing/TableLayout.java b/src/com/digitalmodular/utilities/swing/TableLayout.java new file mode 100644 index 0000000..0bba4b5 --- /dev/null +++ b/src/com/digitalmodular/utilities/swing/TableLayout.java @@ -0,0 +1,324 @@ +package com.digitalmodular.utilities.swing; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.LayoutManager; +import java.io.Serializable; +import java.util.Arrays; + +/** + * A Layout Manager which creates a basic table of components. The widths of the cells are specified as a ratio array of int. The + * relative widths of the rendered columns will always follow these ratios.
+ * + * The preferred widths of the columns will be either the sum of the columns with configured widths as pixels, or the configured ratios + * stretched equally in such a way that no single component is narrower than it's own preferred width, whichever is larger, added to the sum of + * the gap widths. When the width of the parent container is larger, the cells are simply stretched linearly according to the configured ratios. + * + * A special case is when a ratio is specified as zero. In these cases, each column specified as zero will get such a preferred width that no + * single component inside that column is narrower than it's own preferred width. These columns do stretch, however, when the width of the + * parent container is larger than the preferred width, everything is stretched linearly. + * + * For example, a TableLayout with column widths {5, 2} filled with equal components with a width of 100 pixels will + * generate a layout with preferred column widths of 250 and 100 pixels (ratio 5:2). Setting this layout to a container with an absolute width + * of 700 pixels will force the table columns to 500 and 200 pixels wide.
+ * + * Another example. A TableLayout with column widths {0, 200, 0, 120} filled with different components for each column + * with relative widths of 30, 100, 50 and 100 pixels will generate a layout with preferred column widths of 30, 200, 50 and 120 pixels (ratio + * x:200:x:120). Setting this layout to a container with an absolute width of 800 pixels will force the table columns to 60, 400, 100 and 240 + * pixels wide. + * + * @author Mark Jeronimus + * @see LayoutManager + * @see Component#getPreferredSize() date 2006/11/24 + */ +public class TableLayout implements LayoutManager, Serializable +{ + private int hgap; + private int vgap; + private int[] columns; + private int[] colWidths; + + private int numColumns; + private int[] rowHeights; + private int widestColumn; + + /** + * Create a TableLayout with specified column width ratios and gap sizes. + * + * @param hgap + * the horizontal space between all columns, in pixels + * @param vgap + * the vertical space between all rows, in pixels + * @param columns + * the column-width ratios + * @throws IllegalArgumentException + * when the array is null, the array length is zero or one of the array elements is negative + */ + public TableLayout(int hgap, int vgap, int... columns) + { + setColumns(columns); + setHgap(hgap); + setVgap(vgap); + } + + /** + * Set or change the column width ratios. + * + * @param columns + * the column width ratios + * @throws IllegalArgumentException + * when the array is null, the array length is zero or one of the array elements is negative + */ + public void setColumns(int... columns) + { + if (columns == null || columns.length == 0) + throw new IllegalArgumentException("No columns specified"); + this.columns = columns; + numColumns = this.columns.length; + + int widestColumnSize = -1; + widestColumn = 0; + for (int i = 0; i < numColumns; i++) + { + int w = this.columns[i]; + if (w < 0) + throw new IllegalArgumentException("Negative column width specified at index " + i); + + if (w > 0 && widestColumnSize < w) + { + widestColumnSize = w; + widestColumn = i; + } + } + + colWidths = new int[this.columns.length]; + } + + /** + * Returns the column width ratios. The returned array is a direct reference to the contained column array, so care must be taken not to + * alter any elements to illegal values (negative values). + * + * @return the column width ratios + */ + public int[] getColumnns() + { + return columns; + } + + /** + * Set or change the horizontal space between all columns, in pixels + * + * @param hgap + * the horizontal space between all columns, in pixels + */ + public void setHgap(int hgap) + { + if (hgap < 0) + throw new IllegalArgumentException("Negative hgap specified"); + this.hgap = hgap; + } + + /** + * Get the horizontal space between all columns, in pixels + * + * @return the horizontal space between all columns, in pixels + */ + public int getHgap() + { + return hgap; + } + + /** + * Set or change the vertical space between all rows, in pixels + * + * @param vgap + * the vertical space between all rows, in pixels + */ + public void setVgap(int vgap) + { + if (vgap < 0) + throw new IllegalArgumentException("Negative vgap specified"); + this.vgap = vgap; + } + + /** + * Get the vertical space between all rows, in pixels + * + * @return the vertical space between all rows, in pixels + */ + public int getVgap() + { + return vgap; + } + + /** + * {@inheritDoc} + */ + @Override + public void addLayoutComponent(String name, Component comp) + {} + + /** + * {@inheritDoc} + */ + @Override + public void removeLayoutComponent(Component comp) + {} + + /** + * {@inheritDoc} + */ + @Override + public Dimension preferredLayoutSize(Container parent) + { + synchronized (parent.getTreeLock()) + { + Insets insets = parent.getInsets(); + int components = parent.getComponentCount(); + int numRows = (components + numColumns - 1) / numColumns; + + rowHeights = new int[numRows]; + Arrays.fill(rowHeights, 0); + + int narrowestColumn = widestColumn; + int narrowestSize = columns[widestColumn]; + + int component = 0; + for (int y = 0; y < numRows; y++) + { + for (int x = 0; x < numColumns; x++) + { + Component comp = parent.getComponent(component++); + Dimension d = comp.getPreferredSize(); + + if (columns[x] == 0) + { + if (colWidths[x] < d.width) + colWidths[x] = d.width; + } + else if (d.width > columns[x] * narrowestSize / columns[narrowestColumn]) + { + if (columns[x] > 0) + narrowestColumn = x; + narrowestSize = d.width; + } + + if (rowHeights[y] < d.height) + rowHeights[y] = d.height; + + if (component == components) + break; + } + } + int totalWidth = insets.left + insets.right + (numColumns - 1) * hgap; + for (int x = 0; x < numColumns; x++) + { + if (columns[x] != 0) + colWidths[x] = columns[x] * narrowestSize / columns[narrowestColumn]; + + totalWidth += colWidths[x]; + } + int totalHeight = insets.top + insets.bottom + (numRows - 1) * vgap; + for (int y = 0; y < numRows; y++) + totalHeight += rowHeights[y]; + return new Dimension(totalWidth, totalHeight); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Dimension minimumLayoutSize(Container parent) + { + synchronized (parent.getTreeLock()) + { + Insets insets = parent.getInsets(); + int components = parent.getComponentCount(); + int numRows = (components + numColumns - 1) / numColumns; + + int[] colWidths = new int[numColumns]; + System.arraycopy(columns, 0, colWidths, 0, numColumns); + int[] rowHeights = new int[numRows]; + Arrays.fill(rowHeights, 0); + + int component = 0; + for (int y = 0; y < numRows; y++) + { + for (int x = 0; x < numColumns; x++) + { + Component comp = parent.getComponent(component++); + Dimension d = comp.getMinimumSize(); + if (colWidths[x] < d.width) + colWidths[x] = d.width; + if (rowHeights[y] < d.height) + rowHeights[y] = d.height; + if (component == components) + break; + } + } + int totalWidth = insets.left + insets.right + (numColumns - 1) * hgap; + for (int x = 0; x < numColumns; x++) + totalWidth += colWidths[x]; + int totalHeight = insets.top + insets.bottom + (numRows - 1) * vgap; + for (int y = 0; y < numRows; y++) + totalHeight += rowHeights[y]; + return new Dimension(totalWidth, totalHeight); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void layoutContainer(Container parent) + { + synchronized (parent.getTreeLock()) + { + Insets insets = parent.getInsets(); + int components = parent.getComponentCount(); + + int numRows = (components + numColumns - 1) / numColumns; + + Dimension preferredSize = preferredLayoutSize(parent); + int width = parent.getWidth() - (insets.left + insets.right + (numColumns - 1) * hgap); + int height = parent.getHeight() - (insets.top + insets.bottom + (numRows - 1) * vgap); + int preferredWidth = preferredSize.width - (insets.left + insets.right + (numColumns - 1) * hgap); + int preferredHeight = preferredSize.height - (insets.top + insets.bottom + (numRows - 1) * vgap); + + int[] currentColWidths = new int[numColumns]; + for (int x = 0; x < numColumns; x++) + currentColWidths[x] = colWidths[x] * width / preferredWidth; + int[] currentRowHeights = new int[numRows]; + for (int y = 0; y < numRows; y++) + currentRowHeights[y] = rowHeights[y] * height / preferredHeight; + + int component = 0; + int yPos = insets.top; + for (int y = 0; y < numRows; y++) + { + int xPos = insets.left; + for (int x = 0; x < numColumns; x++) + { + parent.getComponent(component++).setBounds(xPos, yPos, currentColWidths[x], currentRowHeights[y]); + if (component == components) + break; + + xPos += currentColWidths[x] + hgap; + } + yPos += currentRowHeights[y] + vgap; + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() + { + return super.getClass().getSimpleName() + "[columns=" + columns + ", hgap=" + hgap + ", vgap=" + vgap + "]"; + } +} diff --git a/src/com/digitalmodular/utilities/swing/table/MutableTableModel.java b/src/com/digitalmodular/utilities/swing/table/MutableTableModel.java new file mode 100644 index 0000000..84900a4 --- /dev/null +++ b/src/com/digitalmodular/utilities/swing/table/MutableTableModel.java @@ -0,0 +1,251 @@ +package com.digitalmodular.utilities.swing.table; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import javax.swing.table.AbstractTableModel; + +/** + * @author Mark Jeronimus + */ +// date 2005-08-07 +public class MutableTableModel extends AbstractTableModel implements List { + private ArrayList tableData = new ArrayList(); + private String[] tableColumnNames; + private boolean[] editable; + + public MutableTableModel(String[] tableColumnNames) { + super(); + + tableData = new ArrayList(); + this.tableColumnNames = tableColumnNames; + editable = new boolean[tableColumnNames.length]; + Arrays.fill(editable, false); + super.fireTableRowsInserted(0, tableData.size() - 1); + } + + public MutableTableModel(String[] tableColumnNames, boolean[] editable) { + super(); + + tableData = new ArrayList(); + this.tableColumnNames = tableColumnNames; + this.editable = editable; + super.fireTableRowsInserted(0, tableData.size() - 1); + } + + public MutableTableModel(ArrayList tableData, String[] tableColumnNames, boolean[] editable) { + super(); + + this.tableData = tableData; + this.tableColumnNames = tableColumnNames; + this.editable = editable; + super.fireTableRowsInserted(0, tableData.size() - 1); + } + + @Override + public boolean add(Object[] elements) { + int i = size(); + tableData.add(elements); + super.fireTableRowsInserted(i, i); + return true; + } + + @Override + public void add(int index, Object[] element) { + tableData.add(index, element); + super.fireTableRowsInserted(index, index); + } + + @Override + public boolean addAll(Collection c) { + int len = c.size(); + if (len != 0) { + int oldSize = tableData.size(); + tableData.addAll(c); + super.fireTableRowsInserted(oldSize, oldSize + len - 1); + return true; + } + return false; + } + + @Override + public boolean addAll(int index, Collection c) { + int len = c.size(); + if (len != 0) { + tableData.addAll(index, c); + super.fireTableRowsInserted(index, index + len - 1); + return true; + } + return false; + } + + @Override + public Object[] set(int index, Object[] element) { + Object[] out = tableData.set(index, element); + super.fireTableRowsUpdated(index, index); + return out; + } + + @Override + public boolean remove(Object o) { + int index = tableData.indexOf(o); + if (index == -1) { + return false; + } + tableData.remove(index); + super.fireTableRowsDeleted(index, index); + return true; + } + + @Override + public Object[] remove(int index) { + Object[] out = tableData.remove(index); + super.fireTableRowsDeleted(index, index); + return out; + } + + @Override + public boolean removeAll(Collection c) { + boolean modified = false; + for (int i = tableData.size() - 1; i >= 0; i--) { + if (c.contains(tableData.get(i))) { + tableData.remove(i); + super.fireTableRowsDeleted(i, i); + modified = true; + } + } + return modified; + } + + @Override + public boolean retainAll(Collection c) { + boolean modified = false; + for (int i = tableData.size() - 1; i >= 0; i--) { + if (!c.contains(tableData.get(i))) { + tableData.remove(i); + super.fireTableRowsDeleted(i, i); + modified = true; + } + } + return modified; + } + + @Override + public void clear() { + int oldSize = tableData.size(); + if (oldSize != 0) { + tableData.clear(); + super.fireTableRowsDeleted(0, oldSize - 1); + } + } + + @Override + public int size() { + return tableData.size(); + } + + @Override + public int getRowCount() { + return tableData.size(); + } + + @Override + public int getColumnCount() { + return tableColumnNames.length; + } + + @Override + public int indexOf(Object o) { + return tableData.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return tableData.lastIndexOf(o); + } + + @Override + public boolean contains(Object o) { + return tableData.contains(o); + } + + @Override + public boolean containsAll(Collection c) { + return tableData.containsAll(c); + } + + @Override + public boolean isEmpty() { + return tableData.isEmpty(); + } + + @Override + public Object[] get(int index) { + return tableData.get(index); + } + + @Override + public Object getValueAt(int row, int col) { + if (row >= size()) { + throw new IndexOutOfBoundsException("row too high: " + row + " (size=" + size() + ")"); + } + return tableData.get(row)[col]; + } + + @Override + public List subList(int fromIndex, int toIndex) { + return tableData.subList(fromIndex, toIndex); + } + + @Override + public Object[] toArray() { + return tableData.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return tableData.toArray(a); + } + + @Override + public Iterator iterator() { + return tableData.iterator(); + } + + @Override + public ListIterator listIterator() { + return tableData.listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + return tableData.listIterator(index); + } + + @Override + public String getColumnName(int col) { + return tableColumnNames[col]; + } + + @Override + public Class getColumnClass(int col) { + if (size() == 0) + return Object.class; + return getValueAt(0, col).getClass(); + } + + @Override + public boolean isCellEditable(int row, int col) { + return editable[col]; + } + + @Override + public void setValueAt(Object value, int row, int col) { + tableData.get(row)[col] = value; + fireTableCellUpdated(row, col); + } +} diff --git a/src/util/CameraFormatSelectionDialog.java b/src/util/CameraFormatSelectionDialog.java new file mode 100644 index 0000000..8cdf270 --- /dev/null +++ b/src/util/CameraFormatSelectionDialog.java @@ -0,0 +1,109 @@ +package util; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.WindowConstants; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import com.digitalmodular.utilities.swing.table.MutableTableModel; + +import de.humatic.dsj.DSMediaType; + +/** + * @author Mark Jeronimus + */ +// date 2014/08/05 +public class CameraFormatSelectionDialog extends JDialog implements ListSelectionListener, ActionListener { + private final DSMediaType[] formats; + private DSMediaType selectedFormat = null; + + private JTable table; + private JButton cancelButton = new JButton("Cancel"); + private JButton okButton = new JButton("Ok"); + + public CameraFormatSelectionDialog(String title, DSMediaType[] formats) { + super((JFrame)null, title, true); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + this.formats = formats; + + initComponents(); + + setSize(700, 300); + setLocationRelativeTo(null); + setVisible(true); + } + + private void initComponents() { + MutableTableModel tm = new MutableTableModel(new String[]{"Format"}); + + for (DSMediaType format : formats) { + tm.add(new Object[]{format.toString()}); + } + + table = new JTable(tm); + table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table.getSelectionModel().addListSelectionListener(this); + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + table.getColumnModel().getColumn(0).setPreferredWidth(650); + + setLayout(new BorderLayout()); + add(new JScrollPane(table), BorderLayout.CENTER); + { + JPanel p = new JPanel(new BorderLayout()); + p.setBorder(new EmptyBorder(6, 6, 6, 6)); + + { + JPanel p2 = new JPanel(new GridLayout(1, 2, 4, 4)); + + cancelButton.addActionListener(this); + p2.add(cancelButton); + okButton.addActionListener(this); + p2.add(okButton); + + p.add(p2, BorderLayout.EAST); + } + + add(p, BorderLayout.SOUTH); + } + + if (formats.length > 0) + table.getSelectionModel().setSelectionInterval(0, 0); + else + okButton.setEnabled(false); + + getRootPane().setDefaultButton(okButton); + } + + public DSMediaType getSelectedFormat() { + return selectedFormat; + } + + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting()) + return; + + okButton.setEnabled(!table.getSelectionModel().isSelectionEmpty()); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == okButton) + selectedFormat = formats[table.getSelectedRow()]; + + dispose(); + } +} diff --git a/src/util/CameraSelectionDialog.java b/src/util/CameraSelectionDialog.java new file mode 100644 index 0000000..c120ffc --- /dev/null +++ b/src/util/CameraSelectionDialog.java @@ -0,0 +1,120 @@ +package util; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.WindowConstants; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import com.digitalmodular.utilities.swing.table.MutableTableModel; + +import de.humatic.dsj.DSFilterInfo; + +/** + * @author Mark Jeronimus + */ +// date 2014/08/05 +public class CameraSelectionDialog extends JDialog implements ListSelectionListener, ActionListener { + private final DSFilterInfo[] cameras; + private ArrayList camerasToExclude; + private DSFilterInfo selectedCamera = null; + + private JTable table; + private JButton cancelButton = new JButton("Cancel"); + private JButton okButton = new JButton("Ok"); + + public CameraSelectionDialog(String title, DSFilterInfo[] cameras, ArrayList camerasToExclude) { + super((JFrame)null, title, true); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + this.cameras = cameras; + this.camerasToExclude = camerasToExclude; + + initComponents(); + + setSize(700, 300); + setLocationRelativeTo(null); + setVisible(true); + } + + private void initComponents() { + MutableTableModel tm = new MutableTableModel(new String[]{"Name", "Path"}); + + for (DSFilterInfo camera : cameras) { + tm.add(new Object[]{camera.getName(), camera.getPath()}); + } + + table = new JTable(tm); + table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table.getSelectionModel().addListSelectionListener(this); + table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); + table.getColumnModel().getColumn(0).setPreferredWidth(250); + table.getColumnModel().getColumn(1).setPreferredWidth(450); + + setLayout(new BorderLayout()); + add(new JScrollPane(table), BorderLayout.CENTER); + { + JPanel p = new JPanel(new BorderLayout()); + p.setBorder(new EmptyBorder(6, 6, 6, 6)); + + { + JPanel p2 = new JPanel(new GridLayout(1, 2, 4, 4)); + + cancelButton.addActionListener(this); + p2.add(cancelButton); + okButton.addActionListener(this); + p2.add(okButton); + + p.add(p2, BorderLayout.EAST); + } + + add(p, BorderLayout.SOUTH); + } + + if (cameras.length > 0) { + for (int i = 0; i < cameras.length; i++) { + DSFilterInfo camera = cameras[i]; + if (!camerasToExclude.contains(camera)) { + table.getSelectionModel().setSelectionInterval(i, i); + break; + } + } + } else { + okButton.setEnabled(false); + } + + getRootPane().setDefaultButton(okButton); + } + + public DSFilterInfo getSelectedCamera() { + return selectedCamera; + } + + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting()) + return; + + okButton.setEnabled(!table.getSelectionModel().isSelectionEmpty()); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == okButton) + selectedCamera = cameras[table.getSelectedRow()]; + + dispose(); + } +} diff --git a/src/util/CameraSettingsPanel.java b/src/util/CameraSettingsPanel.java new file mode 100644 index 0000000..1260377 --- /dev/null +++ b/src/util/CameraSettingsPanel.java @@ -0,0 +1,377 @@ +package util; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Arrays; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import com.digitalmodular.utilities.ConfigManager; +import com.digitalmodular.utilities.swing.TableLayout; + +import de.humatic.dsj.CaptureDeviceControls; + +/** + * @author Mark Jeronimus + */ +// date 2014/08/05 +public class CameraSettingsPanel extends JTabbedPane implements ChangeListener, ActionListener, Runnable { + private String[] tabs = {"Color", "Image", "Mechanical", "Sound", "CAMCONTROL_", "VC_", "LT_"}; + private String[][] propertyStrings = { + {"EXPOSURE", "GAIN", "BRIGHTNESS", "CONTRAST", "SATURATION", "HUE", "COLORENABLE", "WHITEBALANCE", "GAMMA"}, + {"BACKLIGHTCOMPENSATION", "SHARPNESS", "INPUT_LEVEL", "INPUT_SELECT"}, + {"FOCUS", "IRIS", "ZOOM", "PAN", "TILT", "ROLL"}, + {"MASTER_VOL", "MASTER_PAN", "TREBLE", "BASS", "BALANCE"}, + {"CAMCONTROL_ABSOLUTE", "CAMCONTROL_AUTO", "CAMCONTROL_MANUAL", "CAMCONTROL_RELATIVE"}, + {"VC_FLIP_HOR", "VC_FLIP_VER", "VC_TRIGGER", "VC_TRIGGER_ENABLE"}, + {"LT_DIGITAL_PAN", "LT_DIGITAL_PANTILTZOOM", "LT_DIGITAL_TILT", "LT_DIGITAL_ZOOM", "LT_EXPOSURE_TIME", + "LT_FACE_TRACKING", "LT_FINDFACE", "LT_LED"}}; + private int[][] propertyKeys = { + {CaptureDeviceControls.EXPOSURE, CaptureDeviceControls.GAIN, CaptureDeviceControls.BRIGHTNESS, + CaptureDeviceControls.CONTRAST, CaptureDeviceControls.SATURATION, CaptureDeviceControls.HUE, + CaptureDeviceControls.COLORENABLE, CaptureDeviceControls.WHITEBALANCE, CaptureDeviceControls.GAMMA}, + {CaptureDeviceControls.BACKLIGHTCOMPENSATION, CaptureDeviceControls.SHARPNESS, CaptureDeviceControls.INPUT_LEVEL, + CaptureDeviceControls.INPUT_SELECT}, + {CaptureDeviceControls.FOCUS, CaptureDeviceControls.IRIS, CaptureDeviceControls.ZOOM, CaptureDeviceControls.PAN, + CaptureDeviceControls.TILT, CaptureDeviceControls.ROLL}, + {CaptureDeviceControls.MASTER_VOL, CaptureDeviceControls.MASTER_PAN, CaptureDeviceControls.TREBLE, + CaptureDeviceControls.BASS, CaptureDeviceControls.BALANCE}, + {CaptureDeviceControls.CAMCONTROL_ABSOLUTE, CaptureDeviceControls.CAMCONTROL_AUTO, + CaptureDeviceControls.CAMCONTROL_MANUAL, CaptureDeviceControls.CAMCONTROL_RELATIVE}, + {CaptureDeviceControls.VC_FLIP_HOR, CaptureDeviceControls.VC_FLIP_VER, CaptureDeviceControls.VC_TRIGGER, + CaptureDeviceControls.VC_TRIGGER_ENABLE}, + {CaptureDeviceControls.LT_DIGITAL_PAN, CaptureDeviceControls.LT_DIGITAL_PANTILTZOOM, + CaptureDeviceControls.LT_DIGITAL_TILT, CaptureDeviceControls.LT_DIGITAL_ZOOM, + CaptureDeviceControls.LT_EXPOSURE_TIME, CaptureDeviceControls.LT_FACE_TRACKING, CaptureDeviceControls.LT_FINDFACE, + CaptureDeviceControls.LT_LED} }; + + private final DirectShowCamera camera; + + private final int[][][] ranges = new int[propertyStrings.length][][]; + private final int[][] originalValues = new int[propertyStrings.length][]; + private final boolean[][] originalAutos = new boolean[propertyStrings.length][]; + private final JSlider[][] valueSlider = new JSlider[propertyStrings.length][]; + private final JTextField[][] valueField = new JTextField[propertyStrings.length][]; + private final JCheckBox[][] autoCheckbox = new JCheckBox[propertyStrings.length][]; + + private final JButton defaultButton = new JButton("Reset defaults"); + private final JButton disableAutoButton = new JButton("Disable all auto"); + private final JButton loadButton = new JButton("Load settings"); + private final JButton saveButton = new JButton("Save settings"); + private final JButton revertButton = new JButton("Revert settings"); + + private int machineEvent = 0; + + public CameraSettingsPanel(DirectShowCamera camera) { + this.camera = camera; + + initComponents(); + camera.setPreferredFrameRate(10); + + new Thread(this, CameraSettingsPanel.class.getSimpleName()).start(); + } + + private void initComponents() { + for (int j = 0; j < propertyStrings.length; j++) { + JPanel p2 = new JPanel(new BorderLayout()); + + { + JPanel p = new JPanel(new TableLayout(4, 4, 6, 10, 2, 1)); + ranges[j] = new int[propertyStrings[j].length][]; + originalValues[j] = new int[propertyStrings[j].length]; + originalAutos[j] = new boolean[propertyStrings[j].length]; + valueSlider[j] = new JSlider[propertyStrings[j].length]; + valueField[j] = new JTextField[propertyStrings[j].length]; + autoCheckbox[j] = new JCheckBox[propertyStrings[j].length]; + for (int i = 0; i < propertyStrings[j].length; i++) { + p.add(new JLabel(propertyStrings[j][i])); + + ranges[j][i] = camera.getParameterRange(propertyKeys[j][i]); + int value = camera.getCurrentValue(propertyKeys[j][i]); + if (ranges[j][i][0] > ranges[j][i][1]) { + int temp = ranges[j][i][0]; + ranges[j][i][0] = ranges[j][i][1]; + ranges[j][i][1] = temp; + } + boolean invalid = ranges[j][i][0] == ranges[j][i][1] || ranges[j][i][2] == 0; + + System.err.println(Arrays.toString(new int[]{invalid? 0 : ranges[j][i][0], invalid? 1 : ranges[j][i][1], + invalid? 0 : value})); + valueSlider[j][i] = new JSlider(invalid? 0 : ranges[j][i][0], invalid? 1 : ranges[j][i][1], invalid ? 0 + : value); + valueSlider[j][i].addChangeListener(this); + p.add(valueSlider[j][i]); + + valueField[j][i] = new JTextField(Integer.toString(value)); + valueField[j][i].addActionListener(this); + p.add(valueField[j][i]); + + boolean auto = camera.getAuto(propertyKeys[j][i]); + + autoCheckbox[j][i] = new JCheckBox("", auto); + autoCheckbox[j][i].addActionListener(this); + p.add(autoCheckbox[j][i]); + + if (!invalid) { + originalValues[j][i] = value; + originalAutos[j][i] = auto; + } else { + ranges[j][i] = null; + valueSlider[j][i].setEnabled(false); + valueField[j][i].setEnabled(false); + autoCheckbox[j][i].setEnabled(false); + } + } + p2.add(p, BorderLayout.NORTH); + } + + add(p2, tabs[j]); + } + + JPanel p2 = new JPanel(new BorderLayout()); + + { + JPanel p = new JPanel(new FlowLayout(FlowLayout.CENTER)); + + defaultButton.addActionListener(this); + p.add(defaultButton); + disableAutoButton.addActionListener(this); + p.add(disableAutoButton); + loadButton.addActionListener(this); + p.add(loadButton); + saveButton.addActionListener(this); + p.add(saveButton); + revertButton.addActionListener(this); + p.add(revertButton); + + p2.add(p, BorderLayout.CENTER); + } + + add(p2, ""); + } + + @Override + public synchronized void stateChanged(ChangeEvent e) { + if (machineEvent > 0) + return; + try { + machineEvent++; + + Object o = e.getSource(); + for (int j = 0; j < propertyStrings.length; j++) { + for (int i = 0; i < propertyStrings[j].length; i++) { + if (o == valueSlider[j][i]) { + int value = valueSlider[j][i].getValue(); + if (ranges[j][i][2] > 2) + value = value / ranges[j][i][2] * ranges[j][i][2]; + + camera.setParameterValue(propertyKeys[j][i], value); + valueSlider[j][i].setValue(value); + valueField[j][i].setText(Integer.toString(value)); + return; + } + } + } + } + finally { + machineEvent--; + } + } + + @Override + public synchronized void actionPerformed(ActionEvent e) { + if (machineEvent > 0) + return; + try { + machineEvent++; + + Object o = e.getSource(); + for (int j = 0; j < propertyStrings.length; j++) { + for (int i = 0; i < propertyStrings[j].length; i++) { + if (o == valueField[j][i]) { + int value = Integer.parseInt(valueField[j][i].getText()); + if (ranges[j][i][2] > 2) + value = value / ranges[j][i][2] * ranges[j][i][2]; + + camera.setParameterValue(propertyKeys[j][i], value); + valueSlider[j][i].setValue(value); + valueField[j][i].setText(Integer.toString(value)); + return; + } else if (o == autoCheckbox[j][i]) { + boolean auto = autoCheckbox[j][i].isSelected(); + + camera.setAuto(propertyKeys[j][i], auto); + return; + } + } + } + if (o == defaultButton) { + resetDefaults(); + } else if (o == disableAutoButton) { + disableAuto(); + } else if (o == loadButton) { + load(); + } else if (o == saveButton) { + save(); + } else if (o == revertButton) { + revert(); + } + } + finally { + machineEvent--; + } + } + + @Override + public void run() { + try { + while (isVisible()) { + Thread.sleep(200); + getCameraValues(); + } + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private synchronized void getCameraValues() { + try { + machineEvent++; + + for (int j = 0; j < propertyStrings.length; j++) { + for (int i = 0; i < propertyStrings[j].length; i++) { + if (ranges[j][i] == null) + continue; + + int value = camera.getCurrentValue(propertyKeys[j][i]); + + valueSlider[j][i].setValue(value); + if (((JFrame)getTopLevelAncestor()).getFocusOwner() != valueField[j][i]) + valueField[j][i].setText(Integer.toString(value)); + } + } + } + finally { + machineEvent--; + } + } + + private synchronized void resetDefaults() { + try { + machineEvent++; + + for (int j = 0; j < propertyStrings.length; j++) { + for (int i = 0; i < propertyStrings[j].length; i++) { + if (ranges[j][i] == null) + continue; + + int value = ranges[j][i][3]; + + camera.setParameterValue(propertyKeys[j][i], value); + valueSlider[j][i].setValue(value); + valueField[j][i].setText(Integer.toString(value)); + } + } + } + finally { + machineEvent--; + } + } + + private synchronized void disableAuto() { + try { + machineEvent++; + + for (int j = 0; j < propertyStrings.length; j++) { + for (int i = 0; i < propertyStrings[j].length; i++) { + if (ranges[j][i] == null) + continue; + + camera.setAuto(propertyKeys[j][i], false); + autoCheckbox[j][i].setSelected(false); + } + } + } + finally { + machineEvent--; + } + } + + private synchronized void load() { + ConfigManager.revert(); + + try { + machineEvent++; + + for (int j = 0; j < propertyStrings.length; j++) { + for (int i = 0; i < propertyStrings[j].length; i++) { + if (ranges[j][i] == null) + continue; + + int value = ConfigManager.getIntValue(camera.getPath() + "?" + propertyKeys[j][i], originalValues[j][i]); + boolean auto = ConfigManager.getBoolValue(camera.getPath() + "&" + propertyKeys[j][i], originalAutos[j][i]); + + camera.setParameterValue(propertyKeys[j][i], value); + camera.setAuto(propertyKeys[j][i], auto); + valueSlider[j][i].setValue(value); + valueField[j][i].setText(Integer.toString(value)); + autoCheckbox[j][i].setSelected(auto); + } + } + } + finally { + machineEvent--; + } + } + + private synchronized void save() { + for (int j = 0; j < propertyStrings.length; j++) { + for (int i = 0; i < propertyStrings[j].length; i++) { + if (ranges[j][i] == null) + continue; + + ConfigManager.setIntValue(camera.getPath() + "?" + propertyKeys[j][i], valueSlider[j][i].getValue()); + ConfigManager.setBoolValue(camera.getPath() + "&" + propertyKeys[j][i], autoCheckbox[j][i].isSelected()); + } + } + + ConfigManager.save(); + } + + private synchronized void revert() { + try { + machineEvent++; + + for (int j = 0; j < propertyStrings.length; j++) { + for (int i = 0; i < propertyStrings[j].length; i++) { + if (ranges[j][i] == null) + continue; + + int value = originalValues[j][i]; + boolean auto = originalAutos[j][i]; + + valueSlider[j][i].setValue(value); + valueField[j][i].setText(Integer.toString(value)); + camera.setParameterValue(propertyKeys[j][i], value); + camera.setAuto(propertyKeys[j][i], false); + autoCheckbox[j][i].setSelected(auto); + } + } + } + finally { + machineEvent--; + } + } +} diff --git a/src/util/DirectShowCamera.java b/src/util/DirectShowCamera.java new file mode 100644 index 0000000..6cf778c --- /dev/null +++ b/src/util/DirectShowCamera.java @@ -0,0 +1,360 @@ +package util; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.Arrays; + +import de.humatic.dsj.CaptureDeviceControls; +import de.humatic.dsj.DSCapture; +import de.humatic.dsj.DSFilterInfo; +import de.humatic.dsj.DSFilterInfo.DSPinInfo; +import de.humatic.dsj.DSFiltergraph; +import de.humatic.dsj.DSJException; +import de.humatic.dsj.DSJUtils; +import de.humatic.dsj.DSMediaType; + +/** + * @author Mark Jeronimus + * @version 1.0 + * @since 1.0 + * @date 2012/04/02 + */ +public class DirectShowCamera implements PropertyChangeListener { + private final DSFilterInfo camera; + + private ArrayList listeners = new ArrayList<>(); + + private DSPinInfo pin; + private DSMediaType format; + private DSCapture capturer = null; + + public static DSFilterInfo[] getCameras() { + DSFilterInfo[] cameras = DSCapture.queryDevices(DSCapture.RESOLVE_OUTPUTS | DSCapture.SKIP_AUDIO | DSCapture.SKIP_XBARS + | DSCapture.SKIP_BDA)[0]; + return Arrays.copyOf(cameras, cameras.length - 1); + } + + public static void dumpCameras() { + DSFilterInfo[] cameras = getCameras(); + + System.out.println("Found " + (cameras.length - 1) + " cameras:"); + for (int i = 0; i < cameras.length - 1; i++) { + DSFilterInfo device = cameras[i]; + System.out.println(i + "\t" + device.getName()); + } + } + + public DirectShowCamera(DSFilterInfo camera) { + this.camera = camera; + } + + public String getName() { + return camera.getName(); + } + + public String getPath() { + return camera.getPath(); + } + + public static DSMediaType[] getFormats(DSFilterInfo camera) { + ArrayList allFormats = new ArrayList(); + + DSPinInfo[] pins = camera.getDownstreamPins(); + for (DSPinInfo pin : pins) { + // Only output pins + if (pin.getDirection() != DSPinInfo.PINDIR_OUTPUT) + continue; + + // Only capture or unnamed pins + if (!("Capture".equals(pin.getName()) || "".equals(pin.getName()))) + continue; + + DSMediaType[] formats = pin.getFormats(); + for (DSMediaType format : formats) { + // Only ??? formats + if (format.getFormatType() != 0) + continue; + + allFormats.add(format); + } + } + + return allFormats.toArray(new DSMediaType[allFormats.size()]); + } + + public void dumpFormats() { + int count = 0; + + DSPinInfo[] pins = camera.getDownstreamPins(); + for (DSPinInfo pin : pins) { + // Only output pins + if (pin.getDirection() != DSPinInfo.PINDIR_OUTPUT) + continue; + + // Only capture or unnamed pins + if (!("Capture".equals(pin.getName()) || "".equals(pin.getName()))) + continue; + + System.out.println("Pin \"" + pin.getName() + "\""); + + DSMediaType[] formats = pin.getFormats(); + for (DSMediaType format : formats) { + // Only ??? formats + if (format.getFormatType() != 0) + continue; + + System.out.println(count + ": " + format.toString()); + count++; + } + } + + if (count == 0) + throw new IllegalArgumentException("Camera has no recognized formats."); + } + + public void selectFormat(DSMediaType formatToSelect) { + DSPinInfo[] pins = camera.getDownstreamPins(); + for (DSPinInfo pin : pins) { + // Only output pins + if (pin.getDirection() != DSPinInfo.PINDIR_OUTPUT) + continue; + + // Only capture or unnamed pins + if (!("Capture".equals(pin.getName()) || "".equals(pin.getName()))) + continue; + + DSMediaType[] formats = pin.getFormats(); + for (int i = 0; i < formats.length; i++) { + DSMediaType format = formats[i]; + // Only ??? formats + if (format.getFormatType() != 0) + continue; + + if (format.equals(formatToSelect)) { + pin.setPreferredFormat(i); + this.pin = pin; + this.format = formats[i]; + return; + } + } + } + } + + public void selectFormat(int formatID) { + if (isInitialized()) + throw new IllegalArgumentException("Cannot change format while recording"); + + int count = 0; + + DSPinInfo[] pins = camera.getDownstreamPins(); + for (DSPinInfo pin : pins) { + // Only output pins + if (pin.getDirection() != DSPinInfo.PINDIR_OUTPUT) + continue; + + // Only capture or unnamed pins + if (!("Capture".equals(pin.getName()) || "".equals(pin.getName()))) + continue; + + DSMediaType[] formats = pin.getFormats(); + for (int i = 0; i < formats.length; i++) { + DSMediaType format = formats[i]; + + // Only ??? formats + if (format.getFormatType() != 0) + continue; + + if (formatID == count) { + pin.setPreferredFormat(i); + this.pin = pin; + this.format = formats[i]; + return; + } + count++; + } + } + + throw new IllegalArgumentException("Invalid format specified."); + } + + public DSMediaType getFormat() { + return format; + } + + public boolean isInitialized() { + return capturer != null; + } + + public void start() { + capturer = new DSCapture(DSFiltergraph.FRAME_CALLBACK, camera, false, null, this); + } + + public void dumpParameters() { + System.out.println("EXPOSURE = " + + capturer.getActiveVideoDevice().getControls().getCurrentValue(CaptureDeviceControls.EXPOSURE)); + System.out.println("GAIN = " + + capturer.getActiveVideoDevice().getControls().getCurrentValue(CaptureDeviceControls.GAIN)); + System.out.println("BRIGHTNESS = " + + capturer.getActiveVideoDevice().getControls().getCurrentValue(CaptureDeviceControls.BRIGHTNESS)); + System.out.println("CONTRAST = " + + capturer.getActiveVideoDevice().getControls().getCurrentValue(CaptureDeviceControls.CONTRAST)); + System.out.println("SATURATION = " + + capturer.getActiveVideoDevice().getControls().getCurrentValue(CaptureDeviceControls.SATURATION)); + System.out.println("WB = " + + capturer.getActiveVideoDevice().getControls().getCurrentValue(CaptureDeviceControls.WHITEBALANCE)); + System.out.println("SHARPNESS = " + + capturer.getActiveVideoDevice().getControls().getCurrentValue(CaptureDeviceControls.SHARPNESS)); + System.out.println("ZOOM = " + + capturer.getActiveVideoDevice().getControls().getCurrentValue(CaptureDeviceControls.ZOOM)); + System.out.println("---"); + } + + public void stop() { + capturer.stop(); + capturer.dispose(); + capturer = null; + } + + public void addFrameListener(FrameListener listener) { + listeners.add(listener); + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + switch (DSJUtils.getEventType(evt)) { + case DSFiltergraph.FRAME_NOTIFY: + byte[] image = capturer.getData(); + + for (FrameListener listener : listeners) + listener.newFrame(this, image); + + break; + case DSFiltergraph.ACTIVATING: + case DSFiltergraph.INITIALIZED: + case DSFiltergraph.TRANSPORT: + case DSFiltergraph.GRAPH_EVENT: + case DSFiltergraph.CLOSING: + case DSFiltergraph.GRAPH_ERROR: + case DSFiltergraph.CLOSED: + break; + default: + System.out.println("Unknown DSJ event type: " + DSJUtils.getEventType(evt)); + } + } + + public void showParameterDialog() { + capturer.getActiveVideoDevice().showDialog(DSCapture.CaptureDevice.WDM_DEVICE); + } + + /** 100..255 (default=100) */ + public void setZoomValue(int zoom) { + try { + capturer.getActiveVideoDevice().getControls().setParameterValue(CaptureDeviceControls.ZOOM, zoom, 0); + } + catch (DSJException e2) {} + } + + /** 0..-7 */ + public void setExposureValue(int exposure) { + try { + capturer.getActiveVideoDevice().getControls().setParameterValue(CaptureDeviceControls.EXPOSURE, exposure, 0); + } + catch (DSJException e2) {} + } + + /** 0..255 (lower is less noise) */ + public void setGainValue(int gain) { + try { + capturer.getActiveVideoDevice().getControls().setParameterValue(CaptureDeviceControls.GAIN, gain, 0); + } + catch (DSJException e2) {} + } + + /** 0..255 (default=128) */ + public void setBrightnessValue(int brightness) { + try { + capturer.getActiveVideoDevice().getControls().setParameterValue(CaptureDeviceControls.BRIGHTNESS, brightness, 0); + } + catch (DSJException e2) {} + } + + /** 0..255 (default=128) */ + public void setContrastValue(int contrast) { + try { + capturer.getActiveVideoDevice().getControls().setParameterValue(CaptureDeviceControls.CONTRAST, contrast, 0); + } + catch (DSJException e2) {} + } + + /** 0..255 (default=128) */ + public void setSaturationValue(int saturation) { + try { + capturer.getActiveVideoDevice().getControls().setParameterValue(CaptureDeviceControls.SATURATION, saturation, 0); + } + catch (DSJException e2) {} + } + + /** 2000-6500 (default=4000..5000) */ + public void setWhitebalanceValue(int whitebalance) { + try { + capturer.getActiveVideoDevice().getControls() + .setParameterValue(CaptureDeviceControls.WHITEBALANCE, whitebalance, 0); + } + catch (DSJException e2) {} + } + + /** 0.. */ + public void setSharpnessValue(int sharpness) { + try { + capturer.getActiveVideoDevice().getControls().setParameterValue(CaptureDeviceControls.SHARPNESS, sharpness, 0); + } + catch (DSJException e2) {} + } + + public int[] getParameterRange(int key) { + + try { + return capturer.getActiveVideoDevice().getControls().getParameterRange(key); + } + catch (DSJException e2) { + return null; + } + } + + public int getCurrentValue(int key) { + try { + return capturer.getActiveVideoDevice().getControls().getCurrentValue(key); + } + catch (DSJException e2) { + return 0; + } + } + + public void setParameterValue(int key, int value) { + try { + capturer.getActiveVideoDevice().getControls().setParameterValue(key, value, 0); + } + catch (DSJException e2) {} + } + + public boolean getAuto(int key) { + try { + return capturer.getActiveVideoDevice().getControls().getAuto(key); + } + catch (DSJException e2) { + return false; + } + } + + public void setAuto(int key, boolean auto) { + try { + capturer.getActiveVideoDevice().getControls().setAuto(key, auto); + } + catch (DSJException e2) {} + } + + public void setPreferredFrameRate(int framerate) { + pin.setPreferredFrameRate(framerate); + } +} diff --git a/src/util/FrameListener.java b/src/util/FrameListener.java new file mode 100644 index 0000000..8cf30e5 --- /dev/null +++ b/src/util/FrameListener.java @@ -0,0 +1,11 @@ +package util; + +/** + * @author Mark Jeronimus + * @version 1.0 + * @since 1.0 + * @date 2012/04/02 + */ +public interface FrameListener { + public void newFrame(Object source, byte[] data); +}