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