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}