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