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