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}