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}