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 sink for video that accepts 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 VideoSink implements AutoCloseable {
012  /** Video sink types. */
013  public enum Kind {
014    /** Unknown video sink type. */
015    kUnknown(0),
016    /** MJPEG video sink. */
017    kMjpeg(2),
018    /** CV video sink. */
019    kCv(4),
020    /** Raw video sink. */
021    kRaw(8);
022
023    private final int value;
024
025    Kind(int value) {
026      this.value = value;
027    }
028
029    /**
030     * Returns the Kind value.
031     *
032     * @return The Kind value.
033     */
034    public int getValue() {
035      return value;
036    }
037  }
038
039  /**
040   * Convert from the numerical representation of kind to an enum type.
041   *
042   * @param kind The numerical representation of kind
043   * @return The kind
044   */
045  public static Kind getKindFromInt(int kind) {
046    return switch (kind) {
047      case 2 -> Kind.kMjpeg;
048      case 4 -> Kind.kCv;
049      case 8 -> Kind.kRaw;
050      default -> Kind.kUnknown;
051    };
052  }
053
054  /**
055   * Constructs a VideoSink.
056   *
057   * @param handle The video sink handle.
058   */
059  protected VideoSink(int handle) {
060    m_handle = handle;
061  }
062
063  @Override
064  public synchronized void close() {
065    if (m_handle != 0) {
066      CameraServerJNI.releaseSink(m_handle);
067    }
068    m_handle = 0;
069  }
070
071  /**
072   * Returns true if the VideoSink is valid.
073   *
074   * @return True if the VideoSink is valid.
075   */
076  public boolean isValid() {
077    return m_handle != 0;
078  }
079
080  /**
081   * Returns the video sink handle.
082   *
083   * @return The video sink handle.
084   */
085  public int getHandle() {
086    return m_handle;
087  }
088
089  @Override
090  public boolean equals(Object other) {
091    if (this == other) {
092      return true;
093    }
094    if (other == null) {
095      return false;
096    }
097    return other instanceof VideoSink sink && m_handle == sink.m_handle;
098  }
099
100  @Override
101  public int hashCode() {
102    return m_handle;
103  }
104
105  /**
106   * Get the kind of the sink.
107   *
108   * @return The kind of the sink.
109   */
110  public Kind getKind() {
111    return getKindFromInt(CameraServerJNI.getSinkKind(m_handle));
112  }
113
114  /**
115   * Get the name of the sink. The name is an arbitrary identifier provided when the sink is
116   * created, and should be unique.
117   *
118   * @return The name of the sink.
119   */
120  public String getName() {
121    return CameraServerJNI.getSinkName(m_handle);
122  }
123
124  /**
125   * Get the sink description. This is sink-kind specific.
126   *
127   * @return The sink description.
128   */
129  public String getDescription() {
130    return CameraServerJNI.getSinkDescription(m_handle);
131  }
132
133  /**
134   * Get a property of the sink.
135   *
136   * @param name Property name
137   * @return Property (kind Property::kNone if no property with the given name exists)
138   */
139  public VideoProperty getProperty(String name) {
140    return new VideoProperty(CameraServerJNI.getSinkProperty(m_handle, name));
141  }
142
143  /**
144   * Enumerate all properties of this sink.
145   *
146   * @return List of properties.
147   */
148  public VideoProperty[] enumerateProperties() {
149    int[] handles = CameraServerJNI.enumerateSinkProperties(m_handle);
150    VideoProperty[] rv = new VideoProperty[handles.length];
151    for (int i = 0; i < handles.length; i++) {
152      rv[i] = new VideoProperty(handles[i]);
153    }
154    return rv;
155  }
156
157  /**
158   * Set properties from a JSON configuration string.
159   *
160   * <p>The format of the JSON input is:
161   *
162   * <pre>
163   * {
164   *     "properties": [
165   *         {
166   *             "name": property name
167   *             "value": property value
168   *         }
169   *     ]
170   * }
171   * </pre>
172   *
173   * @param config configuration
174   * @return True if set successfully
175   */
176  public boolean setConfigJson(String config) {
177    return CameraServerJNI.setSinkConfigJson(m_handle, config);
178  }
179
180  /**
181   * Get a JSON configuration string.
182   *
183   * @return JSON configuration string
184   */
185  public String getConfigJson() {
186    return CameraServerJNI.getSinkConfigJson(m_handle);
187  }
188
189  /**
190   * Configure which source should provide frames to this sink. Each sink can accept frames from
191   * only a single source, but a single source can provide frames to multiple clients.
192   *
193   * @param source Source
194   */
195  public void setSource(VideoSource source) {
196    if (source == null) {
197      CameraServerJNI.setSinkSource(m_handle, 0);
198    } else {
199      CameraServerJNI.setSinkSource(m_handle, source.m_handle);
200    }
201  }
202
203  /**
204   * Get the connected source.
205   *
206   * @return Connected source; nullptr if no source connected.
207   */
208  public VideoSource getSource() {
209    // While VideoSource.close() will call releaseSource(), getSinkSource()
210    // increments the internal reference count so this is okay to do.
211    return new VideoSource(CameraServerJNI.getSinkSource(m_handle));
212  }
213
214  /**
215   * Get a property of the associated source.
216   *
217   * @param name Property name
218   * @return Property (kind Property::kNone if no property with the given name exists or no source
219   *     connected)
220   */
221  public VideoProperty getSourceProperty(String name) {
222    return new VideoProperty(CameraServerJNI.getSinkSourceProperty(m_handle, name));
223  }
224
225  /**
226   * Enumerate all existing sinks.
227   *
228   * @return Vector of sinks.
229   */
230  public static VideoSink[] enumerateSinks() {
231    int[] handles = CameraServerJNI.enumerateSinks();
232    VideoSink[] rv = new VideoSink[handles.length];
233    for (int i = 0; i < handles.length; i++) {
234      rv[i] = new VideoSink(handles[i]);
235    }
236    return rv;
237  }
238
239  /** The VideoSink handle. */
240  protected int m_handle;
241}