001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.cscore;
006
007/**
008 * A source for video that provides a sequence of frames. Each frame may consist of multiple images
009 * (e.g. from a stereo or depth camera); these are called channels.
010 */
011public class VideoSource implements AutoCloseable {
012  public enum Kind {
013    kUnknown(0),
014    kUsb(1),
015    kHttp(2),
016    kCv(4),
017    kRaw(8);
018
019    private final int value;
020
021    Kind(int value) {
022      this.value = value;
023    }
024
025    public int getValue() {
026      return value;
027    }
028  }
029
030  /** Connection strategy. */
031  public enum ConnectionStrategy {
032    /**
033     * Automatically connect or disconnect based on whether any sinks are connected to this source.
034     * This is the default behavior.
035     */
036    kAutoManage(0),
037
038    /** Try to keep the connection open regardless of whether any sinks are connected. */
039    kKeepOpen(1),
040
041    /**
042     * Never open the connection. If this is set when the connection is open, close the connection.
043     */
044    kForceClose(2);
045
046    private final int value;
047
048    ConnectionStrategy(int value) {
049      this.value = value;
050    }
051
052    public int getValue() {
053      return value;
054    }
055  }
056
057  /**
058   * Convert from the numerical representation of kind to an enum type.
059   *
060   * @param kind The numerical representation of kind
061   * @return The kind
062   */
063  public static Kind getKindFromInt(int kind) {
064    switch (kind) {
065      case 1:
066        return Kind.kUsb;
067      case 2:
068        return Kind.kHttp;
069      case 4:
070        return Kind.kCv;
071      default:
072        return Kind.kUnknown;
073    }
074  }
075
076  protected VideoSource(int handle) {
077    m_handle = handle;
078  }
079
080  @Override
081  public synchronized void close() {
082    if (m_handle != 0) {
083      CameraServerJNI.releaseSource(m_handle);
084    }
085    m_handle = 0;
086  }
087
088  public boolean isValid() {
089    return m_handle != 0;
090  }
091
092  public int getHandle() {
093    return m_handle;
094  }
095
096  @Override
097  public boolean equals(Object other) {
098    if (this == other) {
099      return true;
100    }
101    if (other == null) {
102      return false;
103    }
104    if (getClass() != other.getClass()) {
105      return false;
106    }
107    VideoSource source = (VideoSource) other;
108    return m_handle == source.m_handle;
109  }
110
111  @Override
112  public int hashCode() {
113    return m_handle;
114  }
115
116  /**
117   * Get the kind of the source.
118   *
119   * @return The kind of the source.
120   */
121  public Kind getKind() {
122    return getKindFromInt(CameraServerJNI.getSourceKind(m_handle));
123  }
124
125  /**
126   * Get the name of the source. The name is an arbitrary identifier provided when the source is
127   * created, and should be unique.
128   *
129   * @return The name of the source.
130   */
131  public String getName() {
132    return CameraServerJNI.getSourceName(m_handle);
133  }
134
135  /**
136   * Get the source description. This is source-kind specific.
137   *
138   * @return The source description.
139   */
140  public String getDescription() {
141    return CameraServerJNI.getSourceDescription(m_handle);
142  }
143
144  /**
145   * Get the last time a frame was captured.
146   *
147   * @return Time in 1 us increments.
148   */
149  public long getLastFrameTime() {
150    return CameraServerJNI.getSourceLastFrameTime(m_handle);
151  }
152
153  /**
154   * Sets the connection strategy. By default, the source will automatically connect or disconnect
155   * based on whether any sinks are connected.
156   *
157   * <p>This function is non-blocking; look for either a connection open or close event or call
158   * {@link #isConnected()} to determine the connection state.
159   *
160   * @param strategy connection strategy (auto, keep open, or force close)
161   */
162  public void setConnectionStrategy(ConnectionStrategy strategy) {
163    CameraServerJNI.setSourceConnectionStrategy(m_handle, strategy.getValue());
164  }
165
166  /**
167   * Returns true if the source currently connected to whatever is providing the images.
168   *
169   * @return True if the source currently connected to whatever is providing the images.
170   */
171  public boolean isConnected() {
172    return CameraServerJNI.isSourceConnected(m_handle);
173  }
174
175  /**
176   * Gets source enable status. This is determined with a combination of connection strategy and the
177   * number of sinks connected.
178   *
179   * @return True if enabled, false otherwise.
180   */
181  public boolean isEnabled() {
182    return CameraServerJNI.isSourceEnabled(m_handle);
183  }
184
185  /**
186   * Get a property.
187   *
188   * @param name Property name
189   * @return Property contents (of kind Property::kNone if no property with the given name exists)
190   */
191  public VideoProperty getProperty(String name) {
192    return new VideoProperty(CameraServerJNI.getSourceProperty(m_handle, name));
193  }
194
195  /**
196   * Enumerate all properties of this source.
197   *
198   * @return Array of video properties.
199   */
200  public VideoProperty[] enumerateProperties() {
201    int[] handles = CameraServerJNI.enumerateSourceProperties(m_handle);
202    VideoProperty[] rv = new VideoProperty[handles.length];
203    for (int i = 0; i < handles.length; i++) {
204      rv[i] = new VideoProperty(handles[i]);
205    }
206    return rv;
207  }
208
209  /**
210   * Get the current video mode.
211   *
212   * @return The current video mode.
213   */
214  public VideoMode getVideoMode() {
215    return CameraServerJNI.getSourceVideoMode(m_handle);
216  }
217
218  /**
219   * Set the video mode.
220   *
221   * @param mode Video mode
222   * @return True if set successfully.
223   */
224  public boolean setVideoMode(VideoMode mode) {
225    return CameraServerJNI.setSourceVideoMode(
226        m_handle, mode.pixelFormat.getValue(), mode.width, mode.height, mode.fps);
227  }
228
229  /**
230   * Set the video mode.
231   *
232   * @param pixelFormat desired pixel format
233   * @param width desired width
234   * @param height desired height
235   * @param fps desired FPS
236   * @return True if set successfully
237   */
238  public boolean setVideoMode(VideoMode.PixelFormat pixelFormat, int width, int height, int fps) {
239    return CameraServerJNI.setSourceVideoMode(m_handle, pixelFormat.getValue(), width, height, fps);
240  }
241
242  /**
243   * Set the pixel format.
244   *
245   * @param pixelFormat desired pixel format
246   * @return True if set successfully
247   */
248  public boolean setPixelFormat(VideoMode.PixelFormat pixelFormat) {
249    return CameraServerJNI.setSourcePixelFormat(m_handle, pixelFormat.getValue());
250  }
251
252  /**
253   * Set the resolution.
254   *
255   * @param width desired width
256   * @param height desired height
257   * @return True if set successfully
258   */
259  public boolean setResolution(int width, int height) {
260    return CameraServerJNI.setSourceResolution(m_handle, width, height);
261  }
262
263  /**
264   * Set the frames per second (FPS).
265   *
266   * @param fps desired FPS
267   * @return True if set successfully
268   */
269  public boolean setFPS(int fps) {
270    return CameraServerJNI.setSourceFPS(m_handle, fps);
271  }
272
273  /**
274   * Set video mode and properties from a JSON configuration string.
275   *
276   * <p>The format of the JSON input is:
277   *
278   * <pre>
279   * {
280   *     "pixel format": "MJPEG", "YUYV", etc
281   *     "width": video mode width
282   *     "height": video mode height
283   *     "fps": video mode fps
284   *     "brightness": percentage brightness
285   *     "white balance": "auto", "hold", or value
286   *     "exposure": "auto", "hold", or value
287   *     "properties": [
288   *         {
289   *             "name": property name
290   *             "value": property value
291   *         }
292   *     ]
293   * }
294   * </pre>
295   *
296   * @param config configuration
297   * @return True if set successfully
298   */
299  public boolean setConfigJson(String config) {
300    return CameraServerJNI.setSourceConfigJson(m_handle, config);
301  }
302
303  /**
304   * Get a JSON configuration string.
305   *
306   * @return JSON configuration string
307   */
308  public String getConfigJson() {
309    return CameraServerJNI.getSourceConfigJson(m_handle);
310  }
311
312  /**
313   * Get the actual FPS.
314   *
315   * <p>CameraServerJNI#setTelemetryPeriod() must be called for this to be valid (throws
316   * VisionException if telemetry is not enabled).
317   *
318   * @return Actual FPS averaged over the telemetry period.
319   */
320  public double getActualFPS() {
321    return CameraServerJNI.getTelemetryAverageValue(
322        m_handle, CameraServerJNI.TelemetryKind.kSourceFramesReceived);
323  }
324
325  /**
326   * Get the data rate (in bytes per second).
327   *
328   * <p>CameraServerJNI#setTelemetryPeriod() must be called for this to be valid (throws
329   * VisionException if telemetry is not enabled).
330   *
331   * @return Data rate averaged over the telemetry period.
332   */
333  public double getActualDataRate() {
334    return CameraServerJNI.getTelemetryAverageValue(
335        m_handle, CameraServerJNI.TelemetryKind.kSourceBytesReceived);
336  }
337
338  /**
339   * Enumerate all known video modes for this source.
340   *
341   * @return Vector of video modes.
342   */
343  public VideoMode[] enumerateVideoModes() {
344    return CameraServerJNI.enumerateSourceVideoModes(m_handle);
345  }
346
347  /**
348   * Enumerate all sinks connected to this source.
349   *
350   * @return Vector of sinks.
351   */
352  public VideoSink[] enumerateSinks() {
353    int[] handles = CameraServerJNI.enumerateSourceSinks(m_handle);
354    VideoSink[] rv = new VideoSink[handles.length];
355    for (int i = 0; i < handles.length; i++) {
356      rv[i] = new VideoSink(handles[i]);
357    }
358    return rv;
359  }
360
361  /**
362   * Enumerate all existing sources.
363   *
364   * @return Vector of sources.
365   */
366  public static VideoSource[] enumerateSources() {
367    int[] handles = CameraServerJNI.enumerateSources();
368    VideoSource[] rv = new VideoSource[handles.length];
369    for (int i = 0; i < handles.length; i++) {
370      rv[i] = new VideoSource(handles[i]);
371    }
372    return rv;
373  }
374
375  protected int m_handle;
376}